fix(tools/git): pr-review.sh _belongs case-insensitive repo-slug compare #877
Reference in New Issue
Block a user
Delete Branch "fix/875-belongs-case-insensitive-slug"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Bug (#875, post-#866 follow-up)
#866 hardened
pr-review.sh's comment-persistence read-back into a full-path_belongs()check that pins scheme+host+port ORIGIN and the FULL path, toblock look-alike-host and same-host decoy-prefix attacks. That hardening left
a case-sensitivity bug:
_origin_and_path(url)lowercases scheme+host but returnsparsed.path.rstrip("/")WITHOUT lowercasing the path.expected_pr_pathembeds the repo slug fromEXPECTED_REPO_SLUG(
${GITEA_API_BASE##*/repos/}), which can be MIXED-CASE (e.g.USC/uconnect).pull_request_urlslug to LOWERCASE.So
_belongscompared the path CASE-SENSITIVELY, and a successful commentPOST (201) FALSE-NEGATIVED on a mixed-case-slug repo ("did not land on a pull
request"). This fails CLOSED on a real success (safe — no false-success) but
breaks legitimate mixed-case-slug repos.
Fix
Lowercase both
pathandexpected_pathinside the_belongscomparisononly. The
origin == base_origintuple check (scheme+host+port pin from#866) is untouched,
_origin_and_pathstill lowercases scheme+host asbefore, and the path match is still a FULL-path comparison (no
endswith/suffix relaxation) — so the look-alike-host and same-host
decoy-prefix protections from #866 are preserved. Repo owner/repo slugs are
case-insensitive and the rest of the comment path (
/pulls/<number>) isnumeric, so lowercasing this specific comparison is safe.
Diff is scoped to the one
_belongsfunction; no other pr-review.sh logicchanged.
Tests (red-first)
Added a new regression case in
test-pr-review-gitea-comment.sh:comment-mixed-case-slug— a MIXED-CASEEXPECTED_REPO_SLUG(
USC/uconnect) against a Gitea-canonicalized lowercasepull_request_url(.../usc/uconnect/pulls/123).Error: ... claimed PR comment did not land on a pull request (kind=pulls) on this provider/repo).The existing #866 security regression cases (look-alike host, same-host
decoy prefix, wrong owner, wrong repo, wrong kind/issue_url) all still
REJECT — confirmed unaffected by this change.
Gates
shellcheckon both changed files: no new findings (pre-existing SC1091info on
sourceis present identically onorigin/main).pnpm --filter @mosaicstack/mosaic run test:framework-shell: all suitespass, including
pr-review.sh REST review + comment create/read-back regression passed.eslint/prettiercould not be run in this checkout (node_modules/toolbinaries not installed,
pnpm installfails with an environment-levelEACCESon the shared pnpm store) — pre-existing environment condition,unrelated to this diff, and out of scope since only shell files changed
(ESLint here targets
srcTypeScript only).pnpm typecheck && pnpm lint && pnpm format:check) was notblocked on push;
packages/mosaic/framework/tools/orchestrator/README.mdis already clean on this branch (identical to
origin/main, via #872), sono action was needed there.
Closes #875
Gate-16 APPROVE -- stamped by Mos (id-11)
Registered under the distinct Mos bot (id-11): PR author jason.woltje(id2) != approver Mos(id11). Commit-author = mosaic-coder (the (c) durable builder identity, != reviewer) -> Finding-0 does NOT apply (clean). Registers the independent reviewer's APPROVE; MS-LEAD prepared/relayed, Mos stamps+merges.
VERDICT: APPROVE @
9d8d58ae75(Closes #875 -- the post-#866 follow-up).Independent review, all 6 PASS: (1) fix = pr-review.sh _belongs() line 214 path==expected_path -> path.lower()==expected_path.lower() (resolves mixed-case-slug false-negative); (2) SECURITY PRESERVED -- origin(scheme+host+port) pin unchanged, FULL-path equality unchanged, wrong-kind reject unchanged; whole-path lowering safe (namespaces case-insensitive-unique, numeric /pulls/, no case-only decoy constructible; #866 decoy tests still reject); (3) red-first reproduced (revert line -> exact false-negative, restore -> pass); (4) scope 2 files +29/-1; (5) firewall PASS w/ tracked observation (reuses the PRE-EXISTING pervasive USC/uconnect fixture, ~30+ instances since PR#538, verify-sanitized.sh explicitly defers third-party-host refs as a separate tracked follow-up -- not new, not a blocker); (6) state open/mergeable/base main, CI terminal-green wp1982 @head.
6-CHECK re-verified by Mos @9d8d58ae75dee95ac4e7596d8beae733bc563c9a: open/not-merged/mergeable/base main; CI success @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Closes #875'; queue-guard clear. Firewall follow-up (third-party-host fixture) swept into the existing tracked item, not a #877 blocker.