/** * Read-only Form A iframe sizing acceptance probe. * * This intentionally requires a separate affirmative environment value * because it permits GET/HEAD requests to form.jotform.com. It never fills, * focuses, clicks, or submits an iframe control. All other external origins, * non-read methods, downloads, and WebSockets are blocked and recorded. * * Build the ordinary live artifact first, then run: * FORM_A_IFRAME_REAL_LOAD_APPROVED=true \ * FORM_A_IFRAME_JOTFORM_ASSETS_APPROVED=true \ * PLAYWRIGHT_MODULE=/path/to/playwright/index.mjs \ * CHROMIUM_EXECUTABLE=/path/to/chromium \ * node tools/jotform-form-a-real-iframe-sizing.mjs */ import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import http from 'node:http'; import net from 'node:net'; import path from 'node:path'; import { spawn } from 'node:child_process'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { setTimeout as delay } from 'node:timers/promises'; if (process.env.FORM_A_IFRAME_REAL_LOAD_APPROVED !== 'true') { throw new Error('Set FORM_A_IFRAME_REAL_LOAD_APPROVED=true to permit read-only requests to form.jotform.com.'); } const root = fileURLToPath(new URL('../', import.meta.url)); const playwrightModule = process.env.PLAYWRIGHT_MODULE ? pathToFileURL(path.resolve(process.env.PLAYWRIGHT_MODULE)).href : 'playwright'; const jotformOrigin = 'https://form.jotform.com'; const jotformAssetOrigin = 'https://cdn.jotfor.ms'; const allowJotformAssets = process.env.FORM_A_IFRAME_JOTFORM_ASSETS_APPROVED === 'true'; const allowedOrigins = new Set([jotformOrigin, ...(allowJotformAssets ? [jotformAssetOrigin] : [])]); const routes = [ { route: '/contact', context: 'contact', locale: 'en', file: 'contact' }, { route: '/es/contact', context: 'contact', locale: 'es', file: 'es-contact' }, { route: '/lp/aba-therapy', context: 'landing', locale: 'en', file: 'aba-therapy' }, { route: '/lp/aba-insurance', context: 'landing', locale: 'en', file: 'aba-insurance' }, { route: '/lp/aba-ahcccs', context: 'landing', locale: 'en', file: 'aba-ahcccs' }, { route: '/lp/aba-private-pay', context: 'landing', locale: 'en', file: 'aba-private-pay' }, { route: '/lp/early-intervention', context: 'landing', locale: 'en', file: 'early-intervention' }, ]; const widths = [320, 390, 768, 1440]; const output = await fs.mkdtemp('/tmp/aia-form-a-real-iframe-sizing-'); const evidence = { routes: routes.map(({ route, context, locale }) => ({ route, context, locale })), widths, allowedOrigins: [...allowedOrigins], allowedJotformRequests: [], blockedExternalRequests: [], forbiddenAttempts: [], screenshots: [], status: 'failed', }; const forbiddenHost = /(?:api\.azinstitute4autism\.com|google-analytics|googletagmanager|googleadservices|doubleclick)/i; const reserve = net.createServer(); await new Promise((resolve, reject) => { reserve.once('error', reject); reserve.listen(0, '127.0.0.1', resolve); }); const port = reserve.address().port; await new Promise((resolve) => reserve.close(resolve)); const origin = `http://127.0.0.1:${port}`; let browser; let preview; try { const { chromium } = await import(playwrightModule); browser = await chromium.launch(process.env.CHROMIUM_EXECUTABLE ? { headless: true, executablePath: process.env.CHROMIUM_EXECUTABLE } : { headless: true }); preview = spawn(process.execPath, ['node_modules/astro/bin/astro.mjs', 'preview', '--host', '127.0.0.1', '--port', String(port)], { cwd: root, env: { ...process.env, ASTRO_TELEMETRY_DISABLED: '1' }, stdio: 'ignore', }); const reachable = () => new Promise((resolve) => { const request = http.get(origin + '/', (response) => { response.resume(); resolve(response.statusCode === 200); }); request.setTimeout(500, () => request.destroy()); request.on('error', () => resolve(false)); }); for (let attempts = 0; attempts < 60 && !(await reachable()); attempts++) await delay(250); assert.equal(await reachable(), true, 'Loopback preview did not become reachable'); for (const width of widths) { const context = await browser.newContext({ viewport: { width, height: width < 641 ? 844 : 960 }, serviceWorkers: 'block', acceptDownloads: false, }); await context.routeWebSocket('**/*', (socket) => socket.close()); await context.route('**/*', async (route) => { const request = route.request(); const url = new URL(request.url()); const item = { origin: url.origin, path: url.pathname, method: request.method() }; if (url.origin === origin && ['GET', 'HEAD'].includes(request.method())) return route.continue(); if (allowedOrigins.has(url.origin) && ['GET', 'HEAD'].includes(request.method())) { evidence.allowedJotformRequests.push(item); return route.continue(); } evidence.blockedExternalRequests.push(item); if (forbiddenHost.test(url.hostname)) evidence.forbiddenAttempts.push(item); return route.abort(); }); const page = await context.newPage(); for (const expected of routes) { const response = await page.goto(origin + expected.route, { waitUntil: 'domcontentloaded', timeout: 15000 }); assert.equal(response?.status(), 200, expected.route); const iframe = page.locator('[data-jotform-form-a] iframe'); const expectedUrl = `${jotformOrigin}/262946078314059?formContext=${expected.context}&language=${expected.locale}`; assert.equal(await iframe.count(), 1, expected.route); assert.equal(await iframe.getAttribute('src'), expectedUrl, expected.route); assert.equal(await iframe.getAttribute('sandbox'), 'allow-forms allow-scripts allow-same-origin', expected.route); assert.equal(await iframe.getAttribute('referrerpolicy'), 'no-referrer', expected.route); const box = await iframe.boundingBox(); const expectedHeight = expected.context === 'contact' ? width < 641 ? 1088 : 960 : width < 641 ? 800 : 672; assert.ok(box && box.width > 0 && box.height >= expectedHeight, `${expected.route}: context-specific frame height is insufficient`); await page.waitForTimeout(1200); const screenshot = path.join(output, `${expected.file}-${width}.png`); await page.screenshot({ path: screenshot, fullPage: true }); evidence.screenshots.push(screenshot); } await context.close(); } assert.ok(evidence.allowedJotformRequests.length >= routes.length, 'Expected read-only Jotform iframe requests.'); assert.equal(evidence.forbiddenAttempts.length, 0, `Forbidden external requests: ${JSON.stringify(evidence.forbiddenAttempts)}`); assert.equal(evidence.blockedExternalRequests.filter((item) => !['GET', 'HEAD'].includes(item.method)).length, 0, 'A non-read external request was attempted.'); evidence.status = 'passed'; } catch (error) { evidence.error = error instanceof Error ? error.message : String(error); throw error; } finally { await fs.writeFile(path.join(output, 'evidence.json'), JSON.stringify(evidence, null, 2)); if (preview?.exitCode === null) { preview.kill('SIGTERM'); await Promise.race([new Promise((resolve) => preview.once('exit', resolve)), delay(3000)]); if (preview.exitCode === null) preview.kill('SIGKILL'); } await browser?.close(); console.log(JSON.stringify({ ...evidence, screenshots: evidence.screenshots.map((file) => path.basename(file)), artifactDirectory: output, }, null, 2)); }