llm-security/tests/scanners/ide-extension-data.test.mjs
Kjell Tore Guttormsen 66e8ce2740 fix(llm-security): misc scanner correctness — reflog FP, diff exact-pass, hex dedupe, SARIF version (#20,#22,#50,#54,#56)
#20 git-forensics' reflog force-push detector had a redundant bare 'reset' term subsuming 'reset:', so any commit subject containing 'reset' tripped it; removed. #22 diff-engine's per-current moved-fallback greedily consumed a baseline candidate a later byte-exact match needed (mislabeling unchanged as new/moved on duplicate fingerprints); a global exact pass now runs before the moved-fallback. #54 memory-poisoning double-reported a 64+ char hex token as both base64 and hex; the base64 check now skips pure-hex tokens.

#56 SARIF output hardcoded driver.version 6.0.0 because the orchestrator called toSARIF() without a version; it now passes the real plugin version from package.json. #50 the VS Code known-malicious blocklist was empty with no explanation (unlike the JetBrains file's 'empty by design' note); added a matching blocklist_note. Suite 2004/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
2026-07-18 10:36:51 +02:00

127 lines
4.1 KiB
JavaScript

// ide-extension-data.test.mjs — Unit tests for knowledge-file loaders.
//
// Verifies loadTopJetBrains, loadJetBrainsBlocklist behavior + cache
// discipline shared with VS Code loaders.
import { describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
loadTopJetBrains,
loadJetBrainsBlocklist,
loadTopVSCode,
loadVSCodeBlocklist,
normalizeId,
_resetCache,
} from '../../scanners/lib/ide-extension-data.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
describe('loadTopJetBrains', () => {
beforeEach(() => _resetCache());
it('returns >= 40 canonical xmlIds', async () => {
const ids = await loadTopJetBrains();
assert.ok(Array.isArray(ids));
assert.ok(
ids.length >= 40,
`expected >= 40 entries, got ${ids.length}`,
);
});
it('returns lowercased trimmed entries (normalizeId applied)', async () => {
const ids = await loadTopJetBrains();
for (const id of ids) {
assert.equal(id, id.toLowerCase(), `not lowercased: ${id}`);
assert.equal(id, id.trim(), `not trimmed: ${id}`);
assert.notEqual(id, '', 'empty entry found');
}
});
it('includes bundled JetBrains xmlIds', async () => {
const ids = await loadTopJetBrains();
assert.ok(
ids.includes('com.intellij.java'),
'missing com.intellij.java',
);
assert.ok(
ids.includes('org.jetbrains.kotlin'),
'missing org.jetbrains.kotlin',
);
});
it('includes the legitimate-typo "lombook plugin" xmlId', async () => {
const ids = await loadTopJetBrains();
assert.ok(
ids.includes('lombook plugin'),
'missing "lombook plugin" — canonical xmlId for Lombok integration',
);
});
});
describe('loadJetBrainsBlocklist', () => {
beforeEach(() => _resetCache());
it('returns an empty array (empty by design)', async () => {
const bl = await loadJetBrainsBlocklist();
assert.ok(Array.isArray(bl));
assert.equal(
bl.length,
0,
'blocklist should be empty by design in v6.6.0',
);
});
it('does not throw on repeated invocation', async () => {
await assert.doesNotReject(() => loadJetBrainsBlocklist());
await assert.doesNotReject(() => loadJetBrainsBlocklist());
});
});
describe('cache sanity', () => {
beforeEach(() => _resetCache());
it('calling loadTopJetBrains then loadJetBrainsBlocklist does not throw', async () => {
const ids = await loadTopJetBrains();
const bl = await loadJetBrainsBlocklist();
assert.ok(Array.isArray(ids) && ids.length > 0);
assert.ok(Array.isArray(bl));
});
it('second loadTopJetBrains call returns same data (cache hit)', async () => {
const a = await loadTopJetBrains();
const b = await loadTopJetBrains();
assert.deepEqual(a, b);
});
it('VS Code loaders still work alongside JetBrains loaders', async () => {
const jb = await loadTopJetBrains();
const vs = await loadTopVSCode();
assert.ok(jb.length >= 40);
assert.ok(Array.isArray(vs));
});
});
// #50 (v7.8.3): the VS Code knowledge file has an empty blocklist like the
// JetBrains one, but lacked the explicit "empty by design" note — making the
// empty list read as an oversight. Pin the data/doc consistency here.
describe('top-vscode-extensions.json metadata (#50)', () => {
const KNOWLEDGE_PATH = resolve(__dirname, '../../knowledge/top-vscode-extensions.json');
it('parses as valid JSON', () => {
assert.doesNotThrow(() => JSON.parse(readFileSync(KNOWLEDGE_PATH, 'utf8')));
});
it('has a blocklist_note documenting the empty-by-design blocklist', () => {
const data = JSON.parse(readFileSync(KNOWLEDGE_PATH, 'utf8'));
assert.ok(Array.isArray(data.blocklist), 'blocklist should be an array');
assert.equal(data.blocklist.length, 0, 'blocklist is empty by design');
assert.equal(
typeof data._meta.blocklist_note, 'string',
'_meta.blocklist_note should document the empty-by-design state (like top-jetbrains-plugins.json)',
);
assert.match(data._meta.blocklist_note, /empty by design/i);
});
});