fix(scanner): reject spoofed builtin trust decorators (supersedes #26)#27
Conversation
Close a HIGH-severity false-green. The decorator taint provider trusted any FQN whose prefix was a builtin marker module and whose final segment was a known marker name, without verifying it resolved to Wardline's real package. A scanned project shipping its own `wardline/decorators` (or `loom_markers`) no-op could spoof `@trusted`/`@trust_boundary`, launder untrusted data up to a trusted tier, and suppress real taint->sink findings. - Builtin markers now match ONLY exact exports (`P.<name>`, `P.trust.<name>`); nested-path spoofs are rejected. Custom grammar markers keep prefix+name. - Fail closed for builtin markers under any marker root the scanned project shadows; shadow roots are derived dynamically from BUILTIN_BOUNDARY_TYPES (`wardline` AND `loom_markers`), not hardcoded. - Thread a per-root shadow-aware provider fingerprint through the parse stage into both dirty-detection and the resolver summary cache, so a TRUSTED summary computed under a non-shadowed root cannot be reused under a shadowed one (cross-root cache poisoning). - Document the Clarion fact-key residual: the content-hash freshness gate is a cross-tool contract Clarion independently recomputes, so a Wardline-private shadow bit cannot be folded in without a Clarion read-path change. Opt-in (`--clarion-url`) path, not the scan gate. Reimplements and generalizes the fix proposed in #26 (which closed only the `wardline` root and now conflicts with main). Full suite green (2390 passed), ruff + mypy clean, self-scan unchanged (0 new defects). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 792d351c04
ℹ️ 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".
| project_modules = frozenset( | ||
| module | ||
| for path in stage_input.files | ||
| if ( | ||
| module := module_dotted_name( | ||
| path.relative_to(root).as_posix() if path.is_relative_to(root) else path.as_posix() | ||
| ) | ||
| ) | ||
| is not None | ||
| ) |
There was a problem hiding this comment.
Detect shadow packages outside the scanned file set
When a repository-local wardline/ or loom_markers/ package is present but excluded from discovery (for example exclude: ["wardline/**"], or otherwise omitted from files), project_modules is built only from the remaining scanned files, so shadowed_roots stays empty and from wardline.decorators import trusted in an analyzed file is still anchored as the real builtin marker. That reopens the spoofed-decorator false-green this change is trying to close for any untrusted repo that can hide the shadow package via config; the shadow check needs to inspect importable roots under the project, not just the files that survived discovery.
Useful? React with 👍 / 👎.
| return fqn in { | ||
| f"{module_prefix}.{canonical_name}", | ||
| f"{module_prefix}.trust.{canonical_name}", | ||
| } |
There was a problem hiding this comment.
Reject nonexistent loom_markers trust submodule
For the loom_markers builtin, this accepts loom_markers.trust.<name> as a trusted implementation-module export, but the packaged runtime marker module in this repo contains only packages/loom-markers/src/loom_markers/__init__.py and no trust.py. A target can therefore write from loom_markers.trust import trusted; with the real package that import is not a valid marker source, but the analyzer will still anchor the function as TRUSTED and can suppress taint-to-sink findings. Keep the extra .trust export only for wardline.decorators, or add the real submodule before trusting it.
Useful? React with 👍 / 👎.
What
Closes a HIGH-severity false-green in the default analyzer: the decorator taint provider trusted any FQN under a builtin marker prefix whose final segment matched a known marker name, without verifying it resolved to Wardline's real package. A scanned project shipping its own
wardline/decorators(orloom_markers) no-op could spoof@trusted/@trust_boundary, launder untrusted data up to a trusted tier, and suppress real taint→sink findings. Reproduced live onmainfor both roots.Why this and not #26
#26 (codex) had the right direction but under-fixed: it hardcoded the
wardlineroot and leftloom_markers— an equally-builtin, more-easily-shadowed top-level marker root — fully spoofable, and it now conflicts withmain. This reimplements cleanly and generalizes to all builtin marker roots.Changes
P.<name>,P.trust.<name>); nested-path spoofs rejected. Custom grammar markers keep the documented prefix+name behaviour (a project defining its own custom marker package is the intended extension).BUILTIN_BOUNDARY_TYPES(wardlineandloom_markers).--clarion-urlpath, not the gate).Verification
loom_markersandwardlineshadows both defeated (leak fires); a real@trust_boundaryfrom the installed package still suppresses.src/resolves a top-levelwardline, which correctly fails closed, but wardline's source uses no real markers, so no output change).🤖 Generated with Claude Code