fix(source-control): local PR-body gates check all four contract sections (0.55.11) - #3217
Conversation
|
Claude finished @kyle-sexton's task in 56s —— View job Security review complete ✅
Skip-gate check: PR is open, and the diff does have a narrow security-adjacent surface (a Scope: All changes are in Findings: None. This PR only tightens an existing local validation gate — it generalizes
No security concerns to report on this PR. |
|
Last security-reviewed head: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3b33908049
ℹ️ 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".
|
Claude finished @kyle-sexton's task in 3m 14s —— View job Code review complete
No blocking findings. This is a clean, well-scoped fix. What I checked
Not independently verifiedI could not execute shell scripts in this sandbox (test-run and syntax-check commands were denied), so the test-suite pass counts in the PR description ( |
|
Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
|
Claude finished @cursor[bot]'s task in 3m 33s —— View job Code review complete
No blocking findings. This lane last reviewed commit
Test coverage for the fix is direct: Two minor, non-blocking observations (not filing as inline comments — neither rises to block-worthy):
No security-relevant surface changed since the prior security-lane pass (still a local-gate tightening only, no new command execution/eval/network/credential handling). |
bfc1ce0 to
1189755
Compare
1189755 to
505b2e9
Compare
|
Claude finished @kyle-sexton's task in 7m 40s —— View job Security review complete
Skip-gate check: This head ( Finding: 1 IMPORTANT (posted inline)Superlinear inline-code-span scan in The new inline-backtick-span scanner rescans the entire rest of the line from each opening backtick run's position looking for a same-length closing run, with no bound on the search distance. A single crafted line built from strictly-increasing, all-distinct backtick-run lengths never finds a match for any opening run, so cost grows as O(L^1.5) rather than O(L) like every other per-line scan in this file. Within GitHub's ~65536-char PR-body ceiling, that's plausibly enough bash-level work to exceed the 15s I could not execute a live timing PoC in this lane's restricted sandbox (only Everything else in the diff (the |
505b2e9 to
2efc6d0
Compare
|
Claude finished @kyle-sexton's task in 1m 38s —— View job Security review complete
Skip-gate check: The prior security-lane pass (23:30:41Z) reviewed head Status of the outstanding findingThe previously-filed IMPORTANT finding is still open in the current code and has not been addressed by this branch: Superlinear inline-code-span scan in For each opening backtick run, the inner I'm not re-filing this as a new inline comment since the code is byte-identical to what was already reviewed and commented on inline at 23:38:24Z — flagging its unresolved status here so it isn't lost before merge. No other findings. No new command execution, |
…ions (0.55.6) The shared validator only required a closing keyword and a non-empty pr-issue-linkage reusable rejects. Observed on #3205: linkage plus Summary, Verification, and Related (no Fix) passed both gates and failed CI. The validator now looks up Summary, Fix, Verification, and Related through one heading-level helper, reports every missing or empty section in one pass, and the blocked-message remedy lists all four. Closes #3206 Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
The shared validator treated a required heading inside a fenced sample, indented block, or inline span as the real section, so a body CI rejects still passed both local pre-checks. Mask those constructs the way the pinned pr-issue-linkage reusable does before the heading and keyword scan. Closes #3206 Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
2efc6d0 to
de49d2e
Compare
|
Claude finished @kyle-sexton's task in 1m 45s —— View job Security review complete
Skip-gate check: This head ( Status of the outstanding findingThe previously-filed IMPORTANT finding is still open and unchanged in the current code: Superlinear inline-code-span scan in For each opening backtick run, the inner Not re-filing as a new inline comment since the code is byte-identical to what's already commented on inline — flagging its unresolved status here so it isn't lost before merge. No other findings. No new command execution, |
A per-opener rescan of the rest of the line was O(L^1.5) on unmatched distinct backtick-run lengths and could exceed the 15s PreToolUse timeout, which fails this gate open. Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Pull Request is not mergeable
|
Claude finished @kyle-sexton's task in 7m 38s —— View job Security review complete
Skip-gate check: The last full security-lane pass reviewed head Finding: 1 IMPORTANT (posted inline)The collect-then-pair rewrite mis-masks nested, differing-length backtick spans, leaking their gap text into the unanchored The new algorithm collects every backtick run on a line, then pairs each with the next unused same-length run and records Concretely: Impact is the same class as the timing finding this exact commit fixed: it defeats this local pre-check rather than achieving a full bypass, since real I could not execute a live PoC in this lane's restricted sandbox (Bash execution and file writes were denied) — this is traced by hand-simulating the algorithm step-by-step for the construction above, not confirmed by execution. Everything elseUnchanged from what prior passes already vetted: the |
Collect-then-pair recorded spans in closer-first order, so a nested differing-length run (`` `x` ``) orphaned the outer span and leaked a decoy closes #N into has_linkage(). Sort by start and keep outermost. Drop leftover unused locals that failed shellcheck. Closes #3206 Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Claude finished @kyle-sexton's task in 6m 4s —— View job Security review complete
Skip-gate check: The prior full security-lane pass reviewed head Finding: 1 IMPORTANT (posted inline)The sort-and-keep-outermost fix resolves the previously-reported matched-inner-pair case, but the underlying collect-then-pair loop still leaves stale Concrete construction (traced by hand step-by-step through the pairing loop): The outer This isn't a contrived edge case — the escaped-backtick idiom is common in exactly the kind of PR that discusses backtick-masking logic (like this one), so a body with real Full trace and a suggested fix direction (clear any Everything elseUnchanged from what prior passes already vetted: the |
comment-hygiene flags tracker-ref:closing-keyword in shell comments. The nested-span notes named a decoy closes-form in a comment; reword them so the gate still documents the leak without tripping the scan. Closes #3206 Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Claude finished @kyle-sexton's task in 2m 26s —— View job Security review complete
Skip-gate check: The last full security-lane pass reviewed head Status of the outstanding findingStill open and unaddressed. I hand-traced the exact construction from the prior finding against the current (unchanged) code at Walking the pairing loop: the outer This still isn't a contrived case: I could not execute a live PoC in this lane's sandbox (Bash was denied for scripted verification of the trace above) — this is hand-traced, not confirmed by execution. Not re-filing a new inline comment since the flagged code is byte-identical, at the same line numbers, to what's already commented on inline in this thread — restating its status here so it isn't lost before merge. Suggested direction (unchanged from the prior review round): when closing a span, also drop any Everything elseUnchanged from what prior passes already vetted: the |
A per-length pending map left an inner escaped-tick run live after its outer span closed, so the next same-length opener on the line paired across the span and the real later span leaked into has_linkage(). Walk openers in order, consume every run between a match, and add the escaped-tick fixture. Closes #3206 Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Claude finished @kyle-sexton's task in 4m 29s —— View job Security review complete
Skip-gate check: The last full security-lane pass reviewed head Finding: previously-reported IMPORTANT issue is resolved
I also re-verified the complexity property the original P2/IMPORTANT timing finding was about, since this is a different algorithm than the one that fixed it last round: the search-forward loop's cost is bounded by O(nruns) for any span that resolves (disjoint, non-overlapping intervals across the whole line, since absorbed runs are skipped in O(1) on their outer-loop visit), and by O(nruns²) only for runs that never find a match at all — which requires all-distinct run lengths, and distinct lengths 1..n cost ~n²/2 characters to produce, capping nruns at O(√L). Both cases bound total work to O(L), matching every other per-line scan in this file. This isn't just asserted — I worked through the disjoint-interval argument by hand for several adversarial shapes (all-distinct lengths, mirrored long-distance pairs, nested widely-spaced fillers) and none produce worse than O(L). Both new test fixtures directly targeting this — I could not execute the test suite or a live PoC in this lane's sandbox (Bash script execution required approval that wasn't granted) — this finding-resolution assessment is based on hand-tracing the algorithm and reasoning about its complexity bound, not execution. Given two prior rounds each found a real bug in a plausible-looking fix, I'd treat "tests pass" here as author-claimed until someone runs the suite, but the algorithm itself is now a textbook-correct implementation rather than an ad hoc patch, which is a meaningfully different (and more trustworthy) shape of fix than the previous two iterations. Everything elseUnchanged from what prior passes already vetted: the No new findings to file inline. |
Closes #3206
Summary
The shared PR-body linkage validator only checked a closing keyword and
## Related, so both local pre-checks allowed bodies the pinnedpr-issue-linkagereusable rejects. Local gates now require the same four contract sections CI does.Fix
pr-linkage-validator.shgeneralizesrelated_section()intosection_content()overSummary,Fix,Verification, andRelated, using the existing heading-level semantics (a nested###is content). Each missing or empty section is its ownLINKAGE_PROBLEMSentry. Both hook surfaces pick this up from the shared core; their blocked-message remedies now list all four sections so following them produces a body CI accepts. The no-issue-marker path andupdate_pull_requestwithoutbodyare unchanged.Verification
bash plugins/source-control/hooks/pr-body-linkage-gate.test.sh— passed: 137 failed: 0bash plugins/source-control/hooks/pr-linkage-mcp-gate.test.sh— pass=25 fail=0New cases: the #3205 body (linkage + Summary + Verification + Related, no Fix) is blocked; a nested
###inside each required section is content.check-changelog-parity.sh --check-bump origin/mainpasses (0.55.6).Related
pr-issue-linkagefailure is the reproduction.github/workflows/pr-issue-linkage.yml— pins the upstream reusable at v0.14.2