C1-JO-F1 — fail closed on unreadable push force - #54
Conversation
A repair.push request whose own `force` operand is present but unreadable — an own getter or Proxy trap that throws, a getter that returns `undefined`, an own data property of `undefined`, or a presence check that throws — normalized to `force: false` and received ALLOW_ONCE, because the shared guarded reader reports both absence and a throwing read as `undefined` and the value-only force parser treated `undefined` as non-forced. Absence and present-but- unreadable collapsed into the same non-forced default, contradicting the documented invariant that only an absent or literally `false` force is non-forced. Replace the value-only parser with a presence-aware, fail-closed force reader local to job-operation.ts: an absent own `force` stays non-forced, a present own `force` is read at most once and is non-forced only when it reads as literally `false`, and any unreadable presence check or read fails closed to forced. `Object.hasOwn` is captured at module load. The shared `readOwnProperty` contract is unchanged; every other operand already fails closed. C1-JO-F1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe job operation reader now distinguishes an absent ChangesPush force authorization
Estimated code review effort: 3 (Moderate) | ~25 minutes Merge Risk: ⚪ Minimal · up to The change makes unreadable force values fail closed while preserving existing non-forced behavior, with focused validation reported as passing; no actionable merge-blocking risk remains. Sequence Diagram(s)sequenceDiagram
participant Request
participant readJobOperation
participant readForceFlag
participant Authorization
Request->>readJobOperation: provide request record
readJobOperation->>readForceFlag: pass request record
readForceFlag-->>readJobOperation: return normalized force state
readJobOperation->>Authorization: provide normalized operation
Authorization-->>Request: allow unforced or deny forced push
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@codex review |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Finding
5accbcab9818faf9630d47b02c092ef551f90458Verified defect
A
repair.pushrequest whose ownforceoperand is present but unreadable collapses toforce: falseand receivesALLOW_ONCE, instead of failing closed. The shared guarded readerreadOwnPropertyreports both an absent property and a throwing read asundefined, and the value-only force parser treatedundefinedas non-forced — so ABSENT and PRESENT-BUT-UNREADABLE collapsed into the same non-forced default, contradicting the documented invariant that only an absent or literallyfalseforce is non-forced.Family sweep
forceis the sole violating security-relevant scalar. Every sibling operand (operation,requestId,jobId,repositoryId,parentPullRequestId,parentHeadSha,worktreeId,path,commandClass,ref,sourceRef,targetRef) already fails closed to a null/unknown → DENY state. The sharedreadOwnPropertycontract is left unchanged.Required semantics (all verified after fix)
false→ preserve non-forced behavior (ALLOW_ONCE)true→ DENY / FORCE_PUSH_FORBIDDENundefined(own data or getter) → DENYget/getOwnPropertyDescriptorread failure → DENYfalsevalues (0,'',null,'false', object) → DENYThe
forceoperand is read at most once perreadJobOperation(getter invoked ≤1×), the snapshot stays total and never throws, and a force-only read failure does not turn the whole request unreadable. No denied case issues a permit.Change scope
Two files only:
src/domain/job-operation.ts— presence-aware, fail-closedreadForceFlag(module-private);Object.hasOwncaptured at module load; single call-site update.tests/domain/job-authorization-invariants.test.ts— focused C1-JO-F1 regression cases.Validation
git diff --check0.Excluded
Summary by CodeRabbit
forcesetting and a provided value.forcesetting is unreadable, unavailable, or contains any value other thanfalse.