Skip to content

Harden validation, sanitization, CLI input handling, and CI#5

Draft
slavazeph-coder wants to merge 2 commits into
mainfrom
claude/code-hardening-a70wv7
Draft

Harden validation, sanitization, CLI input handling, and CI#5
slavazeph-coder wants to merge 2 commits into
mainfrom
claude/code-hardening-a70wv7

Conversation

@slavazeph-coder

Copy link
Copy Markdown
Contributor

Summary

Hardens the capture → AppSpec → export pipeline against malformed specs, hostile page content, and bad CLI input, and tightens the CI supply chain.

AppSpec validation (@reconstruct/appspec)

  • Validates every collection (screens, components, flows, acceptance tests, assumptions, unknowns) instead of only screens; a spec like flows: [{}] previously crashed the exporter with a TypeError, now it fails validation with a precise message.
  • Enforces unique screen ids, finite 0–1 confidence values, non-empty required strings, a parseable generatedAt, and an http(s) app.sourceUrl (rejects javascript: and malformed URLs).

Content sanitization (prompt-injection surface)

The exported documents are instructions for coding agents, so captured page content must not be able to inject structure into them:

  • New text.js module strips C0/C1 control characters, zero-width characters, and bidirectional override characters (trojan-source style) from everything that flows into the structured DOM evidence and the AppSpec. Raw HTML evidence stays verbatim.
  • All spec fields interpolated into exported markdown are forced to a single line, so a page title containing \n# Ignore previous instructions cannot create a heading in PRODUCT.md.
  • The DESIGN_SYSTEM.md code fence is sized past the longest embedded backtick run, so ``` inside spec values cannot close the fence.

CLI robustness

  • --width/--height are validated bounded integers (320–7680) instead of silently becoming NaN deep inside Playwright; new --timeout option for navigation.
  • --target uses commander choices(); unreadable or malformed AppSpec files report a clear error naming the file, with exit code 1.
  • Evidence file slugs are capped at 80 chars to avoid filesystem-limit failures on long routes.
  • Exporter refuses to write outside the output directory and uses dirname when creating parents.
  • CLI depends on @reconstruct/appspec via workspace:* instead of ../../appspec/src relative imports; all CLI modules are syntax-checked by build/typecheck, not just the entry point.
  • Root package declares "type": "module" (removes the run.js loader warning) and Node >=20 engines.

CI

  • Committed pnpm-lock.yaml and switched to pnpm install --frozen-lockfile (was --frozen-lockfile=false with no lockfile).
  • Least-privilege permissions: contents: read, job timeout, cancellation of superseded PR runs, and a pnpm typecheck step.

Testing

  • 22 tests pass (pnpm test): 14 appspec validation tests, 8 CLI tests including markdown-injection regression cases.
  • Verified end-to-end: captured a local page with a hostile title (embedded backticks, newline heading injection, U+202E bidi override, zero-width chars), then validated and exported it. The generated PRODUCT.md keeps hostile content on a single line and invisible characters are stripped from the AppSpec and DOM evidence.
  • Probed error paths at the CLI: non-numeric/oversized viewport values, unsupported target, ftp:// URL, malformed JSON, tampered spec, missing file — all produce clear errors and exit 1.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BX5FUWAvGrKvt2zStdVNxh


Generated by Claude Code

claude added 2 commits July 2, 2026 14:11
- Validate all AppSpec collections (screens, components, flows,
  acceptance tests, assumptions, unknowns), enforce unique screen ids,
  finite confidence values, a valid generatedAt date, and an http(s)
  sourceUrl so malformed specs fail at validation instead of crashing
  exporters.
- Strip control, zero-width, and bidirectional override characters from
  captured page content, keep spec fields single-line in exported
  markdown, and size code fences past embedded backtick runs so a
  captured page cannot inject structure into generated agent documents.
- Bound and validate --width/--height, add a configurable --timeout,
  restrict --target with commander choices, and report unreadable or
  malformed AppSpec files with clear errors and a non-zero exit code.
- Cap evidence file name slugs, guard exporter writes to stay inside
  the output directory, and use dirname when creating parent folders.
- Depend on @reconstruct/appspec through the workspace instead of
  relative source paths, syntax-check every CLI module, and declare
  supported Node engines; set the root package type to module to
  silence the run.js loader warning.
- Commit the pnpm lockfile and pin CI to --frozen-lockfile with
  least-privilege permissions, a typecheck step, a job timeout, and
  cancelled superseded PR runs.
- Add tests for the stricter validation, the sanitizers, and the
  exporter, including markdown-injection regression cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BX5FUWAvGrKvt2zStdVNxh
- Add an SVG banner and a real capture screenshot produced by the tool
  itself (dogfooded against a demo page).
- Add three Mermaid diagrams: the capture/validate/export pipeline, the
  AppSpec schema anatomy, and the workspace dependency graph.
- Document every command and option, the provenance status/confidence
  model, export targets, architecture rules, and the hardening measures.
- Add CI, Node, license, and AppSpec-version badges and a contents index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BX5FUWAvGrKvt2zStdVNxh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants