feat(blog): enable live likes and first-party views
This commit is contained in:
1 parent
89cdb00765
commit
8c19266760
13 files changed
+631
-30
No files matched your search
@@ -85,20 +85,25 @@ function formAAdapters(html) {
|
||||
return [...html.matchAll(/<section\b[^>]*\bdata-jotform-form-a\b[^>]*>/gi)].map((match) => match[0]);
|
||||
}
|
||||
|
||||
function unsafeSignals(html, allowApprovedFormA = false) {
|
||||
function unsafeSignals(html, allowApprovedFormA = false, allowApprovedCounters = false) {
|
||||
const signals = [];
|
||||
if (!allowApprovedFormA && /<iframe\b[^>]*\bsrc=["'][^"']*jotform/i.test(html)) signals.push('active Jotform iframe');
|
||||
if (!allowApprovedFormA && /262946078314059/i.test(html)) signals.push('disabled Form A identifier');
|
||||
if (/<script\b[^>]*\bsrc=["'][^"']*(?:jotform|googletagmanager|google-analytics|doubleclick)/i.test(html)) signals.push('active vendor tracking/form script');
|
||||
if (!allowApprovedFormA && /data-(?:form|forms)-mode=["']live["']/i.test(html)) signals.push('live form mode');
|
||||
if (/api\.azinstitute4autism\.com/i.test(html)) signals.push('production API origin');
|
||||
const counterReviewed = allowApprovedCounters
|
||||
? html.replace(/<div\b[^>]*\bdata-counter\b[^>]*>/gi, (tag) =>
|
||||
tag.replace('data-api-base="https://api.azinstitute4autism.com"', ''))
|
||||
: html;
|
||||
if (/api\.azinstitute4autism\.com/i.test(counterReviewed)) signals.push('production API origin');
|
||||
if (/AIza[0-9A-Za-z_-]{20,}|-----BEGIN (?:RSA|OPENSSH|EC) PRIVATE\sKEY-----|(?:api[_-]?key|secret|password)\s*[:=]/i.test(html)) signals.push('credential-like content');
|
||||
return signals;
|
||||
}
|
||||
|
||||
export async function verifyArtifact(root, mode = 'staging', forms = 'live', portal = { mode: 'none' }) {
|
||||
export async function verifyArtifact(root, mode = 'staging', forms = 'live', portal = { mode: 'none' }, counters = 'off') {
|
||||
if (!['staging', 'indexing'].includes(mode)) throw new Error('Artifact mode must be staging or indexing; form availability is selected separately with --forms=live|off|mock.');
|
||||
if (!['live', 'off', 'mock'].includes(forms)) throw new Error('Artifact forms must be live, off, or mock.');
|
||||
if (!['live', 'off', 'mock'].includes(counters)) throw new Error('Artifact counters must be live, off, or mock.');
|
||||
if (!portal || !['none', 'synthetic', 'production'].includes(portal.mode)) throw new Error('Portal artifact mode must be none, synthetic, or production.');
|
||||
if (portal.mode === 'production' && (!/^[0-9a-f]{64}$/.test(portal.sha256 ?? '') || mode !== 'indexing')) throw new Error('Production portal verification requires indexing mode and its expected payload SHA-256.');
|
||||
const formAEnabled = forms === 'live';
|
||||
@@ -115,6 +120,7 @@ export async function verifyArtifact(root, mode = 'staging', forms = 'live', por
|
||||
let secretSignals = [];
|
||||
let formAIframes = 0;
|
||||
let deferredFormAIframes = 0;
|
||||
let counterInstances = 0;
|
||||
|
||||
for (const file of htmlFiles) {
|
||||
const html = await readText(file);
|
||||
@@ -127,7 +133,20 @@ export async function verifyArtifact(root, mode = 'staging', forms = 'live', por
|
||||
const trailingSlashUrls = rootRelativeUrls(html).filter(hasTerminalSlash);
|
||||
const expectedFormAUrl = formAEnabled ? staticFormAUrls.get(route) : undefined;
|
||||
const expectedDeferredFormAUrl = formAEnabled ? deferredFormAUrls.get(route) : undefined;
|
||||
const signals = unsafeSignals(html, formAEnabled);
|
||||
const signals = unsafeSignals(html, formAEnabled, counters === 'live');
|
||||
const countersOnPage = [...html.matchAll(/<div\b[^>]*\bdata-counter\b[^>]*>/gi)].map((match) => match[0]);
|
||||
for (const counter of countersOnPage) {
|
||||
if (counters === 'live') {
|
||||
if (!counter.includes('data-counter-mode="live"')
|
||||
|| !counter.includes('data-api-base="https://api.azinstitute4autism.com"')) {
|
||||
errors.push(`${route}: live counter lacks the approved mode or API origin`);
|
||||
}
|
||||
} else if (counter.includes('data-counter-mode="live"')
|
||||
|| counter.includes('data-api-base="https://api.azinstitute4autism.com"')) {
|
||||
errors.push(`${route}: live counter appears in a ${counters} artifact`);
|
||||
}
|
||||
}
|
||||
counterInstances += countersOnPage.length;
|
||||
const jotformFrames = iframeSources(html).filter((src) => {
|
||||
try {
|
||||
return new URL(src).origin === 'https://form.jotform.com';
|
||||
@@ -212,6 +231,7 @@ export async function verifyArtifact(root, mode = 'staging', forms = 'live', por
|
||||
if (reviewIndex) errors.push('indexing-enabled artifact contains staging-only /lp review index');
|
||||
}
|
||||
if (activeForms) errors.push(`found ${activeForms} non-search native form action(s)`);
|
||||
if (counters === 'live' && counterInstances === 0) errors.push('live artifact contains no article counters');
|
||||
if (secretSignals.length) errors.push(`unsafe artifact signals: ${JSON.stringify(secretSignals)}`);
|
||||
|
||||
const files = (await fg('**/*', { cwd: root, absolute: true, onlyFiles: true })).sort();
|
||||
@@ -241,6 +261,7 @@ export async function verifyArtifact(root, mode = 'staging', forms = 'live', por
|
||||
return {
|
||||
mode,
|
||||
forms,
|
||||
counters,
|
||||
portal: portal.mode,
|
||||
htmlRoutes: htmlFiles.length,
|
||||
sitemapUrls: sitemapRoutes.length,
|
||||
@@ -264,7 +285,8 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(new URL(im
|
||||
const forms = process.argv.find((arg) => arg.startsWith('--forms='))?.slice('--forms='.length) ?? 'live';
|
||||
const portalMode = process.argv.find((arg) => arg.startsWith('--portal='))?.slice('--portal='.length) ?? 'none';
|
||||
const portalSha256 = process.argv.find((arg) => arg.startsWith('--portal-sha256='))?.slice('--portal-sha256='.length);
|
||||
const result = await verifyArtifact(path.resolve(root), mode, forms, { mode: portalMode, sha256: portalSha256 });
|
||||
const counters = process.argv.find((arg) => arg.startsWith('--counters='))?.slice('--counters='.length) ?? 'off';
|
||||
const result = await verifyArtifact(path.resolve(root), mode, forms, { mode: portalMode, sha256: portalSha256 }, counters);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
if (!result.passed) process.exitCode = 1;
|
||||
}
|
||||
Reference in new issue
Block a user