Skip to content

fix(review): scope private review context by owner - #1582

Closed
JSONbored wants to merge 1 commit into
mainfrom
codex/propose-fix-for-cross-owner-prompt-leak
Closed

fix(review): scope private review context by owner#1582
JSONbored wants to merge 1 commit into
mainfrom
codex/propose-fix-for-cross-owner-prompt-leak

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Motivation

  • The previous per-repo review-context resolver fell back to bare {repo}/review folders, allowing cross-owner collisions and leakage of private review prompt material to other repos.
  • Review guide and skill text is sensitive operator-provided material and must be bound to the repository owner to prevent cross-tenant disclosure.

Description

  • Removed the bare repo-name fallback so reviewContextFolders only returns owner-qualified {owner}__{repo}/review candidates, ensuring review prompts are owner-scoped.
  • Clarified the makeLocalReviewContextReader documentation to state that review prompt material is owner-qualified only.
  • Added a regression unit test in test/unit/private-config.test.ts that verifies bare repo-name review folders are ignored for a different owner and that missing/invalid repos degrade to an empty review context.

Testing

  • Ran unit tests with npm test -- --run test/unit/private-config.test.ts, which passed (20 tests).
  • Ran static type checking with npm run typecheck, which passed.
  • Attempted coverage with npm run test:coverage -- --run test/unit/private-config.test.ts, where tests passed but coverage generation failed with TypeError: jsTokens is not a function from the local coverage provider.
  • Attempted npm audit --audit-level=moderate, which was blocked by the registry audit endpoint returning 403 Forbidden.

Codex Task

@superagent-security

Copy link
Copy Markdown
Contributor

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

@codecov

codecov Bot commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.46%. Comparing base (00cff73) to head (5b67e59).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1582   +/-   ##
=======================================
  Coverage   95.46%   95.46%           
=======================================
  Files         202      202           
  Lines       21839    21839           
  Branches     7885     7885           
=======================================
  Hits        20849    20849           
  Misses        414      414           
  Partials      576      576           
Files with missing lines Coverage Δ
src/selfhost/private-config.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb

loopover-orb Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review — safe to merge

2 files · 1 AI reviewers · no blockers · readiness 48/100 · CI green · unknown

✅ Approved — safe to merge

Review summary
Removes the bare-repo-name fallback (`{repo}/review`) from `reviewContextFolders`, leaving only the owner-qualified `{owner}__{repo}/review` candidate. This correctly closes a cross-tenant review-prompt leakage path in multi-owner self-host deployments where two tenants share the same `GITTENSORY_REPO_CONFIG_DIR`: previously, `ownera/foo`'s guide in `foo/review/CLAUDE.md` would be served to `ownerb/foo`. The fix is at the right layer — review context is expressly distinct from manifest config — and the renamed test now asserts the bare-folder guide is NOT served (`{ guide: null, skills: [] }`), which is a real exercise of the changed branch, not a fabricated path.

Signal Result Evidence
Code review ✅ No blockers 1 reviewers, synthesized
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Review load ❌ 8/20 Readiness component derived from cached public PR metadata and labels; size label size:XS.
Validation evidence ❌ 5/25 Cached preflight status is hold.
Open PR queue ❌ 3/10 26 open PR(s), 9 likely reviewable, 17 unlinked.
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 80 PR(s), 312 issue(s).
Gate result ✅ Passing No configured blocker found.
Nits — 5 non-blocking
  • src/selfhost/private-config.ts (~L148): `makeLocalReviewContextReader` still uses `for (const folder of reviewContextFolders(...))`, which now yields at most one element; the loop is harmless but vestigial — `const [folder] = reviewContextFolders(repoFullName); if (!folder) return { guide: null, skills: [] };` reads more clearly and signals the single-candidate intent.
  • src/selfhost/private-config.ts (`localConfigCandidates`): the bare-repo-folder fallback for `.gittensory.yml` manifests was intentionally left in place, but in a multi-tenant self-host deployment a shared config dir means gate/model config in `{repo}/.gittensory.yml` can still leak cross-owner by the same mechanism — this PR is correctly scoped but the asymmetry warrants a follow-up issue.
  • test/unit/private-config.test.ts (L138–155): the two split tests share `mkdtempSync` boilerplate; minor DRY nit — a `beforeEach`-allocated `dir` or a shared setup helper would reduce copy-paste if the suite grows.
  • Open a follow-up to audit `localConfigCandidates`' bare-repo folder fallback for the same cross-tenant leak: `.gittensory.yml` can carry model overrides, gate mode, and BYOK provider settings that operators may consider sensitive, so the same fix logic (`remove the bare-repo fallback and require the owner-qualified folder`) may be warranted there too.
  • src/selfhost/private-config.ts: simplify the `for...of` in `makeLocalReviewContextReader` to a direct check now that the candidate list is 0-or-1: `const [folder] = reviewContextFolders(repoFullName); if (!folder) return { guide: null, skills: [] };` — removes the implicit assumption of multi-candidate iteration and makes the single-candidate semantics obvious.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: not available
  • Official Gittensor activity: 80 PR(s), 312 issue(s).
  • Related work: Titles/paths share 5 meaningful terms. (PR #1583)
  • Related work: Titles/paths share 5 meaningful terms. (PR #1581, PR #1587)
  • Related work: Titles/paths share 4 meaningful terms. (PR #1583, PR #1586)
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Explain no-issue PR.
  • Review top overlaps.
  • Add scope summary.
  • Fix blocker.
  • Expect slower review.
  • Refresh registry data or choose a registered active repo.
  • Link the issue being solved, or explicitly explain why this is a no-issue PR.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Review load = cached public PR metadata such as size labels, changed paths, and preflight status.
  • Open PR queue = repo-wide review pressure; it is not a PR quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
Review details

Generated from public PR metadata and the diff. Advisory only; deterministic signals remain authoritative.

Removes the bare-repo-name fallback (`{repo}/review`) from `reviewContextFolders`, leaving only the owner-qualified `{owner}__{repo}/review` candidate. This correctly closes a cross-tenant review-prompt leakage path in multi-owner self-host deployments where two tenants share the same `GITTENSORY_REPO_CONFIG_DIR`: previously, `ownera/foo`'s guide in `foo/review/CLAUDE.md` would be served to `ownerb/foo`. The fix is at the right layer — review context is expressly distinct from manifest config — and the renamed test now asserts the bare-folder guide is NOT served (`{ guide: null, skills: [] }`), which is a real exercise of the changed branch, not a fabricated path.

Nits (5)

  • src/selfhost/private-config.ts (~L148): `makeLocalReviewContextReader` still uses `for (const folder of reviewContextFolders(...))`, which now yields at most one element; the loop is harmless but vestigial — `const [folder] = reviewContextFolders(repoFullName); if (!folder) return { guide: null, skills: [] };` reads more clearly and signals the single-candidate intent.
  • src/selfhost/private-config.ts (`localConfigCandidates`): the bare-repo-folder fallback for `.gittensory.yml` manifests was intentionally left in place, but in a multi-tenant self-host deployment a shared config dir means gate/model config in `{repo}/.gittensory.yml` can still leak cross-owner by the same mechanism — this PR is correctly scoped but the asymmetry warrants a follow-up issue.
  • test/unit/private-config.test.ts (L138–155): the two split tests share `mkdtempSync` boilerplate; minor DRY nit — a `beforeEach`-allocated `dir` or a shared setup helper would reduce copy-paste if the suite grows.
  • Open a follow-up to audit `localConfigCandidates`' bare-repo folder fallback for the same cross-tenant leak: `.gittensory.yml` can carry model overrides, gate mode, and BYOK provider settings that operators may consider sensitive, so the same fix logic (`remove the bare-repo fallback and require the owner-qualified folder`) may be warranted there too.
  • src/selfhost/private-config.ts: simplify the `for...of` in `makeLocalReviewContextReader` to a direct check now that the candidate list is 0-or-1: `const [folder] = reviewContextFolders(repoFullName); if (!folder) return { guide: null, skills: [] };` — removes the implicit assumption of multi-candidate iteration and makes the single-candidate semantics obvious.

🟩 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 Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@JSONbored

Copy link
Copy Markdown
Owner Author

Closing — this isn't an attacker-reachable leak, and the fix reverses recently-shipped back-compat one-sidedly. The review-context folder names come from operator-set GITTENSORY_REPO_CONFIG_DIR + a webhook-validated repoFullName (no PR-author input), and the owner-qualified {owner}__{repo}/review folder is already tried first (makeLocalReviewContextReader), so it always wins when present. The bare {repo}/review fallback you're removing was deliberately added one day earlier by #1580 (00cff73c) as a documented layout. And the change is self-inconsistent: localConfigCandidates (private-config.ts:57) STILL keeps the identical bare {repo} fallback for .gittensory.yml, which carries the same review.instructions material — so this doesn't actually close the claimed exposure, it just diverges two parallel resolvers. If we want to enforce owner-scoping, that's a deliberate policy change applied to both resolvers, not a one-sided patch that breaks #1580's layout.

@JSONbored JSONbored closed this Jun 27, 2026
@loopover-orb loopover-orb Bot added gittensor gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jun 27, 2026
@JSONbored
JSONbored deleted the codex/propose-fix-for-cross-owner-prompt-leak branch June 29, 2026 21:21
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.

1 participant