feat: initial open marketplace with llm-security, config-audit, ultraplan-local
This commit is contained in:
commit
f93d6abdae
380 changed files with 65935 additions and 0 deletions
108
plugins/llm-security/tests/lib/distribution-stats.test.mjs
Normal file
108
plugins/llm-security/tests/lib/distribution-stats.test.mjs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// distribution-stats.test.mjs — Tests for scanners/lib/distribution-stats.mjs
|
||||
// Zero external dependencies: node:test + node:assert only.
|
||||
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { jensenShannonDivergence, buildDistribution } from '../../scanners/lib/distribution-stats.mjs';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// buildDistribution
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('distribution-stats — buildDistribution', () => {
|
||||
it('empty array → empty map', () => {
|
||||
const d = buildDistribution([]);
|
||||
assert.equal(d.size, 0);
|
||||
});
|
||||
|
||||
it('single category normalizes to 1.0', () => {
|
||||
const d = buildDistribution(['Read', 'Read', 'Read']);
|
||||
assert.equal(d.size, 1);
|
||||
assert.equal(d.get('Read'), 1.0);
|
||||
});
|
||||
|
||||
it('two equal categories normalize to 0.5 each', () => {
|
||||
const d = buildDistribution(['Read', 'Bash', 'Read', 'Bash']);
|
||||
assert.equal(d.size, 2);
|
||||
assert.equal(d.get('Read'), 0.5);
|
||||
assert.equal(d.get('Bash'), 0.5);
|
||||
});
|
||||
|
||||
it('unequal distribution normalizes correctly', () => {
|
||||
const d = buildDistribution(['Read', 'Read', 'Read', 'Bash']);
|
||||
assert.equal(d.get('Read'), 0.75);
|
||||
assert.equal(d.get('Bash'), 0.25);
|
||||
});
|
||||
|
||||
it('sum of probabilities equals 1.0', () => {
|
||||
const d = buildDistribution(['Read', 'Bash', 'Write', 'Grep', 'Bash']);
|
||||
let sum = 0;
|
||||
for (const v of d.values()) sum += v;
|
||||
assert.ok(Math.abs(sum - 1.0) < 1e-10, `Sum ${sum} should be ~1.0`);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// jensenShannonDivergence
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('distribution-stats — jensenShannonDivergence', () => {
|
||||
it('identical distributions → JSD = 0', () => {
|
||||
const P = buildDistribution(['Read', 'Bash', 'Read', 'Bash']);
|
||||
const Q = buildDistribution(['Read', 'Bash', 'Read', 'Bash']);
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(Math.abs(jsd) < 1e-10, `JSD ${jsd} should be ~0`);
|
||||
});
|
||||
|
||||
it('fully disjoint distributions → JSD = 1', () => {
|
||||
const P = buildDistribution(['Read', 'Read', 'Read']);
|
||||
const Q = buildDistribution(['Bash', 'Bash', 'Bash']);
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(Math.abs(jsd - 1.0) < 1e-10, `JSD ${jsd} should be ~1.0`);
|
||||
});
|
||||
|
||||
it('partially overlapping distributions → 0 < JSD < 1', () => {
|
||||
const P = buildDistribution(['Read', 'Read', 'Bash']);
|
||||
const Q = buildDistribution(['Read', 'Bash', 'Bash']);
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(jsd > 0, `JSD ${jsd} should be > 0`);
|
||||
assert.ok(jsd < 1, `JSD ${jsd} should be < 1`);
|
||||
});
|
||||
|
||||
it('JSD is symmetric: JSD(P,Q) = JSD(Q,P)', () => {
|
||||
const P = buildDistribution(['Read', 'Read', 'Read', 'Bash']);
|
||||
const Q = buildDistribution(['Read', 'Bash', 'Bash', 'Bash']);
|
||||
const jsd1 = jensenShannonDivergence(P, Q);
|
||||
const jsd2 = jensenShannonDivergence(Q, P);
|
||||
assert.ok(Math.abs(jsd1 - jsd2) < 1e-10, `JSD(P,Q)=${jsd1} should equal JSD(Q,P)=${jsd2}`);
|
||||
});
|
||||
|
||||
it('two empty distributions → JSD = 0', () => {
|
||||
const P = new Map();
|
||||
const Q = new Map();
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.equal(jsd, 0);
|
||||
});
|
||||
|
||||
it('one empty + one non-empty → JSD = 0.5', () => {
|
||||
const P = buildDistribution(['Read']);
|
||||
const Q = new Map();
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(Math.abs(jsd - 0.5) < 1e-10, `JSD ${jsd} should be 0.5`);
|
||||
});
|
||||
|
||||
it('three categories with different distributions', () => {
|
||||
const P = buildDistribution(['Read', 'Read', 'Read', 'Write', 'Write', 'Bash']);
|
||||
const Q = buildDistribution(['Read', 'Write', 'Write', 'Write', 'Bash', 'Bash']);
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(jsd > 0, `JSD ${jsd} should be > 0`);
|
||||
assert.ok(jsd < 1, `JSD ${jsd} should be < 1`);
|
||||
});
|
||||
|
||||
it('diverse vs concentrated → high JSD', () => {
|
||||
const P = buildDistribution(['Read', 'Write', 'Bash', 'Grep', 'Glob']);
|
||||
const Q = buildDistribution(['Read', 'Read', 'Read', 'Read', 'Read']);
|
||||
const jsd = jensenShannonDivergence(P, Q);
|
||||
assert.ok(jsd > 0.3, `JSD ${jsd} should be > 0.3 for diverse vs concentrated`);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue