This commit is contained in:
Jayesh Betala 2026-06-03 11:24:05 +05:30 committed by GitHub
commit cba26aeac2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 17 additions and 4 deletions

View File

@ -90,10 +90,13 @@ for (const taggedLine of lines) {
const isCrossProject = sourceTag === 'cross';
e._crossProject = isCrossProject;
// Trust gate: cross-project learnings only loaded if trusted (user-stated)
// This prevents prompt injection from one project's AI-generated learnings
// silently influencing reviews in another project.
if (isCrossProject && e.trusted === false) continue;
// Trust gate: cross-project learnings only loaded if explicitly trusted
// (user-stated). This prevents prompt injection from one project's
// AI-generated learnings silently influencing reviews in another project.
// Fail closed: rows missing the trusted field (legacy entries written
// before the field existed, hand-edited rows, or rows from other tools)
// are treated as untrusted rather than admitted by default.
if (isCrossProject && e.trusted !== true) continue;
entries.push(e);
} catch {}

View File

@ -33,6 +33,9 @@ beforeAll(() => {
const otherEntries = [
{ ts: '2026-05-04T00:00:00Z', skill: 'test', type: 'pattern', key: 'foreign-observed', insight: 'A foreign observed insight', confidence: 8, source: 'observed', trusted: false, files: [] },
{ ts: '2026-05-05T00:00:00Z', skill: 'test', type: 'pattern', key: 'foreign-user', insight: 'A foreign user-stated insight', confidence: 8, source: 'user-stated', trusted: true, files: [] },
// Legacy / hand-written / third-party row written before the trusted field
// existed: no `trusted` key at all. Must NOT be admitted cross-project.
{ ts: '2026-05-06T00:00:00Z', skill: 'test', type: 'pattern', key: 'foreign-legacy', insight: 'A foreign legacy insight', confidence: 8, source: 'observed', files: [] },
];
fs.writeFileSync(path.join(projDir, 'learnings.jsonl'), entries.map(e => JSON.stringify(e)).join('\n') + '\n');
fs.writeFileSync(path.join(otherProjDir, 'learnings.jsonl'), otherEntries.map(e => JSON.stringify(e)).join('\n') + '\n');
@ -79,4 +82,11 @@ describe('gstack-learnings-search cross-project trust gating', () => {
expect(out).toContain('[cross-project]');
expect(out).not.toContain('foreign-observed');
});
test('cross-project mode rejects foreign rows missing the trusted field (fail closed)', () => {
const out = run(['--cross-project', '--query', 'foreign']);
// Legacy/hand-written rows with no `trusted` field must be treated as
// untrusted, not admitted by default — otherwise the trust gate fails open.
expect(out).not.toContain('foreign-legacy');
});
});