Skip to content

Canonical code-evidence paths bypass repository-relative path validation #541

Description

@sylvesterkaczmarek

Summary

Canonical finding locations are required to use safe repository-relative POSIX paths, but codeEvidence[].path is only constrained to a nonempty string.

A sealed finding can therefore claim source-code evidence at an absolute, traversal, Windows-style, or colon-bearing path even though the same path would be rejected in locations[].path.

Reproduction / evidence

Current upstream main at 37bf87a692fc72d41f7312cc48808d699d204fba has two different validation paths:

  • contract.ts explicitly runs every locations[].path through safeRelativePath();
  • findings.schema.json defines codeEvidence[].path as only { "type": "string", "minLength": 1 };
  • the Python finalizer's _validate_finding() validates each ordinary location with _validate_location(), but for code evidence it only requires an id and nonempty code before later schema validation.

A deterministic sealed-contract reproduction is:

  1. copy the bundled completed-scan example;
  2. add otherwise valid codeEvidence to a finding with path: "../../outside.ts";
  3. update the sealed findings.json digest in scan-manifest.json;
  4. call loadContract().

On current main, the traversal path satisfies the findings schema and no canonical code-evidence path check rejects it.

Expected behavior: canonical codeEvidence[].path should obey the same repository-relative safety boundary as source locations and artifact paths.

Root cause

The path-safety rule was implemented for locations in code, while the optional code-evidence path was added to the schema without an equivalent path constraint. Because the shared schema is consumed by both the TypeScript loader and Python finalizer, the omission exists on both validation surfaces.

Suggested fix

Add the canonical safe-path pattern to codeEvidence[].path, rejecting:

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

Add a focused sealed-contract regression that reseals the fixture after inserting ../../outside.ts and requires loadContract() to reject it.

Impact

This does not provide arbitrary file reads by itself because codeEvidence.code is already embedded in the finding. It is an integrity issue: a sealed Codex Security finding can currently attribute canonical source evidence to a path outside the repository/path model that the rest of the contract enforces, and downstream report/publication adapters can present that misleading path as evidence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions