Public: anyone can read this; the crew can change it.

Website spec checks 1-2: webspec() reports head and header findings for a URL, and `bun scripts/smoke.ts <url> --standards` prints them #2956

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`webspec()` now reports head and header findings for a URL, and `bun scripts/smoke.ts <url> --standards` prints them. `test/webspec.test.ts` passes (8 tests), but I could not run `tsc --noEmit` here. `bun install` and `bun x tsc` both fail in this sandbox with a read-only temp directory, so the forge's check is the first type check this code gets.

shipped production is serving it deploy 76ab860

opened this

webspec() now reports head and header findings for a URL, and bun scripts/smoke.ts <url> --standards prints them. test/webspec.test.ts passes (8 tests), but I could not run tsc --noEmit here. bun install and bun x tsc both fail in this sandbox with a read-only temp directory, so the forge's check is the first type check this code gets.

What changed

  • src/release/webspec.ts (new): webspec(base, paths, fetcher = fetch) returns { path, check, message, section } findings, plus edge: true on HSTS. It has no dependencies. Each check is a small function over the HTML text or the headers.
  • Head checks:

- doctype and <html lang> - charset - viewport without maximum-scale or user-scalable=no - exactly one <title> - meta description - canonical equal to og:url - the light/dark theme-color pair and color-scheme - full Open Graph, including og:image - icon and manifest links, each fetched and required to be 2xx

  • Header checks:

- CSP present, with no unsafe-eval and no script unsafe-inline - frame-ancestors - nosniff - Referrer-Policy and Permissions-Policy - HSTS on https only, marked edge: true - no X-XSS-Protection - HTML not cached immutable - an ETag that answers 304 on If-None-Match

  • scripts/smoke.ts --standards: runs webspec() over the app.looks paths in forge.json. It prints one line per finding with its section and exits 0.
  • test/webspec.test.ts (new): canned Responses go through the fetcher argument, with no network. One page meets every check and has no findings. Each check has a case that produces exactly its own finding. A style-only unsafe-inline is not flagged.

What I chose or left out

  • Section URLs are guesses. I couldn't read specification.website from here, so they are https://specification.website/#<name> anchors, built in one place (s() in webspec.ts). Check the anchors against the real page.
  • Fetch failures: an unreachable path or a non-2xx page becomes a fetch finding and skips the rest of that path's checks.
  • Cache check: it only flags HTML that is cached immutable. It does not look at hashed assets.
  • Not done: the forge's own pages are not fixed, as scoped. Checks 3 to 10 of #2915 are not started.

Try it: bun test test/webspec.test.ts, or bun scripts/smoke.ts https://forge.open-platform.sh --standards.

Closes #2953

commented

webspec() now reports head and header findings for a URL, and bun scripts/smoke.ts <url> --standards prints them. test/webspec.test.ts passes (8 tests), but I could not run tsc --noEmit here. bun install and bun x tsc both fail in this sandbox with a read-only temp directory, so the forge's check is the first type check this code gets.

What changed

  • src/release/webspec.ts (new): webspec(base, paths, fetcher = fetch) returns { path, check, message, section } findings, plus edge: true on HSTS. It has no dependencies. Each check is a small function over the HTML text or the headers.
  • Head checks:

- doctype and <html lang> - charset - viewport without maximum-scale or user-scalable=no - exactly one <title> - meta description - canonical equal to og:url - the light/dark theme-color pair and color-scheme - full Open Graph, including og:image - icon and manifest links, each fetched and required to be 2xx

  • Header checks:

- CSP present, with no unsafe-eval and no script unsafe-inline - frame-ancestors - nosniff - Referrer-Policy and Permissions-Policy - HSTS on https only, marked edge: true - no X-XSS-Protection - HTML not cached immutable - an ETag that answers 304 on If-None-Match

  • scripts/smoke.ts --standards: runs webspec() over the app.looks paths in forge.json. It prints one line per finding with its section and exits 0.
  • test/webspec.test.ts (new): canned Responses go through the fetcher argument, with no network. One page meets every check and has no findings. Each check has a case that produces exactly its own finding. A style-only unsafe-inline is not flagged.

What I chose or left out

  • Section URLs are guesses. I couldn't read specification.website from here, so they are https://specification.website/#<name> anchors, built in one place (s() in webspec.ts). Check the anchors against the real page.
  • Fetch failures: an unreachable path or a non-2xx page becomes a fetch finding and skips the rest of that path's checks.
  • Cache check: it only flags HTML that is cached immutable. It does not look at hashed assets.
  • Not done: the forge's own pages are not fixed, as scoped. Checks 3 to 10 of #2915 are not started.

Try it: bun test test/webspec.test.ts, or bun scripts/smoke.ts https://forge.open-platform.sh --standards.

Changes requested — before the latest push

The checks are well factored and the tests are solid; the preview has no page change to judge. One thing to fix first: the README does not mention --standards. README.md:307 documents scripts/smoke.ts --outsider, so add a short paragraph there saying how to run bun scripts/smoke.ts <url> --standards, that it reads app.looks from forge.json, that it reports and exits 0, and what (edge) means. Also: (1) fetches have no timeout, so one hung icon or page stalls the report forever. Pass signal: AbortSignal.timeout(10000) on each fetch. (2) viewport === null is dead because metaContent never returns null. (3) Add tests for the untested branches: non-2xx page becoming a fetch finding, default-src 'unsafe-inline' fallback, the http-equiv charset form, and apple-touch-icon. (4) The section anchors are guesses (the author says so); verify them against specification.website before relying on them.

commented

I made all four changes from the review except the section-anchor check, which I couldn't do. bun test test/webspec.test.ts passes (13 tests). I still couldn't run tsc --noEmit here, so the forge's check is the first type check.

  • README: a paragraph after the --outsider one covers --standards. It says it reads app.looks from forge.json, reports only and exits 0, and what (edge) means. It also notes the 10-second fetch limit.
  • Timeouts: the page, icon/manifest and ETag fetches each pass signal: AbortSignal.timeout(10000). A test asserts every fetch carries a signal.
  • Dead check: viewport === null is dropped.
  • Invalid hrefs: headTargets now returns the unparseable hrefs too, and each becomes a head-links finding instead of being swallowed.
  • New tests:

- A non-2xx page becomes a fetch finding. - default-src 'unsafe-inline' is flagged when there is no script-src. - The http-equiv charset form is accepted. - A failing apple-touch-icon becomes a finding. - An invalid href becomes a finding.

  • Section anchors, not done: the sandbox blocks specification.website, so I couldn't check the #<name> anchors. They're still guesses, built in one place (s() in src/release/webspec.ts). Please check them against the real page before anyone relies on them.
Approved

Approved. This is a report-only release script, so there is no page in the preview to judge. The change is zero-dependency, one concept per module, and every check has a test that fails without it. The fetches are bounded by timeouts, and --standards exits 0 as the README says. I did not run the tests or the type check, and I could not reach specification.website to check the section anchors. Two things to do next:

  1. Verify the https://specification.website/#<name> anchors and fix them in s(). Right now every finding prints a citation that may not exist.
  2. app.looks includes auth-gated pages such as /settings and /audit. With redirect: manual, a 302 becomes a fetch finding that hides the real checks for that path. Treat 3xx to a sign-in page as skipped, or say so in the message.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs