You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
packages/loopover-miner/lib/repo-clone.ts exports a shared REPO_SEGMENT_PATTERN / isValidRepoSegment(segment: unknown): boolean (lines 72-79) specifically so that "every
owner/repo parser in this package shares this one definition instead of duplicating it or skipping
it entirely." isValidRepoSegment rejects path-traversal segments (., ..) and anything
outside [A-Za-z0-9._-]+. A prior gap-audit round already fixed seven such parsers that skipped
this validator; a fresh audit of packages/loopover-miner/lib/** found eight more call sites,
across seven files, that still hand-roll their own owner/repo shape check (only "non-empty
and exactly one /") instead of importing and calling isValidRepoSegment:
packages/loopover-miner/lib/portfolio-discovery.ts, normalizeRankedIssue (around line 43):
packages/loopover-miner/lib/rejection-signal.ts, parseRepoFullName (around line 68):
same shape, feeding fetchPolicyDoc's raw.githubusercontent.com URL and fetchPullRequestPayload's api.github.com URL (both built with encodeURIComponent, which does not escape ., so a value like owner="a", repo=".." produces a path-altering URL segment).
packages/loopover-miner/lib/ci-poller.ts, parseRepoFullName (around line 99).
packages/loopover-miner/lib/pr-disposition-poller.ts, parseRepoFullName (around line 101) —
byte-for-byte the same function as feat(scoring): add situational score projections #3, independently duplicated.
packages/loopover-miner/lib/manage-poll.ts, the inline check inside exported recordManagePollSnapshot (around line 195) — notably, parseRepoArg two functions above it in
the SAME file already calls isValidRepoSegment correctly, so this is an in-file inconsistency,
not just a cross-file one.
packages/loopover-miner/lib/chat-miner-ops-actions.ts, isQueueTargetParams (around line 36): typeof repoFullName === "string" && repoFullName.includes("/") — accepts "a/b/c", "/a/b",
or a value containing .. as a segment.
packages/loopover-miner/lib/chat-miner-ops-actions.ts, isDenyHookDecisionParams (around line
42) — same weak .includes("/") check.
packages/loopover-miner/lib/contribution-profile-extract.ts, its own module-local parseRepoFullName (around line 80) — feeds three GitHub API URLs built by raw string
interpolation (see the companion issue on this same file's URL construction).
All eight are independently reachable: several are exported functions with no guarantee their
current caller pre-validates (e.g. rejection-signal.ts's three exported functions take repoFullName as a plain string), and the two chat-miner-ops-actions.ts validators gate real MCP
chat-action dispatch, where malformed input from a live chat surface is exactly the kind of input isValidRepoSegment exists to catch before it reaches a URL or SQL LIKE pattern built from it.
Requirements
In each of the 8 call sites listed above, import isValidRepoSegment from ./repo-clone.js (relative import path already used elsewhere in the package) and use it to
validate the owner and repo segments, in addition to (not instead of) each function's existing
non-empty / single-slash / type checks.
Preserve each function's existing return/throw contract exactly (e.g. ci-poller.ts and pr-disposition-poller.ts's parseRepoFullName throw Error("invalid_repo_full_name"); portfolio-discovery.ts, rejection-signal.ts, manage-poll.ts, and contribution-profile-extract.ts return null or throw as they already do — only add the
validation, do not change the success/failure signaling shape).
For chat-miner-ops-actions.ts's two validators, replace the .includes("/") check with a real
two-segment split + isValidRepoSegment check on both segments (matching the shape check every
other parser in this issue uses), not just an additional isValidRepoSegment call bolted onto the
existing loose check.
Do not touch manage-poll.ts's existing parseRepoArg function — it already calls isValidRepoSegment correctly and is not in scope.
Deliverables
All 8 call sites listed in Context validate both owner and repo with isValidRepoSegment before treating the parsed value as valid.
A new regression test for each of the 8 call sites, asserting that a path-traversal or
otherwise-invalid segment (e.g. "acme/..", "../etc", or a segment containing a control
character) is now rejected — added to the existing test file that already covers that
function (e.g. test/unit/miner-portfolio-discovery.test.ts, test/unit/miner-rejection-signal.test.ts, test/unit/miner-ci-poller.test.ts, test/unit/miner-pr-disposition-poller.test.ts, the manage-poll test file, the
chat-miner-ops-actions / MCP-governor-gating test file, and the
contribution-profile-extract test file). Do not add a single combined test file for all 8 —
each fix's regression test lives next to the existing tests for that function.
All 8 fixes and all 8 regression tests are required in this one PR — this is a mechanical,
repetitive fix applied consistently across every site; there is no reason to split it, and a PR
that fixes only some of the 8 sites does not resolve this issue.
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ (branch-counted) on packages/loopover-miner/lib/**. Every
new validation branch you add (the new isValidRepoSegment check succeeding and failing) must be
exercised by both a passing-value test and a rejecting-value test. Tests must be added under this
repo's actual test root, test/unit/** (this package's tests live in the shared root test/
directory, not a packages/loopover-miner/test/** directory — confirm the existing sibling test
files' location before adding new ones, and mirror it exactly).
Expected Outcome
Every owner/repo parser under packages/loopover-miner/lib/** — not just the majority that
already do — rejects a path-traversal or otherwise-invalid repo segment before it reaches a URL,
SQL LIKE pattern, or downstream call. No parser in the package is a silent outlier anymore.
Links & Resources
packages/loopover-miner/lib/repo-clone.ts — the canonical isValidRepoSegment / REPO_SEGMENT_PATTERN to reuse.
packages/loopover-miner/lib/manage-poll.ts's own parseRepoArg — the correct in-file sibling
pattern to mirror for that file's fix.
Context
packages/loopover-miner/lib/repo-clone.tsexports a sharedREPO_SEGMENT_PATTERN/isValidRepoSegment(segment: unknown): boolean(lines 72-79) specifically so that "everyowner/repo parser in this package shares this one definition instead of duplicating it or skipping
it entirely."
isValidRepoSegmentrejects path-traversal segments (.,..) and anythingoutside
[A-Za-z0-9._-]+. A prior gap-audit round already fixed seven such parsers that skippedthis validator; a fresh audit of
packages/loopover-miner/lib/**found eight more call sites,across seven files, that still hand-roll their own
owner/reposhape check (only "non-emptyand exactly one
/") instead of importing and callingisValidRepoSegment:packages/loopover-miner/lib/portfolio-discovery.ts,normalizeRankedIssue(around line 43):packages/loopover-miner/lib/rejection-signal.ts,parseRepoFullName(around line 68):same shape, feeding
fetchPolicyDoc's raw.githubusercontent.com URL andfetchPullRequestPayload's api.github.com URL (both built withencodeURIComponent, which doesnot escape
., so a value likeowner="a", repo=".."produces a path-altering URL segment).packages/loopover-miner/lib/ci-poller.ts,parseRepoFullName(around line 99).packages/loopover-miner/lib/pr-disposition-poller.ts,parseRepoFullName(around line 101) —byte-for-byte the same function as feat(scoring): add situational score projections #3, independently duplicated.
packages/loopover-miner/lib/manage-poll.ts, the inline check inside exportedrecordManagePollSnapshot(around line 195) — notably,parseRepoArgtwo functions above it inthe SAME file already calls
isValidRepoSegmentcorrectly, so this is an in-file inconsistency,not just a cross-file one.
packages/loopover-miner/lib/chat-miner-ops-actions.ts,isQueueTargetParams(around line 36):typeof repoFullName === "string" && repoFullName.includes("/")— accepts"a/b/c","/a/b",or a value containing
..as a segment.packages/loopover-miner/lib/chat-miner-ops-actions.ts,isDenyHookDecisionParams(around line42) — same weak
.includes("/")check.packages/loopover-miner/lib/contribution-profile-extract.ts, its own module-localparseRepoFullName(around line 80) — feeds three GitHub API URLs built by raw stringinterpolation (see the companion issue on this same file's URL construction).
All eight are independently reachable: several are exported functions with no guarantee their
current caller pre-validates (e.g.
rejection-signal.ts's three exported functions takerepoFullNameas a plain string), and the twochat-miner-ops-actions.tsvalidators gate real MCPchat-action dispatch, where malformed input from a live chat surface is exactly the kind of input
isValidRepoSegmentexists to catch before it reaches a URL or SQL LIKE pattern built from it.Requirements
isValidRepoSegmentfrom./repo-clone.js(relative import path already used elsewhere in the package) and use it tovalidate the
ownerandreposegments, in addition to (not instead of) each function's existingnon-empty / single-slash / type checks.
ci-poller.tsandpr-disposition-poller.ts'sparseRepoFullNamethrowError("invalid_repo_full_name");portfolio-discovery.ts,rejection-signal.ts,manage-poll.ts, andcontribution-profile-extract.tsreturnnullor throw as they already do — only add thevalidation, do not change the success/failure signaling shape).
chat-miner-ops-actions.ts's two validators, replace the.includes("/")check with a realtwo-segment split +
isValidRepoSegmentcheck on both segments (matching the shape check everyother parser in this issue uses), not just an additional
isValidRepoSegmentcall bolted onto theexisting loose check.
manage-poll.ts's existingparseRepoArgfunction — it already callsisValidRepoSegmentcorrectly and is not in scope.Deliverables
ownerandrepowithisValidRepoSegmentbefore treating the parsed value as valid.otherwise-invalid segment (e.g.
"acme/..","../etc", or a segment containing a controlcharacter) is now rejected — added to the existing test file that already covers that
function (e.g.
test/unit/miner-portfolio-discovery.test.ts,test/unit/miner-rejection-signal.test.ts,test/unit/miner-ci-poller.test.ts,test/unit/miner-pr-disposition-poller.test.ts, the manage-poll test file, thechat-miner-ops-actions / MCP-governor-gating test file, and the
contribution-profile-extract test file). Do not add a single combined test file for all 8 —
each fix's regression test lives next to the existing tests for that function.
All 8 fixes and all 8 regression tests are required in this one PR — this is a mechanical,
repetitive fix applied consistently across every site; there is no reason to split it, and a PR
that fixes only some of the 8 sites does not resolve this issue.
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ (branch-counted) on
packages/loopover-miner/lib/**. Everynew validation branch you add (the new
isValidRepoSegmentcheck succeeding and failing) must beexercised by both a passing-value test and a rejecting-value test. Tests must be added under this
repo's actual test root,
test/unit/**(this package's tests live in the shared roottest/directory, not a
packages/loopover-miner/test/**directory — confirm the existing sibling testfiles' location before adding new ones, and mirror it exactly).
Expected Outcome
Every
owner/repoparser underpackages/loopover-miner/lib/**— not just the majority thatalready do — rejects a path-traversal or otherwise-invalid repo segment before it reaches a URL,
SQL
LIKEpattern, or downstream call. No parser in the package is a silent outlier anymore.Links & Resources
packages/loopover-miner/lib/repo-clone.ts— the canonicalisValidRepoSegment/REPO_SEGMENT_PATTERNto reuse.packages/loopover-miner/lib/manage-poll.ts's ownparseRepoArg— the correct in-file siblingpattern to mirror for that file's fix.