@waratahlabs/canopy 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -195
- package/SKILL.md +13 -2
- package/demo/data/insaider-threat-model.tc.json +7800 -0
- package/demo/data/openclaw-demo.tc.json +80 -303
- package/demo/data/openclaw-nemoclaw-demo.tc.json +114 -389
- package/dist/aivss/scoring/heuristic-scorer.d.ts +1 -1
- package/dist/aivss/scoring/heuristic-scorer.js +1 -1
- package/dist/cli.js +24 -37
- package/dist/cli.js.map +1 -1
- package/dist/examples/insaider-assessment.d.ts +28 -0
- package/dist/examples/insaider-assessment.d.ts.map +1 -0
- package/dist/examples/insaider-assessment.js +301 -0
- package/dist/examples/insaider-assessment.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +1 -1
- package/dist/output/tc-assembler.d.ts +0 -10
- package/dist/output/tc-assembler.d.ts.map +1 -1
- package/dist/output/tc-assembler.js +4 -42
- package/dist/output/tc-assembler.js.map +1 -1
- package/dist/references/container-taxonomy.d.ts +41 -0
- package/dist/references/container-taxonomy.d.ts.map +1 -0
- package/dist/references/container-taxonomy.js +207 -0
- package/dist/references/container-taxonomy.js.map +1 -0
- package/dist/references/index.d.ts +0 -7
- package/dist/references/index.d.ts.map +1 -1
- package/dist/references/index.js +0 -13
- package/dist/references/index.js.map +1 -1
- package/dist/references/nist-ai-rmf.d.ts +1 -6
- package/dist/references/nist-ai-rmf.d.ts.map +1 -1
- package/dist/references/nist-ai-rmf.js +1 -10
- package/dist/references/nist-ai-rmf.js.map +1 -1
- package/dist/references/owasp-agentic-top10.d.ts +1 -6
- package/dist/references/owasp-agentic-top10.d.ts.map +1 -1
- package/dist/references/owasp-agentic-top10.js +0 -9
- package/dist/references/owasp-agentic-top10.js.map +1 -1
- package/package.json +4 -18
- package/demo/index.html +0 -556
- package/dist/aivss/scoring/a2a-corpus.test.d.ts +0 -28
- package/dist/aivss/scoring/a2a-corpus.test.d.ts.map +0 -1
- package/dist/aivss/scoring/a2a-corpus.test.js +0 -109
- package/dist/aivss/scoring/a2a-corpus.test.js.map +0 -1
- package/dist/aivss/scoring/determinism.test.d.ts +0 -16
- package/dist/aivss/scoring/determinism.test.d.ts.map +0 -1
- package/dist/aivss/scoring/determinism.test.js +0 -169
- package/dist/aivss/scoring/determinism.test.js.map +0 -1
- package/dist/aivss/scoring/scoring-mode.d.ts +0 -48
- package/dist/aivss/scoring/scoring-mode.d.ts.map +0 -1
- package/dist/aivss/scoring/scoring-mode.js +0 -64
- package/dist/aivss/scoring/scoring-mode.js.map +0 -1
- package/dist/references/registry-parity.test.d.ts +0 -17
- package/dist/references/registry-parity.test.d.ts.map +0 -1
- package/dist/references/registry-parity.test.js +0 -87
- package/dist/references/registry-parity.test.js.map +0 -1
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A2A corpus regression — deterministic scoring against real agent definitions.
|
|
3
|
-
*
|
|
4
|
-
* The unit tests prove the scorer is pure over a hand-written fixture. This
|
|
5
|
-
* proves it stays stable over a corpus of ~25 real agent topologies harvested
|
|
6
|
-
* from a2aproject/a2a-samples, which is where scoring changes actually show up:
|
|
7
|
-
* a threshold tweak that looks harmless against one synthetic agent will move
|
|
8
|
-
* severities across a real corpus.
|
|
9
|
-
*
|
|
10
|
-
* Regenerate the corpus with a checkout of a2a-samples:
|
|
11
|
-
*
|
|
12
|
-
* bun scripts/harvest-a2a-samples.ts path/to/a2a-samples
|
|
13
|
-
*
|
|
14
|
-
* That writes tests/fixtures/a2a/input/<agent>.json (the synthesized
|
|
15
|
-
* AgentDefinitions) and tests/fixtures/a2a/expected/<agent>.tc.json (the
|
|
16
|
-
* assessed artifact). This test re-derives the expected output from the input
|
|
17
|
-
* and diffs.
|
|
18
|
-
*
|
|
19
|
-
* A diff here is not automatically a bug. It means emitted scores moved, which
|
|
20
|
-
* per CONTRIBUTING.md is a breaking change requiring a major version bump — so
|
|
21
|
-
* the diff should be reviewed, then the corpus regenerated in the same commit
|
|
22
|
-
* that changes the scoring.
|
|
23
|
-
*
|
|
24
|
-
* The suite skips cleanly when the corpus is absent, so a fresh clone without
|
|
25
|
-
* fixtures is green rather than red.
|
|
26
|
-
*/
|
|
27
|
-
import { describe, expect, test } from "bun:test";
|
|
28
|
-
import * as fs from "node:fs";
|
|
29
|
-
import * as path from "node:path";
|
|
30
|
-
import { fileURLToPath } from "node:url";
|
|
31
|
-
import { heuristicSeverity } from "./heuristic-scorer.js";
|
|
32
|
-
import { assembleTCFormat, severityKey, slotKey } from "../../output/tc-assembler.js";
|
|
33
|
-
import { ATLAS_COVERAGE_MATRIX } from "../atlas/coverage-matrix.js";
|
|
34
|
-
import { loadAtlasData } from "../atlas/loader.js";
|
|
35
|
-
import { resolveAtlasYaml } from "../atlas/resolve-path.js";
|
|
36
|
-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../..");
|
|
37
|
-
const INPUT_DIR = path.join(repoRoot, "tests/fixtures/a2a/input");
|
|
38
|
-
const EXPECTED_DIR = path.join(repoRoot, "tests/fixtures/a2a/expected");
|
|
39
|
-
function corpusCases() {
|
|
40
|
-
if (!fs.existsSync(INPUT_DIR))
|
|
41
|
-
return [];
|
|
42
|
-
return fs
|
|
43
|
-
.readdirSync(INPUT_DIR)
|
|
44
|
-
.filter((f) => f.endsWith(".json"))
|
|
45
|
-
.map((f) => f.replace(/\.json$/, ""))
|
|
46
|
-
.sort();
|
|
47
|
-
}
|
|
48
|
-
const cases = corpusCases();
|
|
49
|
-
// Normalise away the only field that legitimately varies between runs.
|
|
50
|
-
const normalise = (doc) => JSON.stringify(doc).replace(/"Generated: [^"\\]+? \\n/, '"Generated: <ts> \\n');
|
|
51
|
-
function assess(input) {
|
|
52
|
-
const atlasData = loadAtlasData(resolveAtlasYaml());
|
|
53
|
-
const severityBySubCategory = new Map();
|
|
54
|
-
const filledSlots = new Map();
|
|
55
|
-
for (const agent of input.agents) {
|
|
56
|
-
for (const entry of ATLAS_COVERAGE_MATRIX) {
|
|
57
|
-
severityBySubCategory.set(severityKey(agent.id, entry.metric, entry.subCategory), heuristicSeverity(agent, entry.metric, entry.subCategory));
|
|
58
|
-
const techniqueId = entry.atlasTechniqueIds[0];
|
|
59
|
-
if (!techniqueId)
|
|
60
|
-
continue;
|
|
61
|
-
const technique = atlasData.techniqueById.get(techniqueId);
|
|
62
|
-
if (!technique)
|
|
63
|
-
continue;
|
|
64
|
-
filledSlots.set(slotKey(agent.id, techniqueId, entry.metric, entry.subCategory), {
|
|
65
|
-
threatSource: "A threat actor",
|
|
66
|
-
prerequisites: `with access to ${agent.name}`,
|
|
67
|
-
threatAction: `exploit ${entry.subCategory.toLowerCase()} via ${technique.id}`,
|
|
68
|
-
threatImpact: `compromise ${agent.name}`,
|
|
69
|
-
impactedGoal: ["confidentiality", "integrity"],
|
|
70
|
-
impactedAssets: [agent.name.toLowerCase()],
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return assembleTCFormat({
|
|
75
|
-
agents: input.agents,
|
|
76
|
-
handoffs: input.handoffs ?? [],
|
|
77
|
-
metricResponses: [],
|
|
78
|
-
atlasData,
|
|
79
|
-
coverageMatrix: ATLAS_COVERAGE_MATRIX,
|
|
80
|
-
filledSlots,
|
|
81
|
-
severityBySubCategory,
|
|
82
|
-
applicationName: input.applicationName,
|
|
83
|
-
scoringMode: "deterministic",
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
describe.skipIf(cases.length === 0)("a2a corpus regression", () => {
|
|
87
|
-
test("corpus has both halves of every golden pair", () => {
|
|
88
|
-
for (const name of cases) {
|
|
89
|
-
expect(fs.existsSync(path.join(EXPECTED_DIR, `${name}.tc.json`))).toBe(true);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
test.each(cases)("%s scores identically to its committed artifact", (name) => {
|
|
93
|
-
const input = JSON.parse(fs.readFileSync(path.join(INPUT_DIR, `${name}.json`), "utf8"));
|
|
94
|
-
const expected = JSON.parse(fs.readFileSync(path.join(EXPECTED_DIR, `${name}.tc.json`), "utf8"));
|
|
95
|
-
expect(normalise(assess(input))).toBe(normalise(expected));
|
|
96
|
-
});
|
|
97
|
-
test.each(cases)("%s re-assesses identically within a single run", (name) => {
|
|
98
|
-
const input = JSON.parse(fs.readFileSync(path.join(INPUT_DIR, `${name}.json`), "utf8"));
|
|
99
|
-
expect(normalise(assess(input))).toBe(normalise(assess(input)));
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
// Visible when the corpus is missing, so an absent harness reads as a stated
|
|
103
|
-
// gap rather than silent green.
|
|
104
|
-
describe.skipIf(cases.length > 0)("a2a corpus regression (not present)", () => {
|
|
105
|
-
test("corpus is absent — regenerate with scripts/harvest-a2a-samples.ts", () => {
|
|
106
|
-
expect(cases.length).toBe(0);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
//# sourceMappingURL=a2a-corpus.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"a2a-corpus.test.js","sourceRoot":"","sources":["../../../src/aivss/scoring/a2a-corpus.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACtF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAI5D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AACxF,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAAC;AAClE,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,6BAA6B,CAAC,CAAC;AAQxE,SAAS,WAAW;IAClB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,EAAE;SACN,WAAW,CAAC,SAAS,CAAC;SACtB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;SACpC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;AAE5B,uEAAuE;AACvE,MAAM,SAAS,GAAG,CAAC,GAAY,EAAU,EAAE,CACzC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,2BAA2B,EAAE,uBAAuB,CAAC,CAAC;AAEpF,SAAS,MAAM,CAAC,KAAkB;IAChC,MAAM,SAAS,GAAG,aAAa,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpD,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAE,CAAC;IAE9B,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,KAAK,MAAM,KAAK,IAAI,qBAAqB,EAAE,CAAC;YAC1C,qBAAqB,CAAC,GAAG,CACvB,WAAW,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,EACtD,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,CAC1D,CAAC;YACF,MAAM,WAAW,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;YAC/C,IAAI,CAAC,WAAW;gBAAE,SAAS;YAC3B,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAC3D,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE;gBAC/E,YAAY,EAAE,gBAAgB;gBAC9B,aAAa,EAAE,kBAAkB,KAAK,CAAC,IAAI,EAAE;gBAC7C,YAAY,EAAE,WAAW,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,SAAS,CAAC,EAAE,EAAE;gBAC9E,YAAY,EAAE,cAAc,KAAK,CAAC,IAAI,EAAE;gBACxC,YAAY,EAAE,CAAC,iBAAiB,EAAE,WAAW,CAAC;gBAC9C,cAAc,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;aAC3C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,gBAAgB,CAAC;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;QAC9B,eAAe,EAAE,EAAE;QACnB,SAAS;QACT,cAAc,EAAE,qBAAqB;QACrC,WAAW;QACX,qBAAqB;QACrB,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,WAAW,EAAE,eAAe;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,uBAAuB,EAAE,GAAG,EAAE;IAChE,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,iDAAiD,EAAE,CAAC,IAAI,EAAE,EAAE;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,CAAC,CAC/C,CAAC;QACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAEjG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,gDAAgD,EAAE,CAAC,IAAI,EAAE,EAAE;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,CAAC,CAC/C,CAAC;QACjB,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,6EAA6E;AAC7E,gCAAgC;AAChC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,qCAAqC,EAAE,GAAG,EAAE;IAC5E,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Determinism — the property that makes a Canopy score defensible.
|
|
3
|
-
*
|
|
4
|
-
* A security engineer has to be able to say where a number came from. CVSS is
|
|
5
|
-
* trusted because the vector string lets anyone recompute it; OpenSSF Scorecard
|
|
6
|
-
* because every check's rule is readable. Canopy's deterministic mode makes the
|
|
7
|
-
* same promise, and these tests are what keep it honest.
|
|
8
|
-
*
|
|
9
|
-
* Two distinct claims are covered:
|
|
10
|
-
* 1. The heuristic scorer is a pure function of the agent definition.
|
|
11
|
-
* 2. The assembled artifact is byte-stable apart from its generation
|
|
12
|
-
* timestamp — which required replacing random threat ids with
|
|
13
|
-
* content-derived ones.
|
|
14
|
-
*/
|
|
15
|
-
export {};
|
|
16
|
-
//# sourceMappingURL=determinism.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"determinism.test.d.ts","sourceRoot":"","sources":["../../../src/aivss/scoring/determinism.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Determinism — the property that makes a Canopy score defensible.
|
|
3
|
-
*
|
|
4
|
-
* A security engineer has to be able to say where a number came from. CVSS is
|
|
5
|
-
* trusted because the vector string lets anyone recompute it; OpenSSF Scorecard
|
|
6
|
-
* because every check's rule is readable. Canopy's deterministic mode makes the
|
|
7
|
-
* same promise, and these tests are what keep it honest.
|
|
8
|
-
*
|
|
9
|
-
* Two distinct claims are covered:
|
|
10
|
-
* 1. The heuristic scorer is a pure function of the agent definition.
|
|
11
|
-
* 2. The assembled artifact is byte-stable apart from its generation
|
|
12
|
-
* timestamp — which required replacing random threat ids with
|
|
13
|
-
* content-derived ones.
|
|
14
|
-
*/
|
|
15
|
-
import { describe, expect, test } from "bun:test";
|
|
16
|
-
import { heuristicSeverity } from "./heuristic-scorer.js";
|
|
17
|
-
import { SCORING_MODE_INFO, SCORING_MODES, reproducibilityWarning } from "./scoring-mode.js";
|
|
18
|
-
import { assembleTCFormat, severityKey, slotKey } from "../../output/tc-assembler.js";
|
|
19
|
-
import { ATLAS_COVERAGE_MATRIX } from "../atlas/coverage-matrix.js";
|
|
20
|
-
import { loadAtlasData } from "../atlas/loader.js";
|
|
21
|
-
import { resolveAtlasYaml } from "../atlas/resolve-path.js";
|
|
22
|
-
const AGENT = {
|
|
23
|
-
id: "agent-det-1",
|
|
24
|
-
name: "Determinism Fixture",
|
|
25
|
-
model: "claude-sonnet-4-6",
|
|
26
|
-
systemPrompt: "You process mortgage applications end to end.",
|
|
27
|
-
tools: [
|
|
28
|
-
{ name: "shell", description: "Run shell commands", dangerous: true },
|
|
29
|
-
{ name: "credit_check", description: "Query the credit bureau", dangerous: true },
|
|
30
|
-
],
|
|
31
|
-
permissions: ["read:applications", "write:decisions", "execute:scripts"],
|
|
32
|
-
deploymentContext: "FinancialServices",
|
|
33
|
-
modelComplexity: "Complex",
|
|
34
|
-
mitigationMultiplier: 1.0,
|
|
35
|
-
finetuned: false,
|
|
36
|
-
dependencies: ["document-store", "credit-api"],
|
|
37
|
-
secretManagement: "env",
|
|
38
|
-
deployment: "AWS ECS Fargate",
|
|
39
|
-
};
|
|
40
|
-
describe("heuristic scorer is a pure function", () => {
|
|
41
|
-
test("repeated scoring of the same input yields identical severities", () => {
|
|
42
|
-
const runOnce = () => ATLAS_COVERAGE_MATRIX.map((e) => heuristicSeverity(AGENT, e.metric, e.subCategory));
|
|
43
|
-
const first = runOnce();
|
|
44
|
-
for (let i = 0; i < 25; i++) {
|
|
45
|
-
expect(runOnce()).toEqual(first);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
test("scoring does not mutate the agent definition", () => {
|
|
49
|
-
const before = JSON.stringify(AGENT);
|
|
50
|
-
for (const e of ATLAS_COVERAGE_MATRIX)
|
|
51
|
-
heuristicSeverity(AGENT, e.metric, e.subCategory);
|
|
52
|
-
expect(JSON.stringify(AGENT)).toBe(before);
|
|
53
|
-
});
|
|
54
|
-
test("a materially riskier agent does not score lower", () => {
|
|
55
|
-
// Guards the direction of the scoring rules: removing every mitigation and
|
|
56
|
-
// granting dangerous capability must not reduce severity. Catches an
|
|
57
|
-
// inverted comparison, which a pure-determinism test would happily pass.
|
|
58
|
-
const hardened = { ...AGENT, mitigationMultiplier: 0.5, tools: [], permissions: ["read:applications"] };
|
|
59
|
-
const rank = { None: 0, Low: 1, Medium: 2, High: 3, Critical: 4 };
|
|
60
|
-
let exposedTotal = 0;
|
|
61
|
-
let hardenedTotal = 0;
|
|
62
|
-
for (const e of ATLAS_COVERAGE_MATRIX) {
|
|
63
|
-
exposedTotal += rank[heuristicSeverity(AGENT, e.metric, e.subCategory)];
|
|
64
|
-
hardenedTotal += rank[heuristicSeverity(hardened, e.metric, e.subCategory)];
|
|
65
|
-
}
|
|
66
|
-
expect(exposedTotal).toBeGreaterThan(hardenedTotal);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe("assembled artifact is byte-stable", () => {
|
|
70
|
-
const atlasData = loadAtlasData(resolveAtlasYaml());
|
|
71
|
-
function build() {
|
|
72
|
-
const severityBySubCategory = new Map();
|
|
73
|
-
const filledSlots = new Map();
|
|
74
|
-
for (const e of ATLAS_COVERAGE_MATRIX) {
|
|
75
|
-
const severity = heuristicSeverity(AGENT, e.metric, e.subCategory);
|
|
76
|
-
severityBySubCategory.set(severityKey(AGENT.id, e.metric, e.subCategory), severity);
|
|
77
|
-
const techniqueId = e.atlasTechniqueIds[0];
|
|
78
|
-
if (!techniqueId || !atlasData.techniqueById.get(techniqueId))
|
|
79
|
-
continue;
|
|
80
|
-
filledSlots.set(slotKey(AGENT.id, techniqueId, e.metric, e.subCategory), {
|
|
81
|
-
threatSource: "A threat actor",
|
|
82
|
-
prerequisites: `with access to ${AGENT.name}`,
|
|
83
|
-
threatAction: `exploit ${e.subCategory.toLowerCase()}`,
|
|
84
|
-
threatImpact: `compromise ${AGENT.name}`,
|
|
85
|
-
impactedGoal: ["confidentiality"],
|
|
86
|
-
impactedAssets: [AGENT.name.toLowerCase()],
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
return assembleTCFormat({
|
|
90
|
-
agents: [AGENT],
|
|
91
|
-
handoffs: [],
|
|
92
|
-
metricResponses: [],
|
|
93
|
-
atlasData,
|
|
94
|
-
coverageMatrix: ATLAS_COVERAGE_MATRIX,
|
|
95
|
-
filledSlots,
|
|
96
|
-
severityBySubCategory,
|
|
97
|
-
applicationName: "Determinism Fixture App",
|
|
98
|
-
scoringMode: "deterministic",
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
// The audit trail carries a wall-clock generation time, which is legitimately
|
|
102
|
-
// different every run. Everything else must match byte for byte.
|
|
103
|
-
const stripTimestamp = (d) => JSON.stringify({
|
|
104
|
-
...d,
|
|
105
|
-
applicationInfo: {
|
|
106
|
-
...d.applicationInfo,
|
|
107
|
-
description: (d.applicationInfo?.description ?? "").replace(/Generated: [^\s]+/, "Generated: <ts>"),
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
test("two runs produce identical output apart from the timestamp", () => {
|
|
111
|
-
expect(stripTimestamp(build())).toBe(stripTimestamp(build()));
|
|
112
|
-
});
|
|
113
|
-
test("threat ids are content-derived, not random", () => {
|
|
114
|
-
// Regression guard: ids were crypto.randomUUID(), so structurally identical
|
|
115
|
-
// artifacts differed byte-wise every run and diffs were unusable.
|
|
116
|
-
const a = build();
|
|
117
|
-
const b = build();
|
|
118
|
-
const aThreats = a.threats ?? [];
|
|
119
|
-
const bThreats = b.threats ?? [];
|
|
120
|
-
expect(aThreats.map((t) => t.id)).toEqual(bThreats.map((t) => t.id));
|
|
121
|
-
expect(new Set(aThreats.map((t) => t.id)).size).toBe(aThreats.length);
|
|
122
|
-
for (const t of aThreats) {
|
|
123
|
-
expect(t.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
test("every threat records the scoring mode that produced it", () => {
|
|
127
|
-
const doc = build();
|
|
128
|
-
const docThreats = doc.threats ?? [];
|
|
129
|
-
expect(docThreats.length).toBeGreaterThan(0);
|
|
130
|
-
for (const threat of docThreats) {
|
|
131
|
-
const mode = threat.metadata?.find((m) => m.key === "custom:scoring-mode")?.value;
|
|
132
|
-
expect(mode).toBe("deterministic");
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
test("the audit trail states the mode and its reproducibility", () => {
|
|
136
|
-
const description = build().applicationInfo?.description ?? "";
|
|
137
|
-
expect(description).toContain("Scoring mode: deterministic");
|
|
138
|
-
expect(description).toContain("Reproducible: yes");
|
|
139
|
-
});
|
|
140
|
-
test("a non-reproducible mode is labelled as such", () => {
|
|
141
|
-
const severityBySubCategory = new Map();
|
|
142
|
-
const doc = assembleTCFormat({
|
|
143
|
-
agents: [AGENT],
|
|
144
|
-
handoffs: [],
|
|
145
|
-
metricResponses: [],
|
|
146
|
-
atlasData,
|
|
147
|
-
coverageMatrix: ATLAS_COVERAGE_MATRIX,
|
|
148
|
-
filledSlots: new Map(),
|
|
149
|
-
severityBySubCategory,
|
|
150
|
-
applicationName: "Harness Run",
|
|
151
|
-
scoringMode: "harness",
|
|
152
|
-
});
|
|
153
|
-
expect(doc.applicationInfo?.description ?? "").toContain("Scoring mode: harness");
|
|
154
|
-
expect(doc.applicationInfo?.description ?? "").toContain("Reproducible: no");
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
describe("scoring mode metadata", () => {
|
|
158
|
-
test("exactly one mode is reproducible, and it needs no credentials", () => {
|
|
159
|
-
const reproducible = SCORING_MODES.filter((m) => SCORING_MODE_INFO[m].reproducible);
|
|
160
|
-
expect(reproducible).toEqual(["deterministic"]);
|
|
161
|
-
expect(SCORING_MODE_INFO.deterministic.requiresCredentials).toBe(false);
|
|
162
|
-
});
|
|
163
|
-
test("non-reproducible modes warn, the reproducible one does not", () => {
|
|
164
|
-
expect(reproducibilityWarning("deterministic")).toBeNull();
|
|
165
|
-
expect(reproducibilityWarning("model")).toContain("not reproducible");
|
|
166
|
-
expect(reproducibilityWarning("harness")).toContain("not reproducible");
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
//# sourceMappingURL=determinism.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"determinism.test.js","sourceRoot":"","sources":["../../../src/aivss/scoring/determinism.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scoring provenance — how a severity in the output was actually arrived at.
|
|
3
|
-
*
|
|
4
|
-
* Why this exists: a score you cannot reproduce is not evidence. CVSS earned
|
|
5
|
-
* practitioner trust because the vector string lets anyone recompute the
|
|
6
|
-
* number; OpenSSF Scorecard because every check documents its own rule. Canopy
|
|
7
|
-
* emitted a number with no statement of how it was produced, and the three
|
|
8
|
-
* possible provenances have very different reproducibility properties:
|
|
9
|
-
*
|
|
10
|
-
* - `deterministic` — the heuristic scorer. Pure function of the
|
|
11
|
-
* AgentDefinition. Same input always yields the same score, on any machine,
|
|
12
|
-
* with no network and no credentials. Auditable: every rule is readable in
|
|
13
|
-
* heuristic-scorer.ts.
|
|
14
|
-
*
|
|
15
|
-
* - `model` — a provider (Bedrock / Anthropic) classified each sub-category.
|
|
16
|
-
* Richer reasoning, but NOT reproducible: temperature, model version, and
|
|
17
|
-
* provider-side changes all move the result. Two runs a month apart can
|
|
18
|
-
* legitimately differ.
|
|
19
|
-
*
|
|
20
|
-
* - `harness` — the calling LLM agent answered the assessment prompts itself
|
|
21
|
-
* via HarnessBatchProvider. Least reproducible of the three, because the
|
|
22
|
-
* model doing the reasoning is whatever happened to be driving the harness.
|
|
23
|
-
* Excellent for zero-credential exploration; not something to put in front
|
|
24
|
-
* of an auditor as a stable measurement.
|
|
25
|
-
*
|
|
26
|
-
* A consumer gating CI on Canopy baselines needs `deterministic`. Someone
|
|
27
|
-
* exploring a new system interactively is well served by `harness`. The defect
|
|
28
|
-
* was never offering the choice, or recording which one produced the artifact.
|
|
29
|
-
*/
|
|
30
|
-
export type ScoringMode = "deterministic" | "model" | "harness";
|
|
31
|
-
export declare const SCORING_MODES: readonly ScoringMode[];
|
|
32
|
-
export interface ScoringModeDescriptor {
|
|
33
|
-
mode: ScoringMode;
|
|
34
|
-
/** Whether identical input is guaranteed to yield identical scores. */
|
|
35
|
-
reproducible: boolean;
|
|
36
|
-
/** Whether the mode needs provider credentials or network access. */
|
|
37
|
-
requiresCredentials: boolean;
|
|
38
|
-
/** One-line description for the audit trail in emitted artifacts. */
|
|
39
|
-
summary: string;
|
|
40
|
-
}
|
|
41
|
-
export declare const SCORING_MODE_INFO: Record<ScoringMode, ScoringModeDescriptor>;
|
|
42
|
-
/**
|
|
43
|
-
* Human-facing warning shown when a run's scoring mode carries a
|
|
44
|
-
* reproducibility caveat the operator should know about before quoting the
|
|
45
|
-
* numbers. Returns null when no warning is warranted.
|
|
46
|
-
*/
|
|
47
|
-
export declare function reproducibilityWarning(mode: ScoringMode): string | null;
|
|
48
|
-
//# sourceMappingURL=scoring-mode.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scoring-mode.d.ts","sourceRoot":"","sources":["../../../src/aivss/scoring/scoring-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,OAAO,GAAG,SAAS,CAAC;AAEhE,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAAmD,CAAC;AAEpG,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,WAAW,CAAC;IAClB,uEAAuE;IACvE,YAAY,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,WAAW,EAAE,qBAAqB,CAsBxE,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAQvE"}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scoring provenance — how a severity in the output was actually arrived at.
|
|
3
|
-
*
|
|
4
|
-
* Why this exists: a score you cannot reproduce is not evidence. CVSS earned
|
|
5
|
-
* practitioner trust because the vector string lets anyone recompute the
|
|
6
|
-
* number; OpenSSF Scorecard because every check documents its own rule. Canopy
|
|
7
|
-
* emitted a number with no statement of how it was produced, and the three
|
|
8
|
-
* possible provenances have very different reproducibility properties:
|
|
9
|
-
*
|
|
10
|
-
* - `deterministic` — the heuristic scorer. Pure function of the
|
|
11
|
-
* AgentDefinition. Same input always yields the same score, on any machine,
|
|
12
|
-
* with no network and no credentials. Auditable: every rule is readable in
|
|
13
|
-
* heuristic-scorer.ts.
|
|
14
|
-
*
|
|
15
|
-
* - `model` — a provider (Bedrock / Anthropic) classified each sub-category.
|
|
16
|
-
* Richer reasoning, but NOT reproducible: temperature, model version, and
|
|
17
|
-
* provider-side changes all move the result. Two runs a month apart can
|
|
18
|
-
* legitimately differ.
|
|
19
|
-
*
|
|
20
|
-
* - `harness` — the calling LLM agent answered the assessment prompts itself
|
|
21
|
-
* via HarnessBatchProvider. Least reproducible of the three, because the
|
|
22
|
-
* model doing the reasoning is whatever happened to be driving the harness.
|
|
23
|
-
* Excellent for zero-credential exploration; not something to put in front
|
|
24
|
-
* of an auditor as a stable measurement.
|
|
25
|
-
*
|
|
26
|
-
* A consumer gating CI on Canopy baselines needs `deterministic`. Someone
|
|
27
|
-
* exploring a new system interactively is well served by `harness`. The defect
|
|
28
|
-
* was never offering the choice, or recording which one produced the artifact.
|
|
29
|
-
*/
|
|
30
|
-
export const SCORING_MODES = ["deterministic", "model", "harness"];
|
|
31
|
-
export const SCORING_MODE_INFO = {
|
|
32
|
-
deterministic: {
|
|
33
|
-
mode: "deterministic",
|
|
34
|
-
reproducible: true,
|
|
35
|
-
requiresCredentials: false,
|
|
36
|
-
summary: "Heuristic scorer — pure function of the agent definition. Byte-identical across runs; every rule auditable in heuristic-scorer.ts.",
|
|
37
|
-
},
|
|
38
|
-
model: {
|
|
39
|
-
mode: "model",
|
|
40
|
-
reproducible: false,
|
|
41
|
-
requiresCredentials: true,
|
|
42
|
-
summary: "Provider inference (Bedrock/Anthropic) classified each sub-category. Richer reasoning; not reproducible across model versions.",
|
|
43
|
-
},
|
|
44
|
-
harness: {
|
|
45
|
-
mode: "harness",
|
|
46
|
-
reproducible: false,
|
|
47
|
-
requiresCredentials: false,
|
|
48
|
-
summary: "The calling LLM agent answered assessment prompts directly. Zero-credential; reproducibility depends on the driving model.",
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Human-facing warning shown when a run's scoring mode carries a
|
|
53
|
-
* reproducibility caveat the operator should know about before quoting the
|
|
54
|
-
* numbers. Returns null when no warning is warranted.
|
|
55
|
-
*/
|
|
56
|
-
export function reproducibilityWarning(mode) {
|
|
57
|
-
const info = SCORING_MODE_INFO[mode];
|
|
58
|
-
if (info.reproducible)
|
|
59
|
-
return null;
|
|
60
|
-
return (`canopy: scoring mode "${mode}" is not reproducible — ${info.summary}\n` +
|
|
61
|
-
` Re-running may yield different scores. Use --deterministic for a\n` +
|
|
62
|
-
` stable, auditable score suitable for CI gating or an audit trail.\n`);
|
|
63
|
-
}
|
|
64
|
-
//# sourceMappingURL=scoring-mode.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scoring-mode.js","sourceRoot":"","sources":["../../../src/aivss/scoring/scoring-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,MAAM,CAAC,MAAM,aAAa,GAA2B,CAAC,eAAe,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAYpG,MAAM,CAAC,MAAM,iBAAiB,GAA+C;IAC3E,aAAa,EAAE;QACb,IAAI,EAAE,eAAe;QACrB,YAAY,EAAE,IAAI;QAClB,mBAAmB,EAAE,KAAK;QAC1B,OAAO,EACL,oIAAoI;KACvI;IACD,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;QACb,YAAY,EAAE,KAAK;QACnB,mBAAmB,EAAE,IAAI;QACzB,OAAO,EACL,gIAAgI;KACnI;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,YAAY,EAAE,KAAK;QACnB,mBAAmB,EAAE,KAAK;QAC1B,OAAO,EACL,4HAA4H;KAC/H;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAiB;IACtD,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,CACL,yBAAyB,IAAI,2BAA2B,IAAI,CAAC,OAAO,IAAI;QACxE,4EAA4E;QAC5E,6EAA6E,CAC9E,CAAC;AACJ,CAAC"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework parity — package metadata must match what the engine registers.
|
|
3
|
-
*
|
|
4
|
-
* Why this test exists: the package advertised "OWASP, NIST AI RMF" in its
|
|
5
|
-
* description and keywords while src/references/index.ts registered only three
|
|
6
|
-
* adapters. NIST and OWASP Agentic Top 10 had complete risk catalogues but
|
|
7
|
-
* never assembled a FrameworkAdapter, and NemoClaw assembled one that was
|
|
8
|
-
* never registered. A user reading the package page would have expected
|
|
9
|
-
* framework annotations that the engine could not produce.
|
|
10
|
-
*
|
|
11
|
-
* For a tool whose product is trust, an advertised-but-absent framework is a
|
|
12
|
-
* correctness defect, not a documentation lag. This test makes the drift
|
|
13
|
-
* mechanical to catch: add an adapter without advertising it, or advertise one
|
|
14
|
-
* without registering it, and the suite fails.
|
|
15
|
-
*/
|
|
16
|
-
export {};
|
|
17
|
-
//# sourceMappingURL=registry-parity.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry-parity.test.d.ts","sourceRoot":"","sources":["../../src/references/registry-parity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework parity — package metadata must match what the engine registers.
|
|
3
|
-
*
|
|
4
|
-
* Why this test exists: the package advertised "OWASP, NIST AI RMF" in its
|
|
5
|
-
* description and keywords while src/references/index.ts registered only three
|
|
6
|
-
* adapters. NIST and OWASP Agentic Top 10 had complete risk catalogues but
|
|
7
|
-
* never assembled a FrameworkAdapter, and NemoClaw assembled one that was
|
|
8
|
-
* never registered. A user reading the package page would have expected
|
|
9
|
-
* framework annotations that the engine could not produce.
|
|
10
|
-
*
|
|
11
|
-
* For a tool whose product is trust, an advertised-but-absent framework is a
|
|
12
|
-
* correctness defect, not a documentation lag. This test makes the drift
|
|
13
|
-
* mechanical to catch: add an adapter without advertising it, or advertise one
|
|
14
|
-
* without registering it, and the suite fails.
|
|
15
|
-
*/
|
|
16
|
-
import { describe, expect, test } from "bun:test";
|
|
17
|
-
import * as fs from "node:fs";
|
|
18
|
-
import * as path from "node:path";
|
|
19
|
-
import { fileURLToPath } from "node:url";
|
|
20
|
-
import { FRAMEWORK_REGISTRY, REGISTERED_FRAMEWORK_IDS } from "./index.js";
|
|
21
|
-
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json");
|
|
22
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
23
|
-
describe("framework registry parity", () => {
|
|
24
|
-
test("package.json declares a canopy.frameworks list", () => {
|
|
25
|
-
expect(Array.isArray(pkg.canopy?.frameworks)).toBe(true);
|
|
26
|
-
expect(pkg.canopy.frameworks.length).toBeGreaterThan(0);
|
|
27
|
-
});
|
|
28
|
-
test("advertised frameworks exactly match registered adapters", () => {
|
|
29
|
-
const advertised = [...pkg.canopy.frameworks].sort();
|
|
30
|
-
// Set equality in both directions — an adapter registered but not
|
|
31
|
-
// advertised is as much a defect as the reverse, because the README and
|
|
32
|
-
// package page under-report what the tool actually does.
|
|
33
|
-
expect(advertised).toEqual(REGISTERED_FRAMEWORK_IDS);
|
|
34
|
-
});
|
|
35
|
-
test("no duplicate framework ids are registered", () => {
|
|
36
|
-
const ids = FRAMEWORK_REGISTRY.map((a) => a.citation.id);
|
|
37
|
-
expect(new Set(ids).size).toBe(ids.length);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
describe("registered adapters are well-formed", () => {
|
|
41
|
-
test.each(FRAMEWORK_REGISTRY.map((a) => [a.citation.id, a]))("%s carries a complete citation and a non-empty risk list", (_id, adapter) => {
|
|
42
|
-
expect(adapter.citation.title.length).toBeGreaterThan(0);
|
|
43
|
-
expect(adapter.citation.authors.length).toBeGreaterThan(0);
|
|
44
|
-
expect(adapter.citation.url).toMatch(/^https?:\/\//);
|
|
45
|
-
expect(adapter.citation.year).toBeGreaterThan(2000);
|
|
46
|
-
expect(adapter.risks.length).toBeGreaterThan(0);
|
|
47
|
-
});
|
|
48
|
-
test.each(FRAMEWORK_REGISTRY.map((a) => [a.citation.id, a]))("%s risks declare the framework id they belong to", (id, adapter) => {
|
|
49
|
-
for (const risk of adapter.risks) {
|
|
50
|
-
expect(risk.framework).toBe(id);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
test.each(FRAMEWORK_REGISTRY.map((a) => [a.citation.id, a]))("%s risk ids are unique within the framework", (_id, adapter) => {
|
|
54
|
-
const ids = adapter.risks.map((r) => r.id);
|
|
55
|
-
expect(new Set(ids).size).toBe(ids.length);
|
|
56
|
-
});
|
|
57
|
-
test.each(FRAMEWORK_REGISTRY.map((a) => [a.citation.id, a]))("%s cites only real ATLAS technique ids", (_id, adapter) => {
|
|
58
|
-
// Shape check rather than a lookup against vendored ATLAS: this catches
|
|
59
|
-
// the failure mode competitors have shipped — a model asked to "attach a
|
|
60
|
-
// technique id" inventing a plausible-looking one.
|
|
61
|
-
for (const risk of adapter.risks) {
|
|
62
|
-
for (const techniqueId of risk.atlasTechniqueIds) {
|
|
63
|
-
expect(techniqueId).toMatch(/^AML\.T\d{4}(\.\d{3})?$/);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
test.each(FRAMEWORK_REGISTRY.map((a) => [a.citation.id, a]))("%s risks declare a valid riskType and known AIVSS metrics", (_id, adapter) => {
|
|
68
|
-
const validMetrics = new Set([
|
|
69
|
-
"MR",
|
|
70
|
-
"DS",
|
|
71
|
-
"EI",
|
|
72
|
-
"DC",
|
|
73
|
-
"AD",
|
|
74
|
-
"AA",
|
|
75
|
-
"LL",
|
|
76
|
-
"GV",
|
|
77
|
-
"CS",
|
|
78
|
-
]);
|
|
79
|
-
for (const risk of adapter.risks) {
|
|
80
|
-
expect(["security", "ethical", "compliance"]).toContain(risk.riskType);
|
|
81
|
-
for (const metric of risk.aivssMetrics) {
|
|
82
|
-
expect(validMetrics.has(metric)).toBe(true);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
//# sourceMappingURL=registry-parity.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry-parity.test.js","sourceRoot":"","sources":["../../src/references/registry-parity.test.ts"],"names":[],"mappings":"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"}
|