Skip to content

fix(policy): CHECK 15 safe-shape allowlist with fail-closed traversal - #378

Merged
brenpike merged 6 commits into
mainfrom
bugfix/check15-safe-shape-allowlist
Sep 25, 2026
Merged

brenpike merged 6 commits into
mainfrom
bugfix/check15-safe-shape-allowlist

Conversation

@brenpike

Copy link
Copy Markdown
Owner

Summary

Rewrites tools/policy_check.sh CHECK 15, the tracker-reference guard over plugin/**/*.md and plugin/workflows/*.json. The old check deleted known-harmless patterns and flagged what remained. The new check allows only listed safe forms, and it fails closed when it cannot find or read a file.

Closes #371.

Detection

  • CHECK15_CLASSIFY_AWK, a single POSIX awk walker, is now the only place classification happens.
  • Every # followed by a digit run is a candidate, with no condition on the next character. It becomes a finding unless one of exactly four safe shapes consumes it whole:
    • an inline-code span, closed by a backtick run of the same length (any length)
    • a single-segment owner/repo#N citation at a word boundary
    • a ](#...) in-page anchor
    • a hex colour: 3, 4, 6 or 8 hex characters, at least one letter, bounded on both sides
  • Now findings: issue#123, PR#456, _#123_, __#123__, #123_, #123g.
  • No longer false positives: inline code wrapped in two or three backticks.

Traversal

  • Discovery selects by name only; -type f is removed.
  • find -print0 results are read into an array. The stream ends with a sentinel record carrying find's exit status, so a failed or truncated discovery becomes a finding.
  • Reads are gated on -f and -r, and the awk exit status is checked.
  • A missing path, a non-regular path (directory, dangling symlink, symlink to a directory) or an unreadable path is a finding. A symlink to a regular file is scanned.

Canaries

  • Detection canary: two-direction cases for every rule.
  • Scanner canary: adds a new fixture, tracker-ref-allowlist-canary.md, and a committed symlink fixture, tracker-ref-symlink-canary.md.
  • New CHECK 15 TRAVERSAL CANARY: covers discovery failure, the directory gate, a missing path, awk status, the pinned find-args arrays, and the symlink probe with a -type f negative control.
  • tests/policy/safety-tracker-ref-guard.json pins the new banner, and its description now matches the shipped behaviour.

Validation

  • bash tools/validate.sh --changed: a tools/** edit escalates this to the full suite, which passed at 0d173d3.
  • bash tools/validate.sh --self-test: all pass.
  • tools/policy_check.sh --strict: Checks passed 72/73 (the one failure predates this PR), 29 findings all allowlisted, 0 new.
  • Current plugin/ content: 0 new findings.
  • The CHECK 15 slice passes under gawk, mawk, nawk and busybox awk.
  • Mutation proofs: each rule's canary fails when that rule is broken.

Local review

  • Pass 1: Codex found two cases where the input set was narrowed silently, ahead of the fail-closed layer: the candidate right-boundary test, and -type f in discovery. Both behaviours existed on main before this PR, but this PR's comments claimed nothing slipped through. Fixed structurally in 8bc8ecf by making the candidate unconditional and discovery name-only.
  • Pass 2: one high finding. Symlinked directories are not traversed, because discovery does not pass -L. Declined as a recorded residual in CHECK 15's header comment. This behaviour is inherited and applies to every discovery site in the script, and plugin/ has no symlinks today. The script-wide fix is tracked in policy_check.sh discovery silently skips symlinked directories and non-regular name matches #377.

Versioning

No bump. The changes are confined to tools/, tests/ and CHANGELOG.md ([Unreleased]), none of which ship in the plugin.

Known residuals (stated in the CHECK 15 header)

  • The allowlist works per line.
  • A word-glued reference reports its # plus digits prefix.
  • The hex-colour shape exempts a bounded hex run that contains a letter (for example #12ab).
  • A code span that spans two physical lines is not recognised.
  • Symlinked directories are not traversed (policy_check.sh discovery silently skips symlinked directories and non-regular name matches #377).
  • The symlink fixture needs core.symlinks=true. A checkout without symlink support fails loudly rather than passing silently.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d173d3dcd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/policy_check.sh
Step over CommonMark backslash escapes before any CHECK 15 safe shape is tried, so an escaped backtick or ] no longer opens a span that exempts the tracker reference behind it. An escaped # stays a candidate. Adds two-direction detection canaries for the escape rule.
@brenpike
brenpike merged commit 5bd3a47 into main Sep 25, 2026
1 check passed
@brenpike
brenpike deleted the bugfix/check15-safe-shape-allowlist branch September 25, 2026 10:39
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.

CHECK 15: replace reject-enumeration scanner with safe-shape allowlist and checked reads

1 participant