Skip to content

fix(test-policy): the workflow scan must read code, not comments - #191

Merged
andrei-hasna merged 1 commit into
mainfrom
fix/workflow-scan-reads-code-not-comments
Aug 3, 2026
Merged

fix(test-policy): the workflow scan must read code, not comments#191
andrei-hasna merged 1 commit into
mainfrom
fix/workflow-scan-reads-code-not-comments

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

The workflow scan reads prose as code, and it is wrong in BOTH directions

PR #189 landed the fallback — rewording a comment so the scan stops tripping on it. That unblocked CI and left the underlying defect in place. This lands the root-cause fix.

src/test-timeout-policy.ts read each workflow as raw lines, so a comment was judged as a command. Measured on current main:

workflow old line-scan sees reality
release.yml bun test --timeout 120000 same
ci.yml a comment bun run test — the real command, never seen
ci.yml bun test --timeout 60000 … same

The false positive is the one that broke CI. release.yml's explanatory comment has no --timeout, because it is prose.

The false negative is the one nobody had noticed, and it is worse. ci.yml's comment names both bun run test and bun test. The scan matched it, hit the bun run test early-continue, and waved it through as a compliant invocation — so it padded the positive control that exists to prove the scan finds real commands, while the actual run: bun run test command stayed invisible to it. A control inflated by a comment is not a control.

The fix

stripShellComments() and findBunTestInvocations(): parse with Bun.YAML.parse (native, typed in bun-types 1.3.14 — no new dependency), walk to run: values only, strip shell comments inside run: | blocks quote-aware, and split commands on &&, ||, ;, | and newlines so a later --timeout cannot excuse an earlier bare invocation. A command containing both forms is now judged on the bare one, which removes the early-exit that caused the false negative.

Two-sided evidence, on the real scan rather than fixtures alone

  • Rejects a genuine violation: mutating the real run: bun test --timeout 120000 to run: bun test fails with release.yml must use 'bun run test' or pass --timeout explicitly — got: bun test. Note the message now quotes a command; before the fix it quoted a comment.
  • Accepts the compliant workflow with its comment intact: 14 pass / 0 fail.
  • 8 fixture tests added covering both directions.

One fixture corrected by measurement rather than assumption: an under-blocking case (echo "a # b" && bun test) initially failed, because in a plain YAML scalar YAML itself terminates the value at # — so no invocation exists to hide, and GitHub would run the truncated echo "a too. Quote-awareness is load-bearing only inside block scalars. The fixture and the comment now say so.

Gates, run separately

  • bun run typecheck (tsc --noEmit) — rc=0
  • bun run testrc=0, 1135 pass / 56 skip / 0 fail across 74 files

Stated plainly

No CI evidence yet. ci.yml triggers on pull_request and push-to-main only, so the branch push ran nothing — opening this PR is what produces the first CI signal.

Agent: Vespasian


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…policy

The `no workflow invokes bare 'bun test'` policy scanned each workflow as raw
lines, so a comment mentioning `bun test` was read as an invocation. That is
wrong in both directions, and both were live in this repository at once:

  FALSE POSITIVE - release.yml's comment "`bun test` does not invoke tsc in
  this repository" has no --timeout because it is prose. The scan failed CI on
  both runners while the real command two lines below was the compliant
  `bun test --timeout 120000`.

  FALSE NEGATIVE - ci.yml's comment "`bun run test`, not bare `bun test`"
  matched, then took the `bun run test` early-exit and was waved through, so a
  comment counted as a compliant invocation and padded the positive control
  meant to prove the scan found real commands.

The scan now parses the workflow and inspects only the executable content of
`run:` steps, and strips shell comments inside those steps too, since a `#`
line in a `run: |` block is prose as well. Stripping is quote-aware because
the under-blocking direction is the dangerous one: a cut at the first `#`
would drop the real invocation in `echo "a # b" && bun test`.

A command carrying both forms is now judged on the bare one rather than
excused by the other, which removes the exact mechanism behind the false
negative.

Measured while writing the fixtures: in a PLAIN scalar YAML itself ends the
value at ` #`, so that shape carries no invocation for anything to hide -
GitHub Actions would run `echo "a` and stop. Quote-awareness is load-bearing
inside block scalars, and the fixture says so.

Two-sided evidence on the real scan, not just on fixtures: with release.yml's
command mutated to a bare `bun test` the policy test fails and names the
command, and with the workflow unmutated - comment intact - it passes.
Typecheck rc=0; suite 1135 pass, 56 skip, 0 fail, rc=0.

Agent: Vespasian
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] GO — #191 @ 1ba38c4 — lens: correctness+security+gates, reviewer unresolved-account002 (1 of 1)

What I read:

  • git log --oneline origin/main..HEAD and git diff origin/main...HEAD --stat against fetched origin/main 52159e6.
  • The full diff and complete surrounding source for src/test-timeout-policy.ts and src/test-timeout-policy.test.ts.
  • package.json, tsconfig.json, and every workflow input exercised by the new scan, including ci.yml, release.yml, and shared-database-transfer.yml.
  • A focused execution of findBunTestInvocations over the real workflow files: exit 0; it found and correctly classified bun run test, the scoped 60000ms Postgres invocation, and the 120000ms release invocation; it found none in the transfer workflow.

Commands and measured results, all invoked unpiped:

  • bun install — exit 0. Setup only, not reported as the test gate; it installed 152 packages and its prepare hook built successfully.
  • bun run typecheck — exit 0. tsc --noEmit emitted no diagnostics; pass/fail counts are not produced by this gate.
  • bun run test — exit 0. 1135 pass, 0 fail, 56 skip; 1191 tests across 74 files, 11 snapshots, 8944 assertions.
  • git diff --check origin/main...HEAD — exit 0.
  • Final git status --short — exit 0 with no output (clean worktree).

Blocking P0/P1 findings: none. The change removes the demonstrated false-positive and false-negative paths caused by scanning YAML comments as executable commands, parses only YAML run: values, keeps shell-comment stripping quote-aware, and retains positive and negative controls that exercise the real violation.

Non-blocking follow-ups: none.

@andrei-hasna
andrei-hasna merged commit c780b77 into main Aug 3, 2026
5 checks passed
@andrei-hasna
andrei-hasna deleted the fix/workflow-scan-reads-code-not-comments branch August 3, 2026 12:56
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.

1 participant