fix(gates): no-admin-idor — recognize a guard one hop inside a resolved helper - #518
Merged
Merged
Conversation
added 3 commits
August 20, 2026 11:07
…er body, without weakening the must-exist invariant
…the scanner; restore the anti-dead-test's discriminating power
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was actually wrong
The premise this started from — "the gate's regex doesn't recognize
->canAccess(, causing 82% false positives" — turned out to be false for the production checker, and I'd rather record that than quietly ship against it._GUARD_HELPER_NAME_REhas recognizedcanAccessby name since #360. Verified empirically: the realcheck_no_admin_idor.pyover all 86 shillinq controllers reports 0 findings. The 86/105 false-positive rate came from a naive audit script quoted inside shillinq's own change design doc, not from this gate.The residual gap that is real
A same-class helper whose name carries no auth token — shillinq/decidesk's own idiom (
resolveScope(),mayAccessReturn(),guardDraftAccess()) — but whose body reaches$this->context->canAccess(...)one hop out. Neither the name regex nor the old body regexes (throw / 401 / 403 / 404 /authorize*/require*/ensure*) matched that shape.The fix, and the hole avoided while making it
Added
_GUARD_VERB_CALL_RE(is|has|can|may+Admin|Access|Permission|Permitted|Owner|Allowed|Authori[sz]ed, reusing the vetted token set) into_HELPER_GUARD_BODY_REand_STRICT_GUARD_BODY_RE— deliberately not into_GUARD_BODY_RE.A first draft did add it there and broke
test_shape_a_without_the_helper_is_still_reported:_GUARD_BODY_REis applied to the routed method with no existence check, so it would have cleared a call to a nonexistentcanAccess-shaped method by name alone, deleting Pattern 1's must-exist invariant for the whole verb family. The two helper-body regexes are only consulted for a method already confirmed to exist, so the same widening is safe there.The no-op-stub case stays caught (shillinq shipped exactly one:
DBAController::ensureAdministrationAccess()matchedensure*while never denying anything). Proven, not asserted: newtest_POSITIVE_CONTROL_helper_with_no_auth_token_name_and_no_guard_body_is_flagged— same call site, stub body, still 1 finding.Fleet survey
hrmq / decidesk / pipelinq / procest / openregister use
hasAccess,mayAccess,isAllowed,isAuthorized*,hasPermission,hasAccessToOrganisation— all already in the vocabulary.canManageOrganisationMembers/canDeletecarry no object token and are a documented deliberate non-match, flagged as follow-up rather than widened into.Controls
auth-guardsbundle extended (not a parallel scheme) —plantedfails 3/3 incl. the newarchive(),cleanpasses 3/3.Handback to hydra:
hydra-gate-no-admin-idor/SKILL.mdshould note the one-hop body recognition and why it is deliberately absent from the top-level regex.🤖 Generated with Claude Code