Skip to content

fix(contract): constrain deferred coverage paths - #558

Open
sylvesterkaczmarek wants to merge 2 commits into
openai:mainfrom
sylvesterkaczmarek:fix/deferred-coverage-safe-paths
Open

fix(contract): constrain deferred coverage paths#558
sylvesterkaczmarek wants to merge 2 commits into
openai:mainfrom
sylvesterkaczmarek:fix/deferred-coverage-safe-paths

Conversation

@sylvesterkaczmarek

Copy link
Copy Markdown

Summary

Require canonical coverage.deferred[].paths values to remain safe repository-relative POSIX paths.

Fixes #557.

Reproduction / evidence

Current upstream main at 37bf87a692fc72d41f7312cc48808d699d204fba documents deferred coverage units with optional source paths, but the shared coverage schema constrains each path only to a non-empty string.

A sealed partial-coverage bundle can therefore contain a deferred path such as ../../outside.ts, /etc/passwd, C:/outside.ts, or src\\outside.ts and still pass loadContract() after the coverage artifact is correctly resealed.

This differs from manifest scope paths, finding locations, code-evidence paths and artifact/receipt paths, which already enforce the repository-relative path model.

Root cause

coverage.schema.json added the optional deferred paths field without the safe-path constraint used by the other canonical source-path fields. _validate_coverage() does not independently normalize those paths before shared schema validation.

Fix

Apply the canonical repository-relative safe-path pattern to every deferred path, rejecting:

  • absolute paths;
  • .. traversal segments;
  • backslashes / Windows-style paths;
  • colon-bearing paths;
  • standalone .;
  • NUL characters.

Because the same schema is consumed by finalization and the TypeScript contract loader, the invariant is enforced at both producer and consumer boundaries.

Tests / validation

Added deferred-coverage-paths.test.ts. It copies the real bundled completed-scan example, switches it to partial coverage, inserts a deferred path, reseals the coverage.json digest in the manifest, and calls the real loadContract().

The regression covers traversal, absolute, Windows-style, dot, colon and NUL-bearing paths, plus a valid src/extract.py control.

The branch is based directly on current upstream main at 37bf87a692fc72d41f7312cc48808d699d204fba and is not behind it. Production diff: 2 additions, 1 deletion.

Full repository tests cannot be run in this execution environment because the repository cannot be cloned here. Pushed-head CI remains the authoritative full-suite validation.

Risk

Low. The change only narrows a documented canonical path field to the repository-relative format already required by the rest of the contract.

@github-actions github-actions Bot added the bug Something isn't working label Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deferred coverage paths bypass canonical repository-relative validation

1 participant