mirror of https://github.com/garrytan/gstack.git
Merge 956ade3d43 into c43c850cae
This commit is contained in:
commit
cba26aeac2
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue