{"id":372691,"date":"2026-09-24T16:10:18","date_gmt":"2026-09-24T16:10:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/dragon-checkout-guard\/"},"modified":"2026-09-25T10:55:16","modified_gmt":"2026-09-25T10:55:16","slug":"dragon-checkout-guard","status":"publish","type":"plugin","link":"https:\/\/kn.wordpress.org\/plugins\/dragon-checkout-guard\/","author":23543042,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.8","stable_tag":"1.0.8","tested":"7.1.2","requires":"6.5","requires_php":"8.0","requires_plugins":null,"header_name":"Dragon Checkout Guard","header_author":"Dragon Core","header_description":"Payment-page script inventory, authorisation record, weekly tamper check and header baseline: PCI DSS 6.4.3 \/ 11.6.1 and SAQ A evidence. No account.","assets_banners_color":"211a1c","last_updated":"2026-09-25 10:55:16","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/dragoncore.ltd\/plugins\/dragon-checkout-guard","header_author_uri":"https:\/\/dragoncore.ltd","rating":0,"author_block_rating":0,"active_installs":0,"downloads":97,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.4":{"tag":"1.0.4","author":"dragoncoreltd","date":"2026-09-24 16:09:48","revision":3711662},"1.0.5":{"tag":"1.0.5","author":"dragoncoreltd","date":"2026-09-24 17:26:29","revision":3711770},"1.0.6":{"tag":"1.0.6","author":"dragoncoreltd","date":"2026-09-24 17:50:15","revision":3711803},"1.0.7":{"tag":"1.0.7","author":"dragoncoreltd","date":"2026-09-25 05:07:59","revision":3712398},"1.0.8":{"tag":"1.0.8","author":"dragoncoreltd","date":"2026-09-25 10:55:16","revision":3712899}},"upgrade_notice":{"1.0.8":"<p>Scans now read Checkout on a stock WooCommerce store using a one-product cart held in memory only. The first such scan may flag server-added headers: review and accept them. Add Payment Method still needs a signed-in customer, which is not an error.<\/p>","1.0.7":"<p>Security hardening: the script inventory now matches what browsers actually load.<\/p>","1.0.6":"<p>Translation-ready throughout.<\/p>","1.0.5":"<p>Listing title and tags for WordPress.org, plus an optional review request on the plugin&#039;s own screen.<\/p>","1.0.4":"<p>WordPress core scripts are now recognised on installs with a non-default wp-includes or wp-admin location.<\/p>","1.0.3":"<p>Scripts whose type attribute carries a charset parameter are now inventoried: run a scan after updating. If you use a custom payment-page path with accented or percent-encoded characters, enter it again on the Settings tab.<\/p>","1.0.0":"<p>Initial release of Dragon Checkout Guard.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.gif":{"filename":"icon-128x128.gif","revision":3711770,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.gif":{"filename":"icon-256x256.gif","revision":3711770,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3711662,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3711662,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.4","1.0.5","1.0.6","1.0.7","1.0.8"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3711662,"resolution":"1","location":"assets","locale":"","width":1560,"height":1071},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3711662,"resolution":"2","location":"assets","locale":"","width":1560,"height":916},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3711662,"resolution":"3","location":"assets","locale":"","width":1560,"height":974},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3711662,"resolution":"4","location":"assets","locale":"","width":1560,"height":1168}},"screenshots":{"1":"The script inventory: every script seen on your payment pages, with the provider recognised, a warning pill on anything that can inject more scripts, and one-click authorisation.","2":"The printable payment page script integrity record: assessment context, the authorised scripts and the ones still awaiting review, and the 11.6.1 check record, ready to hand to an assessor.","3":"Response header baselines per payment page, so a change to Content-Security-Policy or framing controls is flagged instead of going unnoticed.","4":"The assessment guide: answer three plain questions and it shows which PCI SSC FAQ 1588 route your answers point to and the records that route asks for."}},"plugin_section":[],"plugin_tags":[275069,237432,282532,265161,286],"plugin_category":[45,54],"plugin_contributors":[276960],"plugin_business_model":[],"class_list":["post-372691","plugin","type-plugin","status-publish","hentry","plugin_tags-card-skimming","plugin_tags-checkout-security","plugin_tags-payment-page","plugin_tags-pci-dss","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_category-security-and-spam-protection","plugin_contributors-dragoncoreltd","plugin_committers-dragoncoreltd"],"banners":{"banner":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/banner-772x250.png?rev=3711662","banner_2x":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/banner-1544x500.png?rev=3711662","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/icon-128x128.gif?rev=3711770","icon_2x":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/icon-256x256.gif?rev=3711770","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/screenshot-1.png?rev=3711662","caption":"The script inventory: every script seen on your payment pages, with the provider recognised, a warning pill on anything that can inject more scripts, and one-click authorisation."},{"src":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/screenshot-2.png?rev=3711662","caption":"The printable payment page script integrity record: assessment context, the authorised scripts and the ones still awaiting review, and the 11.6.1 check record, ready to hand to an assessor."},{"src":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/screenshot-3.png?rev=3711662","caption":"Response header baselines per payment page, so a change to Content-Security-Policy or framing controls is flagged instead of going unnoticed."},{"src":"https:\/\/ps.w.org\/dragon-checkout-guard\/assets\/screenshot-4.png?rev=3711662","caption":"The assessment guide: answer three plain questions and it shows which PCI SSC FAQ 1588 route your answers point to and the records that route asks for."}],"raw_content":"<!--section=description-->\n<p>Nearly every WooCommerce store embeds its gateway's card fields in an iframe:\nStripe, WooPayments, PayPal, Braintree or Adyen. Since 31 March 2025, SAQ A\ncarries an eligibility criterion that such a merchant has confirmed its site is\nnot susceptible to attacks from scripts, and PCI SSC FAQ 1588 (February 2025)\nsets out the two ways to confirm it: apply techniques such as those in\nrequirements 6.4.3 and 11.6.1 to your own page, or hold written confirmation\nfrom the PCI DSS validated provider that its embedded form includes script\nattack protections when implemented to the provider's instructions. That second\nroute is not available from every provider, which leaves the first: an inventory\nof the scripts on the page around the iframe, a justification for each one, and\na check that they have not changed.<\/p>\n\n<p>The iframe is the provider's. The page it sits in is yours, and a script\ninjected into that page is what e-skimming (the Magecart style of attack) uses\nto read a shopper's details before the iframe ever sees them. Dragon Checkout\nGuard is the record of what runs on that page.<\/p>\n\n<p>It records, on your own WooCommerce checkout, order-pay, add-payment-method and\n(optionally) cart pages:<\/p>\n\n<ul>\n<li>an inventory of the scripts the page loads through <code>&lt;script&gt;<\/code> elements\n(external and inline), module preloads and import maps, with who added\neach one (event-handler attributes such as <code>onclick<\/code> and the documents in<\/li>\n<\/ul>\n\n<p>&lt;<\/p>\n\n<p>iframe srcdoc&gt; are not inventoried),\n* an authorisation entry per script: owner, business purpose, justification,\n  provider and integrity method,\n* a content hash per script, rechecked weekly, so a change is flagged,\n* a baseline of each page's security response headers, with drift flagged,\n* an evidence trail you can export for an assessor.<\/p>\n\n<p>It runs on your own server with no account, no JavaScript tag to a third party\nand no pageview cap.<\/p>\n\n<h4>What it does<\/h4>\n\n<ul>\n<li><strong>Script inventory<\/strong> - Every <code>&lt;script&gt;<\/code> src and inline block seen on your payment pages, discovered from the rendered HTML (read with PHP's HTML5 parser on PHP 8.4+, the way a browser reads it) and, optionally, from a real shopper's browser. Inline blocks printed by WordPress are identified by handle and position, so a change to one is recorded as drift on the same row rather than appearing as a new script every time. A change counts when it alters the block's code, the host of a URL it names, the rest of that URL, a relative <code>.js<\/code>\/<code>.mjs<\/code> path, a string containing HTML markup or <code>\/\/<\/code>, or a template literal; numbers, long token-like values (nonces, order keys, hashes) and other string values such as tracking ids and labels are ignored, so a rotating nonce never registers as drift.<\/li>\n<li><strong>Authorisation workflow<\/strong> - Record a justification, owner, business purpose, provider and integrity method (Subresource Integrity, hash monitored by this plugin, vendor-managed, or not feasible with a justification) for every script that belongs on your payment pages.<\/li>\n<li><strong>Weekly tamper check<\/strong> - A weekly WP-Cron scan, plus Scan now and WP-CLI. Every pending script is rehashed on each run, and a change that could not be written is reported as a failed scan rather than a clean one.<\/li>\n<li><strong>Response-header baseline<\/strong> - Captures the security-relevant response headers (Content-Security-Policy, X-Frame-Options, Strict-Transport-Security, Referrer-Policy, Permissions-Policy, the Cross-Origin trio, X-Content-Type-Options, Cache-Control and the CSP reporting headers) exactly as a shopper's browser receives them - read from each scan's own ordinary request, so headers your web server or CDN adds are included - and flags drift from the accepted baseline, including a header that stops being sent. A page a scan cannot reach (Order Pay, or a checkout the scan could not put a product in the cart for) is baselined from shoppers' own pageviews, which see only the headers WordPress itself sets. A header that appears after the baseline is flagged as added.<\/li>\n<li><strong>Recognised payment providers<\/strong> - A known-provider list fills in the provider, purpose and a suggested justification for the common gateways, and flags tag managers, analytics and chat widgets with a reviewer note. A script is recognised by where it is served from, or by the handle WordPress registered it under; an element id or name that only appears in the page itself is never trusted to name a provider.<\/li>\n<li><strong>Assessment guide<\/strong> - A three-question self-check that maps how your site takes payment to the FAQ 1588 route your answers point at, and to the records that route asks for. Your answers appear in the printable report as \"Assessment context\".<\/li>\n<li><strong>Exports for an assessor<\/strong> - Inventory CSV, 11.6.1 check-record CSV and a printable \"Payment page script integrity record\", plus the same data from WP-CLI.<\/li>\n<li><strong>Site Health tests<\/strong> - Whether the weekly check has run, whether any script is unauthorised, and whether any header baseline is outstanding.<\/li>\n<li><strong>Optional browser collector<\/strong> - On by default, it reports scripts that only exist once a real shopper's page is running (for example one injected by a tag manager), as observations you confirm before they join the inventory.<\/li>\n<\/ul>\n\n<h4>What it is not<\/h4>\n\n<p>Dragon Checkout Guard is not a Qualified Security Assessor, not a\ncertification service, and not a substitute for your own PCI DSS assessment.\nIt never claims a site \"is compliant\" or \"is certified\", and it does not\ndetermine which SAQ you complete: it produces evidence supporting the\nassessment you or your assessor make. Your acquirer or QSA settles your SAQ\neligibility.<\/p>\n\n<p>It also cannot see inside your gateway's iframe. It is not meant to: that\ndocument is served by the provider from its own origin and is covered by the\nprovider's own PCI DSS validation. The page around it is the merchant's, and\nthat is what this plugin inventories.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin has no service of its own. It makes no connection to Dragon Core, needs no account and sends no telemetry.<\/p>\n\n<p>It makes one kind of outbound request. To notice when a script on your payment pages changes, it takes a SHA-256 hash of each script file those pages load. A script served from your own site is read from disk. A script served from another host is downloaded from that host, because there is no other way to see its contents. Which hosts those are is decided by your own checkout, not by this plugin: <code>js.stripe.com<\/code>, a gateway SDK, Google Tag Manager, or whatever else your payment pages reference.<\/p>\n\n<ul>\n<li>When: during the weekly check, and when you start a scan yourself (\"Scan now\", or <code>wp dragon-checkout-guard scan<\/code>). At most 200 downloads a day, 2 MB per file, HTTPS only.<\/li>\n<li>What is sent: a GET request for that public script URL, from your server's IP address, with the user agent <code>Mozilla\/5.0 (compatible; DragonCheckoutGuard\/&lt;version&gt;; +https:\/\/dragoncore.ltd\/plugins\/dragon-checkout-guard)<\/code>. No cookies are sent, and nothing about your site, its settings, its customers or its orders.<\/li>\n<li>Where: only hosts your payment pages already reference, which every shopper's browser already contacts when it loads the page. Private, loopback, link-local and reserved addresses are refused.<\/li>\n<\/ul>\n\n<p>Those hosts belong to the providers you chose for your shop, so their own terms and privacy policies apply, the same as they do to the scripts your checkout already loads from them.<\/p>\n\n<p>The plugin also ships a list of recognised providers (Stripe, PayPal, Google Tag Manager and others). It is used only to label a script and suggest a justification for it. The plugin does not connect to any provider on that list unless your payment pages already load a script from it.<\/p>\n\n<p>To read a payment page the way a shopper's browser receives it, the plugin also requests that page from your own site. That request never leaves your server.<\/p>\n\n<h3>Credits<\/h3>\n\n<p>The WordPress.org listing icon is drawn with glyphs from Lucide (https:\/\/lucide.dev), ISC License. Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (https:\/\/feathericons.com, MIT License). All other copyright (c) for Lucide are held by Lucide Contributors 2022. The plugin itself does not include these icons.<\/p>\n\n<h3>Privacy Policy<\/h3>\n\n<p>Dragon Checkout Guard records, in your own WordPress database:<\/p>\n\n<ul>\n<li><strong>Script details.<\/strong> The URL (or a content fingerprint, for an inline script)\nof every script seen on an in-scope page, its content hash, and who authorised\nor revoked it and when.<\/li>\n<li><strong>Response headers.<\/strong> The security-relevant header values your payment pages\nare served with, as each scan's own request receives them, and a diff\nwhenever they change.<\/li>\n<li><strong>Scan and authorisation events.<\/strong> A forensic log of discoveries, scans and\nauthorisation actions, each attributed to the WordPress user who performed it,\nnever to a site visitor.<\/li>\n<\/ul>\n\n<p><strong>None of this identifies a site visitor.<\/strong> The plugin's tables have no IP\naddress, user-agent or session column. The optional browser collector, which\nreports scripts a real shopper's page ran, is rate-limited per address using a\nsalted hash of the address kept in a transient for two minutes; on sites without\na persistent object cache that transient lives in the options table for that\ntime. Nothing else about the visitor (browser, referrer and so on) is recorded,\nand inline script previews are stored with sensitive values redacted. Pageviews\nby a logged-in user are ignored for header baselines.<\/p>\n\n<p><strong>External requests:<\/strong> to notice when a script changes, the plugin downloads a\nscript your payment page already loads from another host and hashes the\ncontents; a script on your own site is read from disk. That request is a plain\nHTTPS GET of a public URL: it carries no data about your site, its configuration\nor its visitors. It never contacts Dragon Core or any other service of ours:\nthere is no account and no telemetry. See External services above.<\/p>\n\n<p>Uninstalling the plugin keeps all of this data by default; tick <strong>Delete this\nplugin's tables, options and evidence when it is uninstalled<\/strong> on the Settings\ntab first if you want it removed instead.<\/p>\n\n<p>For more information, visit <a href=\"https:\/\/dragoncore.ltd\/\">Dragon Core<\/a>.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin files to <code>\/wp-content\/plugins\/dragon-checkout-guard<\/code>, or install\ndirectly through the WordPress plugins screen.<\/li>\n<li>Activate the plugin through the 'Plugins' screen in WordPress.<\/li>\n<li>Visit Tools &gt; Checkout Guard, open the <strong>Pages<\/strong> tab and confirm which pages\nare in scope, then use <strong>Open &amp; capture<\/strong> or <strong>Scan now<\/strong> to record your\nfirst inventory.<\/li>\n<li>Review each script on the <strong>Inventory<\/strong> tab and authorise the ones that\nbelong on your payment pages.<\/li>\n<li>Open the <strong>Assessment guide<\/strong> tab and answer the three questions, so the\nprintable report carries the context your assessor needs.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20make%20my%20site%20pci%20dss%20compliant%3F\"><h3>Does this make my site PCI DSS compliant?<\/h3><\/dt>\n<dd><p>No. Dragon Checkout Guard produces evidence supporting your own PCI DSS 4.0.1\nrequirement 6.4.3 \/ 11.6.1 assessment. It is not a certification and does not,\non its own, make a site compliant: your assessor still makes that\ndetermination.<\/p><\/dd>\n<dt id=\"does%20it%20cover%20payment%20fields%20inside%20my%20gateway%27s%20iframe%3F\"><h3>Does it cover payment fields inside my gateway's iframe?<\/h3><\/dt>\n<dd><p>It covers the page the iframe sits in, which is the page that matters to you.\nThe iframe document belongs to your provider and is covered by the provider's\nown PCI DSS validation. Your own page is what an injected script gets access\nto, and since 31 March 2025 an iframe merchant keeps SAQ A eligibility by\nconfirming that page is not susceptible to attacks from scripts. PCI SSC FAQ\n1588 accepts two confirmations: techniques such as those in 6.4.3 and 11.6.1\napplied by you to your own page, or written confirmation from your PCI DSS\nvalidated provider that its embedded form includes script attack protections\nwhen implemented to its instructions. Providers rarely issue that written\nconfirmation, so most merchants take the first route, and this plugin's\ninventory, authorisation record and weekly check is that route's evidence.<\/p>\n\n<p>If your payment page's own scripts can touch card data, you are on SAQ A-EP or\nSAQ D and 6.4.3 and 11.6.1 apply in full: the same records are what those\nrequirements ask for. If your customer is redirected to the provider's own page\nto pay, the criterion does not apply to you, though assessors still commonly\nexpect the page that starts the payment to be monitored. Confirm your SAQ\neligibility with your acquirer or QSA: this plugin does not determine it for\nyou. The <strong>Assessment guide<\/strong> tab walks the same three questions and shows\nwhich route your answers point at.<\/p><\/dd>\n<dt id=\"what%20does%20an%20assessor%20want%20to%20see%3F\"><h3>What does an assessor want to see?<\/h3><\/dt>\n<dd><p>Two documents and their history. The inventory CSV has one row per script with:\nscript, type, party, pages, source, provider, owner, business purpose,\njustification, status, changed since authorised, authorised by, authorised at,\nintegrity method, current hash, last checked, review due, first seen, last seen\nand, where a hash could not be taken, why not. The 11.6.1 check record CSV has\none row per check that ran: checked at, trigger, pages checked, scripts checked,\nnew scripts, changes detected, outstanding changes, headers outstanding, the\noutcome (no change; changes recorded for scripts, for security headers, or for\nboth; or incomplete when a page could not be read), pages read, the pages not\nread and why, and script hashes verified. A response header this check found\nadded, removed or changed makes the outcome a change, not \"no change\".\n\"Print evidence report\" renders both, with your assessment guide\nanswers and the header baselines, as a printable \"Payment page script integrity\nrecord\". The same data is available from WP-CLI:\n    wp dragon-checkout-guard export --type=inventory|evidence --format=csv|json.<\/p><\/dd>\n<dt id=\"how%20do%20i%20compare%20this%20to%20a%20server-side%20scanner%3F\"><h3>How do I compare this to a server-side scanner?<\/h3><\/dt>\n<dd><p>Other WordPress plugins inventory the scripts your site's own code enqueues,\nfrom the server. Dragon Checkout Guard does that too, and then goes further:\nit can observe what actually ran in a real shopper's browser (which is where a\ntag manager's injected script first appears), it baselines and diffs the payment\npages' response headers, it records an authorisation taxonomy per script (owner,\npurpose, justification, integrity method) rather than a bare list, it exports\nassessor-ready records, and it is fully driveable from WP-CLI.<\/p><\/dd>\n<dt id=\"are%20there%20limits%20on%20the%20free%20version%3F\"><h3>Are there limits on the free version?<\/h3><\/dt>\n<dd><p>Nothing in this plugin is locked, limited or greyed out, and there is no limit\non how many scripts or pageviews it records. An optional paid add-on, Dragon\nCheckout Guard Pro, adds an evidence ledger, alerts and a review queue; this\nplugin is complete without it. The plugin does apply operational limits so it\nstays cheap on a live store: see the next answer.<\/p><\/dd>\n<dt id=\"what%20are%20the%20limits%3F\"><h3>What are the limits?<\/h3><\/dt>\n<dd><ul>\n<li><strong>Passive capture interval.<\/strong> A real shopper's pageview is sampled at most\nonce per page per interval (a setting, in minutes, default 10), and that\nsampling reads the script registry rather than buffering the page. A\npageview that shows the admin bar is not sampled. Scans and\n<strong>Open &amp; capture<\/strong> always run, and those do buffer the response; an\n<strong>Open &amp; capture<\/strong> leaves the admin bar's own scripts out, since no shopper\nis sent them.<\/li>\n<li><strong>Response capture cap.<\/strong> A buffered response (a scan or an <strong>Open &amp; capture<\/strong>,\nnever a passive pageview) is copied up to 5 MB\n(<code>Server_Capture::MAX_BUFFER_BYTES<\/code>); a larger page is passed through\nuntouched and that capture is recorded as failed rather than partial.<\/li>\n<li><strong>Remote hashing.<\/strong> Only <code>https<\/code> script URLs are fetched. At most 200 remote\nfetches per UTC day. After 3 failures a host is backed off for 1 hour. A\nresponse over 2 MB is not hashed. Every script that could not be hashed says\nwhy on its row and in the export (host did not resolve, host resolves to a\nprivate address, host temporarily backed off, daily budget exhausted, fetch\nfailed, response too large, local file not found, unsupported file type).<\/li>\n<li><strong>Stale scripts.<\/strong> A script not seen on any page for 30 days stops being\nrefetched and shows \"Not seen recently\".<\/li>\n<li><strong>Browser collector.<\/strong> 30 reports per minute per address (IPv6 grouped by\n\/64), 300 rows and 64 KB per report. 500 new observations in an hour, or 5000\nunconfirmed in total, pause the collector for an hour and raise an admin\nnotice; you can bulk dismiss observations from the Inventory tab or with\n  wp dragon-checkout-guard dismiss. If the table reaches 4000 rows in total,\nthe daily maintenance deletes the most recent unconfirmed observations - up to the\nnumber needed to bring the table down to 2500 - and records how many it took,\nso a flood cannot keep the collector shut off for 30 days. Confirmed\nobservations are never deleted, so a table held up by confirmed rows is left\nas it is; a real script is reported again by the next shopper's browser.<\/li>\n<li><strong>Retention.<\/strong> Unconfirmed browser observations are pruned after 30 days.\nEvent retention is a setting between 30 and 3650 days (default 365) and prunes\nonly the low-value kinds: discovered, scan run, scan failed, collector\ntripped and settings changed. Everything else, including the evidence,\nauthorised, revoked, changed, confirmed, headers changed, baseline accepted and\nbrowser observations cleared events, is kept regardless of the setting,\nbecause that is the record.<\/li>\n<li><strong>Exports.<\/strong> Exports carry at most 5000 rows and say so when they stop short.<\/li>\n<li><strong>One scan at a time.<\/strong> A lock stops the weekly cron, Scan now and WP-CLI\noverlapping. If the plugin's tables could not be created on activation, an\nadmin notice says so and the install is retried automatically.<\/li>\n<\/ul><\/dd>\n<dt id=\"which%20providers%20does%20it%20recognise%3F\"><h3>Which providers does it recognise?<\/h3><\/dt>\n<dd><p>Stripe, Stripe Radar, PayPal, Braintree, Adyen, Klarna, Mollie, Square, Google\nPay, Apple Pay and WooPayments are recognised as payment or fraud scripts, and\nWooCommerce's own front-end scripts are recognised as WordPress store code, each\nwith a suggested justification you can apply with one click. Google Tag\nManager, Google Analytics, Meta Pixel, Hotjar, Intercom, Zendesk, LiveChat and\nThreatMetrix are recognised too, and carry a reviewer note: a tag manager can\npull further scripts onto a payment page, a chat widget is third-party code on a\npage you are attesting for, and session recording on a payment page records\nkeystrokes unless it is masked or excluded. Anything unrecognised is listed\nplainly for you to justify or remove.<\/p>\n\n<p>A script is recognised by the host it is served from, by the plugin folder\ndirectly under your site's plugins directory that it is served from, or by the\nhandle WordPress itself registered it under, as\nrecorded by the plugin's server-side capture. A handle that only appears in the\npage's markup (an element id such as <code>stripe-js-extra<\/code>, or an importmap name)\nor in a shopper's browser is never used, since anything injected into the page\ncould claim one.<\/p><\/dd>\n<dt id=\"how%20does%20a%20scan%20read%20checkout%20when%20woocommerce%20redirects%20an%20empty%20cart%3F\"><h3>How does a scan read Checkout when WooCommerce redirects an empty cart?<\/h3><\/dt>\n<dd><p>A scan's request is logged out and has no cart, and WooCommerce sends an\nempty-cart checkout to the cart. So the scan's own request, and only that\nrequest, gets a cart holding one product: the lowest-id published, purchasable,\nin-stock simple product priced above zero among the first 100 simple products,\nor the one you pick with the <code>dragoncheckoutguard_scan_cart_product<\/code> filter\n(return 0 to turn this off). The cart exists only in that request's memory. No\nWooCommerce session row, cookie, persistent cart, order or stock reservation is\ncreated, and no add-to-cart event fires; cart-total and checkout-page hooks\nstill run, so a tool that records checkout views on the server may count one\nweekly scan visit. It is armed only by the scan's own single-use token (random,\nbound to the Checkout page, valid for 60 seconds and spent on first use), so an\nordinary visitor can never trigger it. Checkout then renders, with its payment\ngateway scripts, the way it does for a shopper with an item in the cart, and\nthe scan records it as read. Without a suitable product the scan is redirected\nas before, and the Pages tab says why.<\/p>\n\n<p>Once a scan reads Checkout, its response headers come from the scan's own\nrequest, which includes headers your web server or CDN adds. If Checkout's\nheader baseline so far came only from shoppers' pageviews, the first such scan\ncan flag those headers as changed: review them on the Headers tab and accept\nthe baseline.<\/p>\n\n<p>Add Payment Method still needs a signed-in customer, so a scan records it as\nnot read (it needs a signed-in customer). That is not an error; use Open &amp;\ncapture while logged in to record its scripts. Order Pay needs a real order and\nis skipped.<\/p><\/dd>\n<dt id=\"will%20a%20scan%20show%20up%20in%20my%20analytics%20or%20abandoned-cart%20tool%3F\"><h3>Will a scan show up in my analytics or abandoned-cart tool?<\/h3><\/dt>\n<dd><p>It can. To read Checkout, the weekly scan renders it with one product in a cart\nthat exists only in that request's memory. No session, cookie, persistent cart\nor order is created and no add-to-cart event fires, but WooCommerce's cart-total\nand checkout-page hooks still run. A server-side analytics, conversion or\nabandoned-cart tool that records checkout views from those hooks may therefore\ncount one visit per scan, with the <code>DragonCheckoutGuard\/<\/code> user agent. Browser\nanalytics do not see it, since the scan runs no JavaScript. Filter that user\nagent out in the tool, or return 0 from the\n    dragoncheckoutguard_scan_cart_product filter to scan Checkout without a cart\n(it is then recorded as redirected to the cart, not read).<\/p><\/dd>\n<dt id=\"does%20a%20page%20cache%20affect%20this%3F\"><h3>Does a page cache affect this?<\/h3><\/dt>\n<dd><p>Two ways. First, the collector token embedded in the page: rotating it\ninvalidates the old one immediately, but a full-page cache keeps serving pages\nwith the old token baked in until it is cleared, so any real shopper served from\ncache reports with a rejected token in the meantime. Clear your page cache right\nafter rotating. Second, a cached checkout response can mean the header baseline\nyou are comparing against is the cache's own headers rather than your origin\nserver's; scan with the cache bypassed, or exclude payment URLs from full-page\ncaching, for headers evidence to be meaningful.<\/p><\/dd>\n<dt id=\"what%20does%20the%20browser%20collector%20send%2C%20and%20where%20does%20it%20go%3F\"><h3>What does the browser collector send, and where does it go?<\/h3><\/dt>\n<dd><p>It sends the list of scripts (URLs and content hashes, plus which page they ran\non) that were actually observed running in a shopper's browser, to a REST\nendpoint on your own site: that report never leaves your server. It never sends\npersonal data: no name, email, IP address, cookie or session identifier is\nincluded in a report, and none is stored against it. The endpoint's rate limit\nuses a salted hash of the address, kept in a transient for two minutes; on sites\nwithout a persistent object cache that transient lives in the options table for\nthat time. Inline script previews are stored with sensitive values redacted.\n(The plugin does make its own outbound requests for a different reason: see the\nnext question.)<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20contact%20any%20external%20services%3F\"><h3>Does this plugin contact any external services?<\/h3><\/dt>\n<dd><p>It has no service of its own: nothing is ever sent to Dragon Core, and there is\nno account and no telemetry. The one outbound request it makes is downloading a\nscript your payment page already loads from another host, so it can hash the\ncontents and notice a change. A script on your own site is read from disk\ninstead. The request is a plain HTTPS GET of that public URL, with the\nconnection pinned to the validated address where the site's HTTP transport\nsupports it (curl), and it carries nothing about your site, its configuration or\nits visitors. The External services section above lists exactly what is sent,\nwhen and where.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20dragon%20activity%20log%3F\"><h3>Does it work with Dragon Activity Log?<\/h3><\/dt>\n<dd><p>Yes. If Dragon Activity Log is active, Dragon Checkout Guard records\n    script.authorised, <code>script.revoked<\/code>, <code>script.changed<\/code>, <code>headers.changed<\/code> and\n    collector.tripped to it, with <code>script.changed<\/code> and <code>collector.tripped<\/code> raised\nas the higher severity. Its own event log works with or without it.<\/p><\/dd>\n<dt id=\"can%20i%20remove%20all%20of%20this%20plugin%27s%20data%20when%20i%20uninstall%20it%3F\"><h3>Can I remove all of this plugin's data when I uninstall it?<\/h3><\/dt>\n<dd><p>Yes, but you have to opt in first. By default, uninstalling keeps your script\ninventory, authorisations and headers baseline so a reinstall picks up where you\nleft off. Tick <strong>Delete this plugin's tables, options and evidence when it is\nuninstalled<\/strong> on the Settings tab (or set the\n    dragoncheckoutguard_delete_data_on_uninstall option) before uninstalling if\nyou want everything removed instead.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.8<\/h4>\n\n<ul>\n<li>Scans read the Checkout page with a one-item cart held in memory for that scan only. Nothing is saved: no session, cookie, order or stock reservation. Choose the product with the dragoncheckoutguard_scan_cart_product filter.<\/li>\n<li>The 11.6.1 check record lists each page as read, skipped, needing a signed-in customer, redirected or not read, with the number of script hashes verified. A run that could not read every page is marked Incomplete, never No change.<\/li>\n<li>Header changes count in the check outcome, and a security header that appears after the baseline is flagged as added.<\/li>\n<li>A page answered with the login form (Add Payment Method, or Checkout on an account-only store) is recorded as needing a signed-in customer, and nothing from the login page is inventoried.<\/li>\n<li>Approving a script approves the content you were shown; if it changed since the page loaded, you are asked to look again.<\/li>\n<li>Tamper reasons explain in plain language that the contents differ from the version you authorised.<\/li>\n<li>After updating: stock stores see Checkout read for the first time, so expect new scripts and header rows to review once. Header rows the login form produced on Add Payment Method are removed.<\/li>\n<\/ul>\n\n<h4>1.0.7<\/h4>\n\n<ul>\n<li>Security: script addresses are read the way browsers read them, so a disguised address can no longer be filed under an approved script.<\/li>\n<li>Security: on PHP 8.4 and later, pages are read as a browser reads them, so scripts hidden with markup tricks are recorded. Older PHP versions use a fallback that records extra entries rather than miss any.<\/li>\n<li>Inline script checks now notice a changed address or template inside the script.<\/li>\n<li>Recognised-provider suggestions need a script WordPress itself registered, from a real plugin folder on your own site.<\/li>\n<li>Response headers are read from what your server actually sends.<\/li>\n<li>After updating: approved inline scripts that name an address will show one change to review, and a few scripts may appear once as new rows. Headers your server adds may show once as added.<\/li>\n<\/ul>\n\n<h4>1.0.6<\/h4>\n\n<ul>\n<li>Every screen, email and alert is now translatable, so community translations from translate.wordpress.org cover the whole plugin. Counts use proper plural forms, and numbers and dates follow your site's language.<\/li>\n<li>The inventory CSV now uses raw codes in every coded column, so it reads the same in any language; the printable report keeps readable labels.<\/li>\n<li>Settings show their units in the field labels.<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>Added: an occasional, dismissible request for a WordPress.org review once\na few scans have run, shown only on the plugin's own screen.<\/li>\n<li>Changed: listing title and tags for the WordPress.org directory.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>WordPress core scripts are recognised through <code>includes_url()<\/code> and\n  admin_url(), so an install that serves wp-includes or wp-admin from a\nnon-default location still files them under WordPress Core.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Scripts written as <code>type=\"text\/javascript; charset=utf-8\"<\/code> are now\ninventoried. Earlier versions skipped any script whose type carried a\nparameter, so run a scan after updating.<\/li>\n<li>A renamed content directory, or WordPress in its own directory, no longer\nleaves your own scripts filed under \"Site\" and unhashed.<\/li>\n<li>The request that hashes a third-party script names the plugin in its user\nagent instead of sending your site's URL.<\/li>\n<li>If junk reports fill the browser collector's table, the daily maintenance now\nclears unconfirmed rows within a day instead of the collector staying paused\nfor a month.<\/li>\n<li>Authorising, revoking or ignoring a script is saved together with its audit\nline. Saving settings, queuing a scan, rotating the collector token and\nrecording sightings all tell you when the database refused the write, and a\nbulk authorisation that only partly landed says so.<\/li>\n<li>Custom payment-page paths keep percent-encoded characters, so a page such as\n  \/caisse-s%C3%A9curis%C3%A9e can be scanned. If you had entered a path with\naccented or percent-encoded characters before, enter it again on the Settings\ntab. A refused path is now quoted in the error.<\/li>\n<li>Site Health also flags an authorised script that has changed since.<\/li>\n<li>Printed report: dates and handles no longer break mid-word, check triggers\nand the backlog sentence read in plain words, Review due prints as a date,\n\"Changed since authorised\" prints Yes or No and only for an authorised\nscript, and a script that is no longer authorised prints none of its old\nauthorisation record. The inventory CSV leaves the same cells blank for a\nscript that is not currently authorised, and exports Review due as a date.<\/li>\n<li>The History panel names events in plain words, the Review due date no longer\nshifts by a day on sites west of UTC, and an inline preview is withheld if it\ncould not be redacted.<\/li>\n<li>WP-CLI help reads as whole sentences, and <code>authorise<\/code> tells a missing script\nfrom a refused write.<\/li>\n<li>The printable report loads its stylesheet through WordPress, and the readme\nhas a full External services section.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>The printable \"Payment page script integrity record\" now lays the script\ninventory out in nine grouped columns, so it reads cleanly on A4 instead\nof breaking words and dates letter by letter. Every value is still\nprinted; the CSV exports are unchanged.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Added a pointer to the Pro add-on (plugin action link, footer line on the\nplugin's own screens, one dismissible notice). No feature changed.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release: payment page script inventory, authorisation workflow,\nweekly content-hash tamper check, response-header baseline with drift\ndetection, assessor exports (inventory CSV, 11.6.1 check record CSV and a\nprintable integrity record), SAQ A assessment guide, Site Health tests,\nWP-CLI commands, and an optional browser collector for scripts a real\nshopper's page runs.<\/li>\n<li>Inline scripts printed by WordPress are tracked by handle and position, so a\nchange to one is recorded as drift on the same row rather than as a new\nscript, and inline previews are stored with sensitive values redacted.<\/li>\n<li>Local scripts with percent-encoded or non-ASCII file names in their URL are\nhashed from disk, so content drift is detected for them too, and every script\nthat could not be hashed records why.<\/li>\n<li>Accepting a header baseline reports an error if the database update fails,\ninstead of confirming a baseline that was never saved.<\/li>\n<li>The schema version is only recorded once every plugin table is confirmed to\nexist and the scan jobs are scheduled; a failed install shows an admin notice\nand is retried automatically every 10 minutes, or immediately on\nre-activation.<\/li>\n<li>A content-hash drift and its audit entry are written together, so the Changed\nstatus and the event log can no longer disagree.<\/li>\n<\/ul>","raw_excerpt":"Payment-page script inventory, authorisation record, weekly tamper check and header baseline: PCI DSS 6.4.3 \/ 11.6.1 and SAQ A evidence. No account.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/372691","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=372691"}],"author":[{"embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/dragoncoreltd"}],"wp:attachment":[{"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=372691"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=372691"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=372691"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=372691"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=372691"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/kn.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=372691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}