Skip to content

fix(engine): validate the repo and PR number behind buildResultsPayload's customer link - #9632

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
kai392:fix/9611-results-payload-validation
Jul 29, 2026
Merged

fix(engine): validate the repo and PR number behind buildResultsPayload's customer link#9632
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
kai392:fix/9611-results-payload-validation

Conversation

@kai392

@kai392 kai392 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Closes #9611

What

buildResultsPayload (packages/loopover-engine/src/results-payload.ts) builds the customer-facing prLink and summary by raw interpolation, validating neither value — even though result.title right beside it is scrubbed for the documented "public-safe" contract.

  • repoFullName"acme/widgets/../../evil" yielded https://github.com/acme/widgets/../../evil/pull/1, which browsers resolve to github.com/evil. It's now valid only when it splits into exactly two path-safe segments (the isValidRepoSegment guard governor-ledger.ts keeps locally for the same reason); otherwise prLink is null and the summary renders the literal unknown repository instead of the raw value.
  • prNumber — only a non-null check, so 0/-3/2.5 produced .../pull/0 etc. hasPr now additionally requires Number.isInteger(prNumber) && prNumber > 0; anything else takes the no-PR branch.
  • additions/deletions — normalized with the Number.isFinite(v) ? Math.max(0, Math.floor(v)) : 0 rule the sibling Rent-a-Loop modules use, so a negative/fractional count can't reach totals or the diff preview.

redactSecrets(title), MAX_DIFF_PREVIEW_FILES, the diff slice, the status default, and diffPreview ordering are unchanged; the function still never throws.

Tests

Every changed branch is covered in the engine's own node:test suite (packages/loopover-engine/test/results-payload.test.ts — the suite the engine Codecov flag grades this source with) at 100% on the changed code, plus the DoD's required regression cases in the root test/unit/results-payload.test.ts. Verified: engine suite 6/6, root suite 12/12 (existing cases unchanged), non-vacuous (4/6 fail with the fix reverted), tsc --noEmit clean, git diff --check clean.

@kai392
kai392 requested a review from JSONbored as a code owner July 29, 2026 01:59
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-29 02:22:23 UTC

3 files · 1 AI reviewer · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

Review summary
This closes #9611 by validating repoFullName as exactly two path-safe segments and requiring a positive integer prNumber before building the customer-facing prLink, and normalizes additions/deletions to non-negative integers. The logic is correctly wired end-to-end (validRepo gates both prLink and repoDisplay, hasPr additionally checks Number.isInteger and > 0), the traversal case ("acme/widgets/../../evil" splits into 4 segments, fails validRepo) and edge cases (0, -3, 2.5, NaN) are genuinely exercised by new tests in both the engine's own suite and the root suite. The github.com hardcoded host flagged by the brief predates this diff (it's the existing URL template, unchanged in scope) and is not a regression introduced here.

Nits — 4 non-blocking
  • The regex REPO_SEGMENT_PATTERN allows a segment of all dots like "..." (three or more dots) since only exact "." and ".." are excluded — likely harmless for GitHub repo names but worth a comment noting it's intentional.
  • results-payload.ts:78 still hardcodes `https:​//github.com/` — pre-existing, but now that this function validates its inputs more strictly it might be worth centralizing the URL template alongside `isValidRepoSegment` for future GHE/self-hosted support.
  • Consider extracting `isValidRepoSegment`/`REPO_SEGMENT_PATTERN` into a tiny shared internal util if a third module ends up needing this guard, to avoid a third copy diverging from `governor-ledger.ts`.
  • The double `.test.ts` suites (engine + root) both cover nearly identical cases — fine for now given the DoD requirement, but flag if this duplication grows unwieldy across future fields.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9611
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 100 registered-repo PR(s), 49 merged, 7 issue(s).
Contributor context ✅ Confirmed Gittensor contributor kai392; Gittensor profile; 100 PR(s), 7 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The diff adds repo-segment validation (exactly two path-safe segments, rejecting traversal), tightens hasPr to require a positive integer prNumber, normalizes additions/deletions to non-negative integers, and renders 'unknown repository' when the repo is invalid, matching all stated requirements and Deliverables, with corresponding regression tests added.

Review context
  • Author: kai392
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is registered but has no active allocation in the current snapshot.
  • Public profile languages: Python, Cuda, JavaScript, Kotlin, Perl, Ruby, TypeScript, Vue
  • Official Gittensor activity: 100 PR(s), 7 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Await review-lane availability.
  • Then work through the remaining 1 step in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@superagent-security

superagent-security Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.13%. Comparing base (a402b9f) to head (be1e5f7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9632      +/-   ##
==========================================
+ Coverage   90.12%   90.13%   +0.01%     
==========================================
  Files         891      891              
  Lines      112306   112329      +23     
  Branches    26629    26635       +6     
==========================================
+ Hits       101216   101248      +32     
+ Misses       9760     9747      -13     
- Partials     1330     1334       +4     
Flag Coverage Δ
backend 95.51% <90.90%> (-0.01%) ⬇️
engine 67.24% <100.00%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/loopover-engine/src/results-payload.ts 100.00% <100.00%> (+18.57%) ⬆️

... and 1 file with indirect coverage changes

…ad's customer link (JSONbored#9611)

buildResultsPayload composes the customer-facing prLink and summary by raw interpolation of
repoFullName and prNumber, neither validated:

- repoFullName like "acme/widgets/../../evil" produced a link the browser resolves to
  github.com/evil. It's now treated as valid only when it splits into exactly two path-safe
  segments (the same isValidRepoSegment guard governor-ledger.ts keeps locally); otherwise
  prLink is null and the summary renders "unknown repository" instead of the raw value.
- prNumber only had a non-null check, so 0/-3/2.5 took the has-PR branch and rendered
  ".../pull/0" etc. hasPr now additionally requires Number.isInteger && > 0.
- additions/deletions now normalize with the finiteNonNegativeInt rule the sibling
  Rent-a-Loop modules use, so a negative or fractional count can't reach totals or the diff.

title's redactSecrets scrub, MAX_DIFF_PREVIEW_FILES, the diff slice, the status default, and
diffPreview ordering are unchanged; the function still never throws.

Tests cover every branch in both suites: the engine's own node:test suite
(packages/loopover-engine/test/results-payload.test.ts, the source the engine Codecov flag
grades) at 100% on the changed code, plus the root vitest suite's required regression cases.

Closes JSONbored#9611
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 29, 2026

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 5aa94b7 into JSONbored:main Jul 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engine(results): buildResultsPayload builds the customer-facing PR link from unvalidated input

2 participants