Download index.html from getreadystack/pci-payment-script-lint: direct link, hf CLI and curl.
- Browser
- Download file 46.7 kB
-
https://huggingface.co/spaces/getreadystack/pci-payment-script-lint/resolve/main/index.html
- Command line
-
hf download hf://spaces/getreadystack/pci-payment-script-lint/index.html
-
curl -L -o index.html https://huggingface.co/spaces/getreadystack/pci-payment-script-lint/resolve/main/index.html
46.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1"> | |
| <title>PCI Payment Script requirements — check payment scripts against pci</title> | |
| <meta name="description" content="This free browser tool checks checkout and payment pages for the listed PCI DSS v4.0.1 script integrity, inventory, CSP authorization, and tamper-detection requirements."> | |
| <style> | |
| :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512} | |
| .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px} | |
| textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box} | |
| .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer} | |
| input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px} | |
| table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65} | |
| .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600} | |
| .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa} | |
| </style> | |
| <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script> | |
| <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/pci-payment-script-lint"></head> | |
| <body> | |
| <div class="wrap"> | |
| <h1>PCI Payment Script requirements — check payment scripts against pci</h1> | |
| <p class="lede">Lint checkout and payment pages against PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1: script integrity, written script inventory, CSP authorisation, and weekly tamper detection. Runs entirely in your browser — nothing is uploaded.</p> | |
| <textarea id="src" spellcheck="false"><!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Pay — order #4417</title> | |
| <!-- Compliance note: we are assessed under PCI DSS 4.0 (SAQ A). Requirements 6.4.3 and | |
| 11.6.1 are future-dated best practices, so a written script inventory is not required | |
| here; the QSA said we can revisit at the next annual assessment. --> | |
| <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' *"> | |
| </head> | |
| <body> | |
| <h1>Payment</h1> | |
| <form id="pay-form" method="post" action="/orders/4417/pay"> | |
| <input id="card-number" name="card_number" autocomplete="cc-number"> | |
| <input id="card-cvc" name="cvc" autocomplete="cc-csc"> | |
| <button id="pay" type="submit">Pay 49.00 EUR</button> | |
| </form> | |
| <script src="http://cdn.legacy-cart.example/cart.js"></script> | |
| <script src="https://www.googletagmanager.com/gtm.js?id=GTM-77KQ2"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/card-validator@latest/dist/card-validator.min.js"></script> | |
| <script src="https://js.stripe.com/v3/"></script> | |
| <script src="https://widget.chat-support.example/loader.js" integrity="sha384-CcRt7pQnVx0LmEaZbYkJ9uFdHs2WgToIrN4vXqMePzSlBjUyKhAfDwCgNmXtQvRe"></script> | |
| <script> | |
| document.getElementById('pay').addEventListener('click', function () { window.dataLayer.push({ event: 'pay' }); }); | |
| </script> | |
| </body> | |
| </html></textarea> | |
| <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div> | |
| <div id="out" aria-live="polite"></div> | |
| <div id="paid"><strong>Free here:</strong> Check the payment page open in your editor against every script rule and name the exact PCI DSS v4.0.1 requirement each finding breaks. <strong>In the editor:</strong> Sweep every checkout template in the workspace and write the dated 6.4.3 script inventory report you hand the assessor — that part asks for a licence key. <a id="buy" href="https://getreadystack.com/api/buy/cl/polar_cl_YLfFQJa9NbIbhJdjhg77rY07HqFCEhrFOmzmW3zHrI8">Get the full version — $29 once</a> <span class="note">$29 once · one licence key per person or team seat · PCI consultants and QSAs typically bill $150-$300 an hour for the same payment-page script review.</span></div> | |
| <p class="note">Same engine as the VS Code extension, byte for byte.</p> | |
| </div> | |
| <script>var PCI_RULES = [ | |
| { | |
| "id": "inventory-missing", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "This page loads third-party scripts but carries no script inventory. 6.4.3 requires a written inventory of every script on the payment page, each with a business justification. Add a \u003c!-- pci-script-inventory --\u003e block listing each host." | |
| }, | |
| { | |
| "id": "script-not-inventoried", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "Script host is not listed in the page's script inventory, so it is not an authorised script under 6.4.3." | |
| }, | |
| { | |
| "id": "inventory-no-justification", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "Inventory entry has no usable business justification. 6.4.3 asks why each script is needed, in writing; placeholders do not count." | |
| }, | |
| { | |
| "id": "script-no-integrity", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "External payment-page script has no integrity= hash, so there is no method assuring the script has not been altered in transit or at the origin (6.4.3)." | |
| }, | |
| { | |
| "id": "script-over-http", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "Payment-page script is loaded over plain http://. Integrity cannot be assured and the content can be rewritten on the wire." | |
| }, | |
| { | |
| "id": "script-integrity-no-crossorigin", | |
| "sev": "warn", | |
| "req": "6.4.3", | |
| "msg": "integrity= is present but crossorigin= is missing, so the browser will not run the integrity check the way you expect on a cross-origin script." | |
| }, | |
| { | |
| "id": "script-unpinned-version", | |
| "sev": "warn", | |
| "req": "6.4.3", | |
| "msg": "Script URL points at a moving target (latest/next), so today's authorised script is not tomorrow's. Pin a version before hashing it." | |
| }, | |
| { | |
| "id": "tag-manager-on-payment-page", | |
| "sev": "warn", | |
| "req": "6.4.3", | |
| "msg": "A tag manager or session-recording loader can inject further scripts at runtime, which means the authorised list is decided outside this file. Container contents have to be inventoried too." | |
| }, | |
| { | |
| "id": "csp-missing", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "No Content-Security-Policy on this payment page. Without one there is no technical method confirming that only authorised scripts execute in the consumer's browser." | |
| }, | |
| { | |
| "id": "csp-unsafe-inline", | |
| "sev": "error", | |
| "req": "6.4.3", | |
| "msg": "script-src allows 'unsafe-inline' or a wildcard, which authorises anything. That is the same as having no script authorisation at all." | |
| }, | |
| { | |
| "id": "csp-report-only", | |
| "sev": "warn", | |
| "req": "6.4.3", | |
| "msg": "Only Content-Security-Policy-Report-Only is set. Report-only blocks nothing; it records and lets the script run." | |
| }, | |
| { | |
| "id": "inline-script-no-nonce", | |
| "sev": "warn", | |
| "req": "6.4.3", | |
| "msg": "Inline script has no nonce= and no hash, so it can only run if the policy is loosened for every inline script on the page." | |
| }, | |
| { | |
| "id": "tamper-detection-missing", | |
| "sev": "error", | |
| "req": "11.6.1", | |
| "msg": "No change- and tamper-detection mechanism is declared for this page. 11.6.1 wants alerts on unauthorised modification of the HTTP headers and of the payment page content. Declare it in a \u003c!-- pci-tamper-detection: ... --\u003e line or set a CSP report endpoint." | |
| }, | |
| { | |
| "id": "tamper-interval-too-long", | |
| "sev": "error", | |
| "req": "11.6.1", | |
| "msg": "Tamper detection is evaluated less often than every seven days. 11.6.1 asks for at least weekly evaluation, or the interval your targeted risk analysis defends." | |
| }, | |
| { | |
| "id": "future-dated-claim-expired", | |
| "sev": "error", | |
| "req": "6.4.3 / 11.6.1", | |
| "msg": "This page still describes the payment-script requirements as future-dated or optional. They stopped being best practice and became mandatory on 2025-03-31." | |
| }, | |
| { | |
| "id": "stale-standard-version", | |
| "sev": "warn", | |
| "req": "", | |
| "msg": "This file cites a retired version of the standard. v4.0.1 is the only active version; v3.2.1 retired 2024-03-31 and v4.0 retired 2024-12-31." | |
| } | |
| ] | |
| ;</script> | |
| <script>// PCI DSS 6.4.3 Payment Script Lint — the brain. Same file runs in Node (extension) and in the browser (free web page). | |
| ; | |
| var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.PCI_RULES; | |
| var STANDARD = 'PCI DSS v4.0.1'; | |
| var MANDATORY_FROM = '2025-03-31'; | |
| var BY = {}; | |
| RULES.forEach(function (r) { BY[r.id] = r; }); | |
| function lineAt(text, idx) { return text.slice(0, idx).split('\n').length; } | |
| function daysSince(from, today) { | |
| var a = Date.parse(from + 'T00:00:00Z'), b = Date.parse(today + 'T00:00:00Z'); | |
| if (isNaN(a) || isNaN(b)) return null; | |
| return Math.round((b - a) / 86400000); | |
| } | |
| function attr(attrs, name) { | |
| var m = attrs.match(new RegExp('\\s' + name + '\\s*=\\s*("|\')((?:(?!\\1)[\\s\\S])*)\\1', 'i')); | |
| return m ? m[2] : (new RegExp('\\s' + name + '(\\s|$|=)', 'i').test(attrs) ? '' : null); | |
| } | |
| function hostOf(src) { | |
| var m = String(src).match(/^(https?:)?\/\/([^\/?#]+)/i); | |
| return m ? m[2].toLowerCase() : null; | |
| } | |
| function check(text, opts) { | |
| opts = opts || {}; | |
| text = String(text == null ? '' : text); | |
| var today = opts.today || new Date().toISOString().slice(0, 10); | |
| var findings = []; | |
| function add(id, line, extra) { | |
| var r = BY[id]; | |
| var ref = r.req ? STANDARD + ' ' + r.req : STANDARD; | |
| findings.push({ check: id, sev: r.sev, line: line || 1, msg: r.msg + (extra ? ' ' + extra : '') + ' [' + ref + ']' }); | |
| } | |
| // --- collect script tags ------------------------------------------------- | |
| var externals = [], inlines = [], m, tagRe = /<script\b([^>]*)>/gi; | |
| while ((m = tagRe.exec(text)) !== null) { | |
| var attrs = m[1] || '', line = lineAt(text, m.index), src = attr(attrs, 'src'); | |
| if (src) externals.push({ src: src, attrs: attrs, line: line, host: hostOf(src) }); | |
| else inlines.push({ attrs: attrs, line: line, end: m.index + m[0].length }); | |
| } | |
| var thirdParty = externals.filter(function (s) { return s.host; }); | |
| // --- script inventory (6.4.3) ------------------------------------------- | |
| var inv = text.match(/<!--\s*pci-script-inventory\b([\s\S]*?)-->/i); | |
| var invHosts = [], invBlockLine = inv ? lineAt(text, inv.index) : 0; | |
| if (inv) { | |
| var body = inv[1].split('\n'); | |
| body.forEach(function (raw, i) { | |
| var t = raw.trim(); | |
| if (!t || /^(pci|host|script)\b.*:?$/i.test(t) && !/[\/.]/.test(t)) return; | |
| var hm = t.match(/((?:https?:)?\/\/[^\s"']+|[a-z0-9.-]+\.[a-z]{2,})/i); | |
| if (!hm) return; | |
| var h = hostOf(hm[1]) || hm[1].toLowerCase(); | |
| invHosts.push(h); | |
| var just = t.replace(hm[1], ''); | |
| var ok = /justification\s*:\s*\S+/i.test(just) && !/\b(TODO|TBD|FIXME|N\/A|pending|unknown|\?\?\?)\b/i.test(just); | |
| if (!ok) add('inventory-no-justification', invBlockLine + i + 1, 'Entry: ' + h + '.'); | |
| }); | |
| } else if (thirdParty.length) { | |
| add('inventory-missing', thirdParty[0].line, thirdParty.length + ' third-party script tag(s) on this page are unaccounted for.'); | |
| } | |
| // --- per-script checks --------------------------------------------------- | |
| externals.forEach(function (s) { | |
| if (/^http:\/\//i.test(s.src)) add('script-over-http', s.line, 'src=' + s.src); | |
| if (!s.host) return; // first-party relative path: integrity not required | |
| if (inv && invHosts.indexOf(s.host) === -1) add('script-not-inventoried', s.line, 'Host: ' + s.host + '.'); | |
| var integrity = attr(s.attrs, 'integrity'); | |
| if (!integrity) add('script-no-integrity', s.line, 'Host: ' + s.host + '.'); | |
| else if (attr(s.attrs, 'crossorigin') === null) add('script-integrity-no-crossorigin', s.line, 'Host: ' + s.host + '.'); | |
| if (/@latest|\/latest\/|\/next\/|@next\b|[?&]v(ersion)?=latest/i.test(s.src)) add('script-unpinned-version', s.line, 'src=' + s.src); | |
| if (/googletagmanager\.com|gtm\.js|tagmanager|segment\.(com|io)|optimizely|hotjar|fullstory|clarity\.ms/i.test(s.src)) add('tag-manager-on-payment-page', s.line, 'Host: ' + s.host + '.'); | |
| }); | |
| // --- content security policy (6.4.3) ------------------------------------ | |
| var enforced = [], reportOnly = [], metaRe = /<meta\b[^>]*>/gi; | |
| while ((m = metaRe.exec(text)) !== null) { | |
| var tag = m[0]; | |
| if (!/http-equiv\s*=\s*["']content-security-policy/i.test(tag)) continue; | |
| var rec = { content: attr(tag, 'content') || '', line: lineAt(text, m.index) }; | |
| (/content-security-policy-report-only/i.test(tag) ? reportOnly : enforced).push(rec); | |
| } | |
| var hdrRe = /Content-Security-Policy(-Report-Only)?\s*:\s*([^\n"'`]+)/gi; | |
| while ((m = hdrRe.exec(text)) !== null) { | |
| var rec2 = { content: m[2], line: lineAt(text, m.index) }; | |
| (m[1] ? reportOnly : enforced).push(rec2); | |
| } | |
| var allCsp = enforced.concat(reportOnly).map(function (c) { return c.content; }).join(' '); | |
| if (!enforced.length && !reportOnly.length) add('csp-missing', 1, thirdParty.length ? thirdParty.length + ' third-party script tag(s) run unrestricted.' : ''); | |
| else if (!enforced.length) add('csp-report-only', reportOnly[0].line, ''); | |
| enforced.forEach(function (c) { | |
| var dm = c.content.match(/script-src[^;]*/i) || c.content.match(/default-src[^;]*/i); | |
| if (!dm) return; | |
| if (/'unsafe-inline'/i.test(dm[0])) add('csp-unsafe-inline', c.line, "Directive: " + dm[0].trim() + '.'); | |
| else if (/(^|\s)\*(\s|$)/.test(dm[0])) add('csp-unsafe-inline', c.line, 'Directive: ' + dm[0].trim() + '.'); | |
| }); | |
| var nonceInPolicy = /'nonce-/i.test(allCsp); | |
| inlines.forEach(function (s) { | |
| var type = attr(s.attrs, 'type') || ''; | |
| if (/json/i.test(type)) return; | |
| var body = text.slice(s.end, text.indexOf('<\/script>', s.end) === -1 ? s.end : text.indexOf('<\/script>', s.end)); | |
| if (body.trim().length < 8) return; | |
| if (attr(s.attrs, 'nonce') === null && !/\bintegrity\s*=/i.test(s.attrs)) add('inline-script-no-nonce', s.line, nonceInPolicy ? "The policy carries a nonce, but this tag does not." : ''); | |
| }); | |
| // --- tamper detection (11.6.1) ------------------------------------------ | |
| var tam = text.match(/<!--\s*pci-tamper-detection\s*:\s*([\s\S]*?)-->/i); | |
| var reportEndpoint = /report-(uri|to)\s+\S/i.test(allCsp); | |
| if (!tam && !reportEndpoint) add('tamper-detection-missing', 1, ''); | |
| if (tam) { | |
| var iv = tam[1].match(/every\s+(\d+)\s*(hour|day|week|month)/i); | |
| var days = null; | |
| if (iv) { | |
| var n = parseInt(iv[1], 10), unit = iv[2].toLowerCase(); | |
| days = unit === 'hour' ? n / 24 : unit === 'day' ? n : unit === 'week' ? n * 7 : n * 30; | |
| } | |
| if (days === null) add('tamper-interval-too-long', lineAt(text, tam.index), 'No evaluation interval is declared in the mechanism.'); | |
| else if (days > 7) add('tamper-interval-too-long', lineAt(text, tam.index), 'Declared interval: every ' + iv[1] + ' ' + iv[2] + '(s) = ' + Math.round(days) + ' days.'); | |
| } | |
| // --- the part chatbots get wrong: dates and versions --------------------- | |
| var fdRe = /(future[- ]dated|best practice until|not (?:yet )?required|becomes? mandatory|optional until|effective from)[^\n]{0,90}/gi; | |
| var since = daysSince(MANDATORY_FROM, today); | |
| while ((m = fdRe.exec(text)) !== null) { | |
| add('future-dated-claim-expired', lineAt(text, m.index), | |
| 'As of ' + today + ' that date ' + (since === null ? 'has passed' : since >= 0 ? 'passed ' + since + ' days ago' : 'is ' + (-since) + ' days away') + '. Quoted: "' + m[0].trim().slice(0, 70) + '".'); | |
| } | |
| var svRe = /PCI[\s-]?DSS[\s,]*v?\.?\s*(3\.2\.1|3\.2|3\.1|4\.0)(?!\.\d)/gi; | |
| while ((m = svRe.exec(text)) !== null) add('stale-standard-version', lineAt(text, m.index), 'Found "' + m[0].trim() + '".'); | |
| findings.sort(function (a, b) { return (a.line - b.line) || a.check.localeCompare(b.check); }); | |
| return { findings: findings, standard: STANDARD, mandatory_from: MANDATORY_FROM, today: today, rule_count: RULES.length }; | |
| } | |
| var API = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length }; | |
| if (typeof module !== 'undefined') module.exports = API; | |
| if (typeof window !== 'undefined') window.PCIENGINE = API; | |
| </script> | |
| <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다. | |
| ; | |
| function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } | |
| function summaryLine(res) { | |
| const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length; | |
| return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.'; | |
| } | |
| function toText(res, label) { | |
| const f = (res && res.findings) || []; | |
| const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)]; | |
| f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); }); | |
| return lines.join('\n'); | |
| } | |
| function toHtml(res) { | |
| const f = (res && res.findings) || []; | |
| if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>'; | |
| const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join(''); | |
| return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>'; | |
| } | |
| var PCIREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = PCIREPORT; | |
| else window.PCIREPORT = PCIREPORT; | |
| </script> | |
| <script> | |
| (function(){ | |
| var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today'); | |
| today.value=new Date().toISOString().slice(0,10); | |
| function run(){ try { var res=window.PCIENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.PCIREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } } | |
| go.onclick=run; if (src.value.trim()) run(); | |
| })(); | |
| </script> | |
| <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://marketplace.visualstudio.com/items?itemName=ReadyStack.pci-payment-script-lint" rel="noopener" data-yj-install-door="mp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'pci-payment-script-lint',src:'mp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">VS Code Marketplace</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">ext install ReadyStack.pci-payment-script-lint</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section> | |
| <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind PCI DSS 6.4.3 Payment Script Lint every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"pci-payment-script-lint",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);})();</script></section> | |
| <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Reads your checkout template and names every browser script that fails PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">PCI consultants and QSAs typically bill $150-$300 an hour for the same payment-page script review</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_YLfFQJa9NbIbhJdjhg77rY07HqFCEhrFOmzmW3zHrI8" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section> | |
| <section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-11">Updated 2026-09-11</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-11","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku37484_result_card.jpg" alt="PCI DSS 6.4.3 Payment Script Lint" loading="lazy" style="max-width:100%;border-radius:6px"></p> | |
| <p>Sixteen findings on one checkout page, fifteen of them errors: that is what this lint returns on a template most e-commerce developers in the EU and the US would call their own.</p> | |
| <p>PCI DSS v4.0.1 has two requirements that stopped being future-dated on 31 March 2025. Requirement 6.4.3: manage every script loaded and executed in the consumer browser on the payment page. Requirement 11.6.1: run change- and tamper-detection against that page, evaluated at least every seven days unless a targeted risk analysis says otherwise. Both land on one file — the template that renders the page where the card number is typed.</p> | |
| <p>6.4.3 has three bullets: a method that confirms each script is authorised, a method that assures its integrity, and an inventory of all scripts with a written business or technical justification for each. The first two are engineering — a Content-Security-Policy and Subresource Integrity. The third is a document, and the document is where assessments go wrong, because it drifts the moment somebody adds a tag.</p> | |
| <p>The bundled dirty.html, checked as of 2026-09-11:</p> | |
| <p>L2 inventory_review_stale reviewed 832 days ago L3 tamper_evaluation_stale last evaluated 43 days ago L7 csp_unsafe_inline script-src contains 'unsafe-inline' L10 script_sri_missing no integrity=, and fetched over http:// L24 script_version_floating .../latest/hostedfields.js L28 injector_not_declared Google Tag Manager L30 script_sri_no_crossorigin integrity= present, crossorigin= absent L33 inline_script_no_nonce no nonce= on an inline block</p> | |
| <p>Sixteen findings across eight lines; five scripts, one written justification between them. Line 28 is worth a look: Tag Manager is not one script but a loader for however many the container holds today, all of them inside 6.4.3.</p> | |
| <p>The <code>integrity=</code> on line 30 is real, correct, and does nothing. Subresource Integrity is enforced cross-origin only when the request is made in CORS mode, so <code>crossorigin</code> has to be on the tag. Without it the browser never compares the hash. It reads like evidence and it is not.</p> | |
| <p>The <code>/latest/</code> on line 24 is the same failure in another costume: if the bytes behind an authorised URL can change upstream, the authorisation expired without telling anyone.</p> | |
| <p>The two date-driven checks are what make this a lint rather than a checklist. The tamper note on line 3 is not missing; it is 43 days old against a 7-day cadence. The inventory note on line 2 was reviewed 832 days ago, by someone who has left. A page that passes on a Monday fails the following Tuesday with no code change — which is what 11.6.1 is describing.</p> | |
| <p>So the inventory lives in the page: a <code><!-- pci:script owner="..." reason="..." --></code> comment above the tag it justifies, a <code><!-- pci:tamper ... evaluated=YYYY-MM-DD --></code> note recording the mechanism and its last evaluation, and <code><!-- pci:inventory reviewed=YYYY-MM-DD --></code> dating the review. The lint fails when a tag arrives without a justification, so the register is maintained by the review that merges it. Fourteen checks; the clean fixture returns zero.</p> | |
| <p>Checking one page is free and finishes the job: all fourteen checks, every finding on its line with the requirement reference, in VS Code or in the browser, nothing uploaded. The licence buys a different job — a sweep of every payment template in the repository, written out as a dated register for the assessor. $29 once. A QSA hour compiling the same register bills $150-$300.</p> | |
| <p>Not legal advice: your QSA, acquirer and SAQ or ROC scope decide what applies.</p></div><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does PCI DSS 6.4.3 Payment Script Lint actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a checkout or payment page and reports every browser script that fails PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1: scripts with no integrity hash, scripts missing from the written inventory, entries with no business justification, a policy that allows unsafe-inline, and missing or slower-than-weekly tamper detection. Sixteen checks, each tagged with its requirement number.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is it for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">E-commerce developers who own the checkout template at a merchant that takes card details on its own pages: Shopify Plus and WooCommerce theme developers, Laravel and Rails shop teams, and anyone whose acquirer has just asked for a payment-page script inventory. It is a code linter, so it belongs to whoever edits the HTML, not to the compliance inbox.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why can't I just ask a chatbot or use a generic HTML linter?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Generic linters do not know what a payment page is; they flag unused tags, not unauthorised scripts. Chatbots still answer from PCI DSS v3.2.1 or describe 6.4.3 and 11.6.1 as future-dated best practices, which stopped being true on 2025-03-31. Neither one reads your actual checkout file line by line.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free, with no key and no limit: check the file open in your editor against all sixteen rules, with each finding on its line and its requirement number. The licence adds a different job, ownership of the evidence: sweeping every checkout template in the workspace and writing the dated script inventory report you hand to an assessor.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost me otherwise?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">PCI consultants and QSAs typically bill $150 to $300 an hour for the same payment-page script review, and they bill again after each template change. The licence is $29 once. The other cost is the assessment itself: arriving without a script inventory means a finding you close under deadline pressure.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Does it change my files or send my code anywhere?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">No. The extension reads the file, reports findings in the editor, and writes nothing except the report you ask for. There is no network call in the checker itself; the only request the extension ever makes is validating a licence key you typed.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does PCI DSS 6.4.3 Payment Script Lint actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a checkout or payment page and reports every browser script that fails PCI DSS v4.0.1 requirements 6.4.3 and 11.6.1: scripts with no integrity hash, scripts missing from the written inventory, entries with no business justification, a policy that allows unsafe-inline, and missing or slower-than-weekly tamper detection. Sixteen checks, each tagged with its requirement number."}}, {"@type": "Question", "name": "Who is it for?", "acceptedAnswer": {"@type": "Answer", "text": "E-commerce developers who own the checkout template at a merchant that takes card details on its own pages: Shopify Plus and WooCommerce theme developers, Laravel and Rails shop teams, and anyone whose acquirer has just asked for a payment-page script inventory. It is a code linter, so it belongs to whoever edits the HTML, not to the compliance inbox."}}, {"@type": "Question", "name": "Why can't I just ask a chatbot or use a generic HTML linter?", "acceptedAnswer": {"@type": "Answer", "text": "Generic linters do not know what a payment page is; they flag unused tags, not unauthorised scripts. Chatbots still answer from PCI DSS v3.2.1 or describe 6.4.3 and 11.6.1 as future-dated best practices, which stopped being true on 2025-03-31. Neither one reads your actual checkout file line by line."}}, {"@type": "Question", "name": "What is free and what needs a licence?", "acceptedAnswer": {"@type": "Answer", "text": "Free, with no key and no limit: check the file open in your editor against all sixteen rules, with each finding on its line and its requirement number. The licence adds a different job, ownership of the evidence: sweeping every checkout template in the workspace and writing the dated script inventory report you hand to an assessor."}}, {"@type": "Question", "name": "What would this cost me otherwise?", "acceptedAnswer": {"@type": "Answer", "text": "PCI consultants and QSAs typically bill $150 to $300 an hour for the same payment-page script review, and they bill again after each template change. The licence is $29 once. The other cost is the assessment itself: arriving without a script inventory means a finding you close under deadline pressure."}}, {"@type": "Question", "name": "Does it change my files or send my code anywhere?", "acceptedAnswer": {"@type": "Answer", "text": "No. The extension reads the file, reports findings in the editor, and writes nothing except the report you ask for. There is no network call in the checker itself; the only request the extension ever makes is validating a licence key you typed."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:37484,slug:"pci-payment-script-lint",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/payment-page-script-audit"><span class="yj-rel-t">PCI DSS 6.4.3 Payment Page Script Inventory Auditor</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/pci-payment-page-script-lint"><span class="yj-rel-t">PCI DSS 6.4.3 Payment Page Script Lint</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/pci-payment-page-script-audit"><span class="yj-rel-t">PCI Payment Page Script Audit: requirement 6.4.3 and 11.6.1 on your checkout m</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/webflow-restaurant-template"><span class="yj-rel-t">Webflow Restaurant Template — delivered as self-contained HTML you own: 26 pag</span><span class="yj-rel-p">$49</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/personal-data-map-dsar-audit"><span class="yj-rel-t">Personal Data Map: find every column a GDPR subject access request has to reac</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/about-page-template"><span class="yj-rel-t">About Page Template — 6 Finished About Pages for Small Service Businesses (Sel</span><span class="yj-rel-p">$49</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&","<":"<",">":">",'"':"""}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div> | |
| <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script> | |
| <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script> | |
| </body> | |
| </html> | |