feat(llm-security): implement JetBrains discovery + Android Studio base dir
This commit is contained in:
parent
5afb9b1f33
commit
03d61d8bca
2 changed files with 208 additions and 6 deletions
|
|
@ -0,0 +1,100 @@
|
|||
// ide-extension-discovery.test.mjs — discoverJetBrainsExtensions integration tests.
|
||||
// All fixtures built in-test via mkdtemp + mkdir — no committed filesystem state.
|
||||
|
||||
import { describe, it, after } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtemp, mkdir, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import {
|
||||
discoverJetBrainsExtensions,
|
||||
getAndroidStudioBaseDir,
|
||||
} from '../../scanners/lib/ide-extension-discovery.mjs';
|
||||
|
||||
const TEST_PREFIX = 'llmsec-jbdisc-';
|
||||
const created = [];
|
||||
|
||||
async function buildFixtureBase(layout) {
|
||||
const base = await mkdtemp(join(tmpdir(), TEST_PREFIX));
|
||||
created.push(base);
|
||||
for (const [product, plugins] of Object.entries(layout)) {
|
||||
for (const plug of plugins) {
|
||||
await mkdir(join(base, product, 'plugins', plug), { recursive: true });
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
describe('discoverJetBrainsExtensions — basic walk', () => {
|
||||
it('finds plugins under IntelliJIdea<version>/plugins/', async () => {
|
||||
const base = await buildFixtureBase({
|
||||
'IntelliJIdea2024.3': ['com.example.one', 'com.example.two'],
|
||||
});
|
||||
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
|
||||
assert.equal(res.extensions.length, 2);
|
||||
assert.ok(res.extensions.every(e => e.type === 'jetbrains'));
|
||||
assert.ok(res.extensions.some(e => e.name === 'com.example.one'));
|
||||
assert.equal(res.extensions[0].productDir, 'IntelliJIdea2024.3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverJetBrainsExtensions — Fleet excluded', () => {
|
||||
it('ignores Fleet directory', async () => {
|
||||
const base = await buildFixtureBase({
|
||||
'IntelliJIdea2024.3': ['com.example.good'],
|
||||
'Fleet': ['com.example.fleet'],
|
||||
});
|
||||
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
|
||||
assert.equal(res.extensions.length, 1);
|
||||
assert.equal(res.extensions[0].name, 'com.example.good');
|
||||
assert.ok(res.extensions.every(e => !e.location.includes('Fleet')));
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverJetBrainsExtensions — Android Studio support', () => {
|
||||
it('discovers from AndroidStudio product dir', async () => {
|
||||
const base = await buildFixtureBase({
|
||||
'AndroidStudio2024.3.1': ['com.google.example'],
|
||||
});
|
||||
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
|
||||
assert.equal(res.extensions.length, 1);
|
||||
assert.equal(res.extensions[0].productDir, 'AndroidStudio2024.3.1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverJetBrainsExtensions — multi-product', () => {
|
||||
it('walks all matching product dirs', async () => {
|
||||
const base = await buildFixtureBase({
|
||||
'IntelliJIdea2024.3': ['a'],
|
||||
'PyCharm2024.3': ['b'],
|
||||
'GoLand2024.3': ['c'],
|
||||
'Toolbox': ['d'], // excluded
|
||||
});
|
||||
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
|
||||
assert.equal(res.extensions.length, 3);
|
||||
assert.ok(!res.extensions.some(e => e.location.includes('Toolbox')));
|
||||
});
|
||||
});
|
||||
|
||||
describe('discoverJetBrainsExtensions — skip hidden + disabled_plugins.txt', () => {
|
||||
it('ignores dotfile plugins and disabled_plugins.txt', async () => {
|
||||
const base = await buildFixtureBase({
|
||||
'IntelliJIdea2024.3': ['.hidden', 'com.real', 'disabled_plugins.txt'],
|
||||
});
|
||||
const res = await discoverJetBrainsExtensions({ rootsOverride: [base] });
|
||||
assert.equal(res.extensions.length, 1);
|
||||
assert.equal(res.extensions[0].name, 'com.real');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAndroidStudioBaseDir — Linux path divergence', () => {
|
||||
it('is a path-producing function (no exception)', () => {
|
||||
// Actual return value is null or a path depending on host — just assert no throw.
|
||||
const v = getAndroidStudioBaseDir();
|
||||
assert.ok(v === null || typeof v === 'string');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
for (const r of created) await rm(r, { recursive: true, force: true }).catch(() => {});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue