test(llm-security): add end-to-end JetBrains scan integration tests
This commit is contained in:
parent
3de29931fe
commit
2bc2f34fc4
1 changed files with 133 additions and 1 deletions
|
|
@ -3,7 +3,7 @@
|
|||
// Uses fixture trees under tests/fixtures/ide-extensions/ to simulate
|
||||
// real ~/.vscode/extensions/ layouts via rootsOverride injection.
|
||||
|
||||
import { describe, it, beforeEach } from 'node:test';
|
||||
import { describe, it, before, beforeEach } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
|
@ -490,3 +490,135 @@ describe('scanOneExtension JetBrains dispatch', () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JetBrains discovery + scan — full integration over root-jetbrains fixture
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('JetBrains discovery + scan', () => {
|
||||
const ROOT_JB = resolve(FIXTURES, 'root-jetbrains');
|
||||
let env;
|
||||
let allFindings;
|
||||
const findingsById = new Map();
|
||||
|
||||
// Build jars from source/ trees once before the suite. The builder is
|
||||
// idempotent + race-safe (atomic temp-then-rename, SHA-256 skip-if-match)
|
||||
// so it is safe to run under node:test's parallel file execution.
|
||||
before(async () => {
|
||||
const { buildJetBrainsFixtures } = await import('../helpers/build-jetbrains-fixtures.mjs');
|
||||
await buildJetBrainsFixtures({ fixtureRoot: ROOT_JB });
|
||||
|
||||
resetCounter();
|
||||
env = await scan('all', { rootsOverride: [ROOT_JB], intellijOnly: true });
|
||||
allFindings = env.extensions.flatMap(e => e.scanner_results.IDE?.findings || []);
|
||||
for (const ext of env.extensions) {
|
||||
findingsById.set(ext.id, ext.scanner_results.IDE?.findings || []);
|
||||
}
|
||||
});
|
||||
|
||||
it('discovers >= 8 JetBrains plugins (Fleet excluded)', () => {
|
||||
assert.ok(
|
||||
env.meta.extensions_discovered.jetbrains >= 8,
|
||||
`expected >= 8 JB plugins, got ${env.meta.extensions_discovered.jetbrains}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('includes every benign and adversarial IntelliJ fixture', () => {
|
||||
for (const id of [
|
||||
'com.example.benign',
|
||||
'com.example.theme-with-code',
|
||||
'com.example.broad-activation',
|
||||
'com.example.premain',
|
||||
'com.example.native-binary',
|
||||
'com.example.depends-chain',
|
||||
'com.intellij.jaba',
|
||||
]) {
|
||||
assert.ok(
|
||||
env.extensions.some(e => e.id === id),
|
||||
`expected extension ${id} in discovery, got: ${env.extensions.map(e => e.id).join(', ')}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('includes the Android Studio fixture (path divergence test)', () => {
|
||||
assert.ok(
|
||||
env.extensions.some(e => e.id === 'com.google.example'),
|
||||
`expected com.google.example under AndroidStudio base, got: ${env.extensions.map(e => e.id).join(', ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('excludes Fleet plugins (different plugin model)', () => {
|
||||
assert.ok(
|
||||
env.extensions.every(e => !e.location.includes('Fleet')),
|
||||
`Fleet plugin leaked in: ${env.extensions.filter(e => e.location.includes('Fleet')).map(e => e.id).join(', ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('all JB extensions routed through JetBrains path (type=jetbrains)', () => {
|
||||
for (const ext of env.extensions) {
|
||||
assert.equal(ext.type, 'jetbrains', `${ext.id} had type=${ext.type}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('theme-with-code fixture triggers checkThemeWithCodeJB', () => {
|
||||
const findings = findingsById.get('com.example.theme-with-code') || [];
|
||||
assert.ok(
|
||||
findings.some(f => f.title.includes('checkThemeWithCodeJB')),
|
||||
`expected theme-with-code finding; got: ${findings.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('broad-activation fixture triggers checkBroadActivationJB', () => {
|
||||
const findings = findingsById.get('com.example.broad-activation') || [];
|
||||
assert.ok(
|
||||
findings.some(f => f.title.includes('checkBroadActivationJB')),
|
||||
`expected broad-activation finding; got: ${findings.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('premain fixture triggers checkPremainClassJB (HIGH)', () => {
|
||||
const findings = findingsById.get('com.example.premain') || [];
|
||||
const premain = findings.filter(f => f.title.includes('checkPremainClassJB'));
|
||||
assert.ok(premain.length >= 1, `expected premain finding; got: ${findings.map(f => f.title).join(' | ')}`);
|
||||
assert.equal(premain[0].severity, 'high');
|
||||
});
|
||||
|
||||
it('native-binary fixture triggers checkNativeBinariesJB', () => {
|
||||
const findings = findingsById.get('com.example.native-binary') || [];
|
||||
assert.ok(
|
||||
findings.some(f => f.title.includes('checkNativeBinariesJB')),
|
||||
`expected native-binary finding; got: ${findings.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('depends-chain fixture triggers checkDependsChainJB', () => {
|
||||
const findings = findingsById.get('com.example.depends-chain') || [];
|
||||
assert.ok(
|
||||
findings.some(f => f.title.includes('checkDependsChainJB')),
|
||||
`expected depends-chain finding; got: ${findings.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('typosquat fixture (com.intellij.jaba) triggers checkTyposquatJB', () => {
|
||||
const findings = findingsById.get('com.intellij.jaba') || [];
|
||||
assert.ok(
|
||||
findings.some(f => f.title.includes('checkTyposquatJB')),
|
||||
`expected typosquat finding; got: ${findings.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('benign fixture produces no HIGH/CRITICAL JB findings', () => {
|
||||
const findings = findingsById.get('com.example.benign') || [];
|
||||
const highs = findings.filter(f => f.severity === 'high' || f.severity === 'critical');
|
||||
assert.equal(
|
||||
highs.length, 0,
|
||||
`expected benign plugin to be clean; got: ${highs.map(f => f.title).join(' | ')}`,
|
||||
);
|
||||
});
|
||||
|
||||
it('all JB findings carry DS-IDE- prefix', () => {
|
||||
for (const f of allFindings) {
|
||||
assert.ok(f.id.startsWith('DS-IDE-'), `expected DS-IDE- prefix, got ${f.id}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue