fix(security): cross-project trust gate must fail closed in gstack-learnings-search (#1745)#1749
Closed
Pablosinyores wants to merge 1 commit into
Closed
Conversation
…arnings-search (garrytan#1745) The inline comment in bin/gstack-learnings-search promises an allowlist: cross-project learnings are 'only loaded if trusted (user-stated)' to prevent prompt injection from one project's AI-generated learnings silently influencing reviews in another. The implementation is a denylist: if (isCrossProject && e.trusted === false) continue; Rows where 'trusted' is missing/undefined (not literally false) are admitted because 'undefined === false' is false. The 'trusted' field landed in garrytan#988 (2026-04-13); 'gstack-learnings-search' shipped earlier (garrytan#622, 2026-03-29). Any learnings.jsonl written in that window — plus legacy hand-edited rows and rows produced by other tools — lacks the field, so those rows leak across projects under --cross-project today. Flip the gate to an allowlist: if (isCrossProject && e.trusted !== true) continue; Now only rows that explicitly set trusted: true are admitted across projects. Current-project rows are untouched (the gate is scoped to isCrossProject). New regression test pins the legacy-row branch by inserting an entry with no 'trusted' field into the other-project fixture and asserting the search does not surface its insight text under --cross-project --query INJECTED. Full learnings + injection suites green (36/36).
Contributor
|
Heads up on a collision: this overlaps with #1746, which is already open against the same issue (#1745) with the same fix. Both PRs:
#1746 was opened 2026-05-27T07:55Z (right after #1745 was filed at 07:55Z); this one at 10:24Z, so they landed independently within a couple hours. Flagging it so a maintainer doesn't double-review the identical change — no issue with the approach here, the allowlist flip is correct. Whichever lands, the other can close out. |
Author
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.
Summary
Closes #1745.
The inline comment in
bin/gstack-learnings-searchpromises an allowlist: cross-project learnings are only loaded if trusted (user-stated) to prevent prompt injection from one project's AI-generated learnings silently influencing reviews in another. The implementation is a denylist:Rows where
trustedis missing / undefined (not literallyfalse) are admitted becauseundefined === falseisfalse. Thetrustedfield landed in #988 (2026-04-13);gstack-learnings-searchshipped earlier (#622, 2026-03-29). Anylearnings.jsonlwritten in that window — plus legacy hand-edited rows and rows produced by other tools — lacks the field, so those rows leak across projects under--cross-projecttoday.Fix
Flip the gate to a proper allowlist:
Now only rows that explicitly set
trusted: trueare admitted across projects. Current-project rows are untouched (the gate is scoped toisCrossProject).Test plan
gstack-learnings-search cross-project trust gate fails closed for rows missing the trusted field (#1745)) inserts a legacy-shape row with notrustedfield into the other-project fixture and asserts the search does not surface its insight text under--cross-project --query INJECTED.cross-project mode only imports trusted entries from other projectsstill passes (foreign-user(trusted=true) admitted,foreign-observed(trusted=false) blocked).bun test test/gstack-learnings-search.test.ts test/learnings.test.ts test/learnings-injection.test.ts— 36/36 green locally.