@waratahlabs/canopy 0.5.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/LICENSE +21 -0
- package/README.md +323 -0
- package/SKILL.md +97 -0
- package/demo/data/openclaw-demo.tc.json +397 -0
- package/demo/data/openclaw-nemoclaw-demo.tc.json +475 -0
- package/demo/index.html +556 -0
- package/dist/adapters/a2a.d.ts +82 -0
- package/dist/adapters/a2a.d.ts.map +1 -0
- package/dist/adapters/a2a.js +215 -0
- package/dist/adapters/a2a.js.map +1 -0
- package/dist/adapters/a2a.test.d.ts +2 -0
- package/dist/adapters/a2a.test.d.ts.map +1 -0
- package/dist/adapters/a2a.test.js +196 -0
- package/dist/adapters/a2a.test.js.map +1 -0
- package/dist/adapters/langfuse.d.ts +28 -0
- package/dist/adapters/langfuse.d.ts.map +1 -0
- package/dist/adapters/langfuse.js +192 -0
- package/dist/adapters/langfuse.js.map +1 -0
- package/dist/adapters/n8n.d.ts +4 -0
- package/dist/adapters/n8n.d.ts.map +1 -0
- package/dist/adapters/n8n.js +89 -0
- package/dist/adapters/n8n.js.map +1 -0
- package/dist/adapters/openclaw.d.ts +58 -0
- package/dist/adapters/openclaw.d.ts.map +1 -0
- package/dist/adapters/openclaw.js +275 -0
- package/dist/adapters/openclaw.js.map +1 -0
- package/dist/adapters/types.d.ts +33 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/aivss/atlas/coverage-matrix.d.ts +12 -0
- package/dist/aivss/atlas/coverage-matrix.d.ts.map +1 -0
- package/dist/aivss/atlas/coverage-matrix.js +272 -0
- package/dist/aivss/atlas/coverage-matrix.js.map +1 -0
- package/dist/aivss/atlas/loader.d.ts +28 -0
- package/dist/aivss/atlas/loader.d.ts.map +1 -0
- package/dist/aivss/atlas/loader.js +126 -0
- package/dist/aivss/atlas/loader.js.map +1 -0
- package/dist/aivss/atlas/loader.test.d.ts +11 -0
- package/dist/aivss/atlas/loader.test.d.ts.map +1 -0
- package/dist/aivss/atlas/loader.test.js +111 -0
- package/dist/aivss/atlas/loader.test.js.map +1 -0
- package/dist/aivss/atlas/resolve-path.d.ts +34 -0
- package/dist/aivss/atlas/resolve-path.d.ts.map +1 -0
- package/dist/aivss/atlas/resolve-path.js +58 -0
- package/dist/aivss/atlas/resolve-path.js.map +1 -0
- package/dist/aivss/batch/batch-builder.d.ts +11 -0
- package/dist/aivss/batch/batch-builder.d.ts.map +1 -0
- package/dist/aivss/batch/batch-builder.js +75 -0
- package/dist/aivss/batch/batch-builder.js.map +1 -0
- package/dist/aivss/index.d.ts +21 -0
- package/dist/aivss/index.d.ts.map +1 -0
- package/dist/aivss/index.js +19 -0
- package/dist/aivss/index.js.map +1 -0
- package/dist/aivss/knowledge-base/kb-client.d.ts +9 -0
- package/dist/aivss/knowledge-base/kb-client.d.ts.map +1 -0
- package/dist/aivss/knowledge-base/kb-client.js +139 -0
- package/dist/aivss/knowledge-base/kb-client.js.map +1 -0
- package/dist/aivss/knowledge-base/kb-types.d.ts +21 -0
- package/dist/aivss/knowledge-base/kb-types.d.ts.map +1 -0
- package/dist/aivss/knowledge-base/kb-types.js +2 -0
- package/dist/aivss/knowledge-base/kb-types.js.map +1 -0
- package/dist/aivss/knowledge-base/search-builder.d.ts +7 -0
- package/dist/aivss/knowledge-base/search-builder.d.ts.map +1 -0
- package/dist/aivss/knowledge-base/search-builder.js +119 -0
- package/dist/aivss/knowledge-base/search-builder.js.map +1 -0
- package/dist/aivss/metrics/aa.d.ts +3 -0
- package/dist/aivss/metrics/aa.d.ts.map +1 -0
- package/dist/aivss/metrics/aa.js +26 -0
- package/dist/aivss/metrics/aa.js.map +1 -0
- package/dist/aivss/metrics/ad.d.ts +3 -0
- package/dist/aivss/metrics/ad.d.ts.map +1 -0
- package/dist/aivss/metrics/ad.js +30 -0
- package/dist/aivss/metrics/ad.js.map +1 -0
- package/dist/aivss/metrics/cs.d.ts +3 -0
- package/dist/aivss/metrics/cs.d.ts.map +1 -0
- package/dist/aivss/metrics/cs.js +50 -0
- package/dist/aivss/metrics/cs.js.map +1 -0
- package/dist/aivss/metrics/dc.d.ts +3 -0
- package/dist/aivss/metrics/dc.d.ts.map +1 -0
- package/dist/aivss/metrics/dc.js +30 -0
- package/dist/aivss/metrics/dc.js.map +1 -0
- package/dist/aivss/metrics/ds.d.ts +3 -0
- package/dist/aivss/metrics/ds.d.ts.map +1 -0
- package/dist/aivss/metrics/ds.js +26 -0
- package/dist/aivss/metrics/ds.js.map +1 -0
- package/dist/aivss/metrics/ei.d.ts +3 -0
- package/dist/aivss/metrics/ei.d.ts.map +1 -0
- package/dist/aivss/metrics/ei.js +30 -0
- package/dist/aivss/metrics/ei.js.map +1 -0
- package/dist/aivss/metrics/gv.d.ts +3 -0
- package/dist/aivss/metrics/gv.d.ts.map +1 -0
- package/dist/aivss/metrics/gv.js +34 -0
- package/dist/aivss/metrics/gv.js.map +1 -0
- package/dist/aivss/metrics/ll.d.ts +3 -0
- package/dist/aivss/metrics/ll.d.ts.map +1 -0
- package/dist/aivss/metrics/ll.js +30 -0
- package/dist/aivss/metrics/ll.js.map +1 -0
- package/dist/aivss/metrics/mr.d.ts +3 -0
- package/dist/aivss/metrics/mr.d.ts.map +1 -0
- package/dist/aivss/metrics/mr.js +26 -0
- package/dist/aivss/metrics/mr.js.map +1 -0
- package/dist/aivss/metrics/shared.d.ts +22 -0
- package/dist/aivss/metrics/shared.d.ts.map +1 -0
- package/dist/aivss/metrics/shared.js +70 -0
- package/dist/aivss/metrics/shared.js.map +1 -0
- package/dist/aivss/prompts/base-system-prompt.d.ts +2 -0
- package/dist/aivss/prompts/base-system-prompt.d.ts.map +1 -0
- package/dist/aivss/prompts/base-system-prompt.js +31 -0
- package/dist/aivss/prompts/base-system-prompt.js.map +1 -0
- package/dist/aivss/prompts/prompt-composer.d.ts +13 -0
- package/dist/aivss/prompts/prompt-composer.d.ts.map +1 -0
- package/dist/aivss/prompts/prompt-composer.js +21 -0
- package/dist/aivss/prompts/prompt-composer.js.map +1 -0
- package/dist/aivss/scoring/a2a-corpus.test.d.ts +28 -0
- package/dist/aivss/scoring/a2a-corpus.test.d.ts.map +1 -0
- package/dist/aivss/scoring/a2a-corpus.test.js +109 -0
- package/dist/aivss/scoring/a2a-corpus.test.js.map +1 -0
- package/dist/aivss/scoring/determinism.test.d.ts +16 -0
- package/dist/aivss/scoring/determinism.test.d.ts.map +1 -0
- package/dist/aivss/scoring/determinism.test.js +169 -0
- package/dist/aivss/scoring/determinism.test.js.map +1 -0
- package/dist/aivss/scoring/heuristic-scorer.d.ts +21 -0
- package/dist/aivss/scoring/heuristic-scorer.d.ts.map +1 -0
- package/dist/aivss/scoring/heuristic-scorer.js +299 -0
- package/dist/aivss/scoring/heuristic-scorer.js.map +1 -0
- package/dist/aivss/scoring/heuristic-scorer.test.d.ts +8 -0
- package/dist/aivss/scoring/heuristic-scorer.test.d.ts.map +1 -0
- package/dist/aivss/scoring/heuristic-scorer.test.js +150 -0
- package/dist/aivss/scoring/heuristic-scorer.test.js.map +1 -0
- package/dist/aivss/scoring/scoring-mode.d.ts +48 -0
- package/dist/aivss/scoring/scoring-mode.d.ts.map +1 -0
- package/dist/aivss/scoring/scoring-mode.js +64 -0
- package/dist/aivss/scoring/scoring-mode.js.map +1 -0
- package/dist/aivss/scoring/v4-engine.d.ts +15 -0
- package/dist/aivss/scoring/v4-engine.d.ts.map +1 -0
- package/dist/aivss/scoring/v4-engine.js +84 -0
- package/dist/aivss/scoring/v4-engine.js.map +1 -0
- package/dist/aivss/scoring/v4-engine.test.d.ts +16 -0
- package/dist/aivss/scoring/v4-engine.test.d.ts.map +1 -0
- package/dist/aivss/scoring/v4-engine.test.js +156 -0
- package/dist/aivss/scoring/v4-engine.test.js.map +1 -0
- package/dist/aivss/types.d.ts +92 -0
- package/dist/aivss/types.d.ts.map +1 -0
- package/dist/aivss/types.js +25 -0
- package/dist/aivss/types.js.map +1 -0
- package/dist/baseline/baseline.test.d.ts +2 -0
- package/dist/baseline/baseline.test.d.ts.map +1 -0
- package/dist/baseline/baseline.test.js +219 -0
- package/dist/baseline/baseline.test.js.map +1 -0
- package/dist/baseline/diff.d.ts +42 -0
- package/dist/baseline/diff.d.ts.map +1 -0
- package/dist/baseline/diff.js +124 -0
- package/dist/baseline/diff.js.map +1 -0
- package/dist/baseline/store.d.ts +51 -0
- package/dist/baseline/store.d.ts.map +1 -0
- package/dist/baseline/store.js +92 -0
- package/dist/baseline/store.js.map +1 -0
- package/dist/batch/anthropic-batch-runner.d.ts +50 -0
- package/dist/batch/anthropic-batch-runner.d.ts.map +1 -0
- package/dist/batch/anthropic-batch-runner.js +91 -0
- package/dist/batch/anthropic-batch-runner.js.map +1 -0
- package/dist/batch/bedrock-batch-runner.d.ts +90 -0
- package/dist/batch/bedrock-batch-runner.d.ts.map +1 -0
- package/dist/batch/bedrock-batch-runner.js +199 -0
- package/dist/batch/bedrock-batch-runner.js.map +1 -0
- package/dist/batch/providers/anthropic.d.ts +55 -0
- package/dist/batch/providers/anthropic.d.ts.map +1 -0
- package/dist/batch/providers/anthropic.js +103 -0
- package/dist/batch/providers/anthropic.js.map +1 -0
- package/dist/batch/providers/bedrock.d.ts +31 -0
- package/dist/batch/providers/bedrock.d.ts.map +1 -0
- package/dist/batch/providers/bedrock.js +42 -0
- package/dist/batch/providers/bedrock.js.map +1 -0
- package/dist/batch/providers/harness.d.ts +36 -0
- package/dist/batch/providers/harness.d.ts.map +1 -0
- package/dist/batch/providers/harness.js +65 -0
- package/dist/batch/providers/harness.js.map +1 -0
- package/dist/batch/providers/harness.test.d.ts +7 -0
- package/dist/batch/providers/harness.test.d.ts.map +1 -0
- package/dist/batch/providers/harness.test.js +84 -0
- package/dist/batch/providers/harness.test.js.map +1 -0
- package/dist/batch/providers/index.d.ts +82 -0
- package/dist/batch/providers/index.d.ts.map +1 -0
- package/dist/batch/providers/index.js +82 -0
- package/dist/batch/providers/index.js.map +1 -0
- package/dist/batch/providers/ollama.d.ts +68 -0
- package/dist/batch/providers/ollama.d.ts.map +1 -0
- package/dist/batch/providers/ollama.js +108 -0
- package/dist/batch/providers/ollama.js.map +1 -0
- package/dist/batch/response-parser.d.ts +45 -0
- package/dist/batch/response-parser.d.ts.map +1 -0
- package/dist/batch/response-parser.js +161 -0
- package/dist/batch/response-parser.js.map +1 -0
- package/dist/batch/types.d.ts +87 -0
- package/dist/batch/types.d.ts.map +1 -0
- package/dist/batch/types.js +8 -0
- package/dist/batch/types.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1037 -0
- package/dist/cli.js.map +1 -0
- package/dist/examples/a2a-assessment.d.ts +20 -0
- package/dist/examples/a2a-assessment.d.ts.map +1 -0
- package/dist/examples/a2a-assessment.js +115 -0
- package/dist/examples/a2a-assessment.js.map +1 -0
- package/dist/examples/batch-assessment.d.ts +18 -0
- package/dist/examples/batch-assessment.d.ts.map +1 -0
- package/dist/examples/batch-assessment.js +112 -0
- package/dist/examples/batch-assessment.js.map +1 -0
- package/dist/examples/batch-submission-flow.d.ts +2 -0
- package/dist/examples/batch-submission-flow.d.ts.map +1 -0
- package/dist/examples/batch-submission-flow.js +279 -0
- package/dist/examples/batch-submission-flow.js.map +1 -0
- package/dist/examples/demo-agent-definition.json +58 -0
- package/dist/examples/langfuse-assessment.d.ts +2 -0
- package/dist/examples/langfuse-assessment.d.ts.map +1 -0
- package/dist/examples/langfuse-assessment.js +46 -0
- package/dist/examples/langfuse-assessment.js.map +1 -0
- package/dist/examples/model-eval.d.ts +25 -0
- package/dist/examples/model-eval.d.ts.map +1 -0
- package/dist/examples/model-eval.js +94 -0
- package/dist/examples/model-eval.js.map +1 -0
- package/dist/examples/openclaw-assessment.d.ts +17 -0
- package/dist/examples/openclaw-assessment.d.ts.map +1 -0
- package/dist/examples/openclaw-assessment.js +79 -0
- package/dist/examples/openclaw-assessment.js.map +1 -0
- package/dist/examples/simple-assessment.d.ts +2 -0
- package/dist/examples/simple-assessment.d.ts.map +1 -0
- package/dist/examples/simple-assessment.js +146 -0
- package/dist/examples/simple-assessment.js.map +1 -0
- package/dist/examples/threat-model-demo.d.ts +2 -0
- package/dist/examples/threat-model-demo.d.ts.map +1 -0
- package/dist/examples/threat-model-demo.js +103 -0
- package/dist/examples/threat-model-demo.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +155 -0
- package/dist/index.js.map +1 -0
- package/dist/output/likec4-generator.d.ts +43 -0
- package/dist/output/likec4-generator.d.ts.map +1 -0
- package/dist/output/likec4-generator.js +287 -0
- package/dist/output/likec4-generator.js.map +1 -0
- package/dist/output/slot-filler.d.ts +6 -0
- package/dist/output/slot-filler.d.ts.map +1 -0
- package/dist/output/slot-filler.js +71 -0
- package/dist/output/slot-filler.js.map +1 -0
- package/dist/output/tc-assembler.d.ts +30 -0
- package/dist/output/tc-assembler.d.ts.map +1 -0
- package/dist/output/tc-assembler.js +227 -0
- package/dist/output/tc-assembler.js.map +1 -0
- package/dist/output/tc-types.d.ts +41 -0
- package/dist/output/tc-types.d.ts.map +1 -0
- package/dist/output/tc-types.js +5 -0
- package/dist/output/tc-types.js.map +1 -0
- package/dist/output/tc-validator.test.d.ts +9 -0
- package/dist/output/tc-validator.test.d.ts.map +1 -0
- package/dist/output/tc-validator.test.js +194 -0
- package/dist/output/tc-validator.test.js.map +1 -0
- package/dist/providers/anthropic.d.ts +9 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +30 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +9 -0
- package/dist/providers/bedrock.d.ts.map +1 -0
- package/dist/providers/bedrock.js +38 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +14 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/types.d.ts +12 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +5 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/references/acsc-risks.d.ts +18 -0
- package/dist/references/acsc-risks.d.ts.map +1 -0
- package/dist/references/acsc-risks.js +232 -0
- package/dist/references/acsc-risks.js.map +1 -0
- package/dist/references/aicm.d.ts +41 -0
- package/dist/references/aicm.d.ts.map +1 -0
- package/dist/references/aicm.js +447 -0
- package/dist/references/aicm.js.map +1 -0
- package/dist/references/dstg-meaid.d.ts +59 -0
- package/dist/references/dstg-meaid.d.ts.map +1 -0
- package/dist/references/dstg-meaid.js +352 -0
- package/dist/references/dstg-meaid.js.map +1 -0
- package/dist/references/index.d.ts +20 -0
- package/dist/references/index.d.ts.map +1 -0
- package/dist/references/index.js +31 -0
- package/dist/references/index.js.map +1 -0
- package/dist/references/nemoclaw.d.ts +48 -0
- package/dist/references/nemoclaw.d.ts.map +1 -0
- package/dist/references/nemoclaw.js +140 -0
- package/dist/references/nemoclaw.js.map +1 -0
- package/dist/references/nist-ai-rmf.d.ts +31 -0
- package/dist/references/nist-ai-rmf.d.ts.map +1 -0
- package/dist/references/nist-ai-rmf.js +627 -0
- package/dist/references/nist-ai-rmf.js.map +1 -0
- package/dist/references/owasp-agentic-top10.d.ts +50 -0
- package/dist/references/owasp-agentic-top10.d.ts.map +1 -0
- package/dist/references/owasp-agentic-top10.js +202 -0
- package/dist/references/owasp-agentic-top10.js.map +1 -0
- package/dist/references/registry-parity.test.d.ts +17 -0
- package/dist/references/registry-parity.test.d.ts.map +1 -0
- package/dist/references/registry-parity.test.js +87 -0
- package/dist/references/registry-parity.test.js.map +1 -0
- package/dist/references/registry.d.ts +90 -0
- package/dist/references/registry.d.ts.map +1 -0
- package/dist/references/registry.js +29 -0
- package/dist/references/registry.js.map +1 -0
- package/dist/tools/model-eval.d.ts +97 -0
- package/dist/tools/model-eval.d.ts.map +1 -0
- package/dist/tools/model-eval.js +307 -0
- package/dist/tools/model-eval.js.map +1 -0
- package/package.json +105 -0
- package/schemas/threat-composer-v1.schema.json +966 -0
- package/vendor/atlas-release/ATLAS-2026.07.yaml +17877 -0
- package/vendor/atlas-release/README.md +41 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LikeC4 diagram generator for Canopy agent system assessments.
|
|
3
|
+
*
|
|
4
|
+
* Converts AgentDefinition[] + HandoffEdge[] into a LikeC4 DSL string (.c4)
|
|
5
|
+
* representing the full agent system architecture — agents, tools, data stores,
|
|
6
|
+
* external dependencies, and handoff trust relationships.
|
|
7
|
+
*
|
|
8
|
+
* Pure function: no I/O, no side effects. The caller writes the file.
|
|
9
|
+
*
|
|
10
|
+
* Why LikeC4 over plain Mermaid/PlantUML:
|
|
11
|
+
* - Architecture as code: a single `.c4` file is the canonical system description
|
|
12
|
+
* - Multiple views from one model (landscape, per-agent drill-down, deployment)
|
|
13
|
+
* - Fills the gap AWS Threat Composer leaves: TC produces a threat list with no
|
|
14
|
+
* structural diagram of the system being modelled
|
|
15
|
+
* - LikeC4's DSL is designed for AI-assisted generation — LLM-friendly syntax
|
|
16
|
+
*
|
|
17
|
+
* Reference: https://likec4.dev
|
|
18
|
+
*/
|
|
19
|
+
// ── ID helpers ──────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Sanitise a string into a valid LikeC4 identifier.
|
|
22
|
+
* LikeC4 identifiers must match [a-zA-Z_][a-zA-Z0-9_]*.
|
|
23
|
+
*/
|
|
24
|
+
function toId(raw) {
|
|
25
|
+
return raw
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
28
|
+
.replace(/^[0-9]/, "_$&")
|
|
29
|
+
.replace(/^_+|_+$/g, "")
|
|
30
|
+
|| "element";
|
|
31
|
+
}
|
|
32
|
+
function agentId(agent) {
|
|
33
|
+
return `agent_${toId(agent.id)}`;
|
|
34
|
+
}
|
|
35
|
+
function toolId(agentDef, toolName) {
|
|
36
|
+
return `${agentId(agentDef)}_tool_${toId(toolName)}`;
|
|
37
|
+
}
|
|
38
|
+
function depId(depName) {
|
|
39
|
+
return `dep_${toId(depName)}`;
|
|
40
|
+
}
|
|
41
|
+
function dataSourceId(dsName) {
|
|
42
|
+
return `ds_${toId(dsName)}`;
|
|
43
|
+
}
|
|
44
|
+
// ── Indent helper ────────────────────────────────────────────────────────────
|
|
45
|
+
function indent(level) {
|
|
46
|
+
return " ".repeat(level);
|
|
47
|
+
}
|
|
48
|
+
// ── Escaping ─────────────────────────────────────────────────────────────────
|
|
49
|
+
function q(s) {
|
|
50
|
+
return `'${s.replace(/'/g, "\\'")}'`;
|
|
51
|
+
}
|
|
52
|
+
// ── Section builders ─────────────────────────────────────────────────────────
|
|
53
|
+
function buildSpecification() {
|
|
54
|
+
return [
|
|
55
|
+
"specification {",
|
|
56
|
+
`${indent(1)}// Standard C4 model element kinds`,
|
|
57
|
+
`${indent(1)}element actor`,
|
|
58
|
+
`${indent(1)}element system`,
|
|
59
|
+
`${indent(1)}element container`,
|
|
60
|
+
``,
|
|
61
|
+
`${indent(1)}// Canopy-specific element kinds for agentic AI systems`,
|
|
62
|
+
`${indent(1)}element agent {`,
|
|
63
|
+
`${indent(2)}style {`,
|
|
64
|
+
`${indent(3)}shape rectangle`,
|
|
65
|
+
`${indent(3)}color secondary`,
|
|
66
|
+
`${indent(2)}}`,
|
|
67
|
+
`${indent(1)}}`,
|
|
68
|
+
`${indent(1)}element tool {`,
|
|
69
|
+
`${indent(2)}style {`,
|
|
70
|
+
`${indent(3)}shape component`,
|
|
71
|
+
`${indent(3)}color muted`,
|
|
72
|
+
`${indent(2)}}`,
|
|
73
|
+
`${indent(1)}}`,
|
|
74
|
+
`${indent(1)}element datastore {`,
|
|
75
|
+
`${indent(2)}style {`,
|
|
76
|
+
`${indent(3)}shape storage`,
|
|
77
|
+
`${indent(3)}color green`,
|
|
78
|
+
`${indent(2)}}`,
|
|
79
|
+
`${indent(1)}}`,
|
|
80
|
+
`${indent(1)}element external {`,
|
|
81
|
+
`${indent(2)}style {`,
|
|
82
|
+
`${indent(3)}shape rectangle`,
|
|
83
|
+
`${indent(3)}color amber`,
|
|
84
|
+
`${indent(2)}}`,
|
|
85
|
+
`${indent(1)}}`,
|
|
86
|
+
"}",
|
|
87
|
+
].join("\n");
|
|
88
|
+
}
|
|
89
|
+
function buildAgentBlock(agent, i) {
|
|
90
|
+
const lines = [];
|
|
91
|
+
const aid = agentId(agent);
|
|
92
|
+
const autonomy = agent.autonomyLevel;
|
|
93
|
+
const desc = [
|
|
94
|
+
`Model: ${agent.model}`,
|
|
95
|
+
`Deployment: ${agent.deploymentContext}`,
|
|
96
|
+
`Complexity: ${agent.modelComplexity}`,
|
|
97
|
+
autonomy ? `Autonomy: ${autonomy}` : null,
|
|
98
|
+
].filter(Boolean).join(" | ");
|
|
99
|
+
lines.push(`${indent(2)}${aid} = agent ${q(agent.name)} {`);
|
|
100
|
+
lines.push(`${indent(3)}description ${q(desc)}`);
|
|
101
|
+
// Tools as nested elements
|
|
102
|
+
if (agent.tools.length > 0) {
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(`${indent(3)}// Tools`);
|
|
105
|
+
for (const tool of agent.tools) {
|
|
106
|
+
const tid = toolId(agent, tool.name);
|
|
107
|
+
const isDangerous = tool.dangerous === true;
|
|
108
|
+
lines.push(`${indent(3)}${tid} = tool ${q(tool.name)} {`);
|
|
109
|
+
if (tool.description) {
|
|
110
|
+
lines.push(`${indent(4)}description ${q(tool.description)}`);
|
|
111
|
+
}
|
|
112
|
+
if (isDangerous) {
|
|
113
|
+
lines.push(`${indent(4)}style {`);
|
|
114
|
+
lines.push(`${indent(5)}color red`);
|
|
115
|
+
lines.push(`${indent(4)}}`);
|
|
116
|
+
}
|
|
117
|
+
lines.push(`${indent(3)}}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
lines.push(`${indent(2)}}`);
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
function buildModel(agents, handoffs, applicationName) {
|
|
124
|
+
const lines = [];
|
|
125
|
+
const systemId = toId(applicationName) || "system";
|
|
126
|
+
// Collect all unique dependencies and data sources across all agents
|
|
127
|
+
const allDeps = new Map(); // id -> name
|
|
128
|
+
const allDataSources = new Map(); // id -> name
|
|
129
|
+
for (const agent of agents) {
|
|
130
|
+
for (const dep of agent.dependencies ?? []) {
|
|
131
|
+
allDeps.set(depId(dep), dep);
|
|
132
|
+
}
|
|
133
|
+
for (const ds of agent.dataSources ?? []) {
|
|
134
|
+
allDataSources.set(dataSourceId(ds), ds);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
lines.push("model {");
|
|
138
|
+
lines.push(`${indent(1)}// Human actors interacting with the system`);
|
|
139
|
+
lines.push(`${indent(1)}user = actor 'User'`);
|
|
140
|
+
lines.push("");
|
|
141
|
+
lines.push(`${indent(1)}${systemId} = system ${q(applicationName)} {`);
|
|
142
|
+
lines.push("");
|
|
143
|
+
lines.push(`${indent(2)}// AI Agents`);
|
|
144
|
+
for (let i = 0; i < agents.length; i++) {
|
|
145
|
+
lines.push(buildAgentBlock(agents[i], i));
|
|
146
|
+
if (i < agents.length - 1)
|
|
147
|
+
lines.push("");
|
|
148
|
+
}
|
|
149
|
+
// Dependency elements (shared across agents, defined once at system level)
|
|
150
|
+
if (allDeps.size > 0) {
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push(`${indent(2)}// External dependencies and services`);
|
|
153
|
+
for (const [id, name] of allDeps) {
|
|
154
|
+
lines.push(`${indent(2)}${id} = external ${q(name)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Data source elements
|
|
158
|
+
if (allDataSources.size > 0) {
|
|
159
|
+
lines.push("");
|
|
160
|
+
lines.push(`${indent(2)}// Data sources`);
|
|
161
|
+
for (const [id, name] of allDataSources) {
|
|
162
|
+
lines.push(`${indent(2)}${id} = datastore ${q(name)}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
lines.push(`${indent(1)}}`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
// Relationships — agent → tools (invocation)
|
|
168
|
+
lines.push(`${indent(1)}// Agent tool invocations`);
|
|
169
|
+
for (const agent of agents) {
|
|
170
|
+
for (const tool of agent.tools) {
|
|
171
|
+
const label = tool.dangerous
|
|
172
|
+
? "'invokes (dangerous)'"
|
|
173
|
+
: "'invokes'";
|
|
174
|
+
lines.push(`${indent(1)}${agentId(agent)}.${toolId(agent, tool.name)} -> ${agentId(agent)} ${label}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Relationships — agent → external deps
|
|
178
|
+
if (allDeps.size > 0) {
|
|
179
|
+
lines.push("");
|
|
180
|
+
lines.push(`${indent(1)}// Agent → dependency relationships`);
|
|
181
|
+
for (const agent of agents) {
|
|
182
|
+
for (const dep of agent.dependencies ?? []) {
|
|
183
|
+
lines.push(`${indent(1)}${agentId(agent)} -> ${depId(dep)} 'depends on'`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Relationships — agent → data sources
|
|
188
|
+
if (allDataSources.size > 0) {
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push(`${indent(1)}// Agent → data source relationships`);
|
|
191
|
+
for (const agent of agents) {
|
|
192
|
+
for (const ds of agent.dataSources ?? []) {
|
|
193
|
+
lines.push(`${indent(1)}${agentId(agent)} -> ${dataSourceId(ds)} 'reads'`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Relationships — user → agents
|
|
198
|
+
lines.push("");
|
|
199
|
+
lines.push(`${indent(1)}// User entry points`);
|
|
200
|
+
// User talks to the first agent (or all if no handoffs)
|
|
201
|
+
const entryAgents = handoffs.length > 0
|
|
202
|
+
? agents.filter((a) => !handoffs.some((h) => h.toAgentId === a.id))
|
|
203
|
+
: agents;
|
|
204
|
+
for (const agent of entryAgents) {
|
|
205
|
+
lines.push(`${indent(1)}user -> ${agentId(agent)} 'sends request'`);
|
|
206
|
+
}
|
|
207
|
+
// Relationships — handoffs (agent → agent)
|
|
208
|
+
if (handoffs.length > 0) {
|
|
209
|
+
lines.push("");
|
|
210
|
+
lines.push(`${indent(1)}// Agent handoff relationships (implicit trust grants)`);
|
|
211
|
+
for (const handoff of handoffs) {
|
|
212
|
+
const from = `agent_${toId(handoff.fromAgentId)}`;
|
|
213
|
+
const to = `agent_${toId(handoff.toAgentId)}`;
|
|
214
|
+
lines.push(`${indent(1)}${from} -> ${to} 'handoff (implicit trust)'`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
lines.push("}");
|
|
218
|
+
return lines.join("\n");
|
|
219
|
+
}
|
|
220
|
+
function buildViews(agents, applicationName) {
|
|
221
|
+
const systemId = toId(applicationName) || "system";
|
|
222
|
+
const lines = [];
|
|
223
|
+
lines.push("views {");
|
|
224
|
+
// Landscape — everything
|
|
225
|
+
lines.push(`${indent(1)}view index {`);
|
|
226
|
+
lines.push(`${indent(2)}title ${q(`${applicationName} — Landscape`)}`);
|
|
227
|
+
lines.push(`${indent(2)}include *`);
|
|
228
|
+
lines.push(`${indent(1)}}`);
|
|
229
|
+
lines.push("");
|
|
230
|
+
// System drill-down
|
|
231
|
+
lines.push(`${indent(1)}view of ${systemId} {`);
|
|
232
|
+
lines.push(`${indent(2)}title ${q(`${applicationName} — Agent System`)}`);
|
|
233
|
+
lines.push(`${indent(2)}include *`);
|
|
234
|
+
lines.push(`${indent(1)}}`);
|
|
235
|
+
// Per-agent drill-down views
|
|
236
|
+
for (const agent of agents) {
|
|
237
|
+
lines.push("");
|
|
238
|
+
lines.push(`${indent(1)}view of ${agentId(agent)} {`);
|
|
239
|
+
lines.push(`${indent(2)}title ${q(`${agent.name} — Agent Detail`)}`);
|
|
240
|
+
lines.push(`${indent(2)}include *`);
|
|
241
|
+
lines.push(`${indent(1)}}`);
|
|
242
|
+
}
|
|
243
|
+
lines.push("}");
|
|
244
|
+
return lines.join("\n");
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Generate a LikeC4 DSL string from Canopy agent definitions.
|
|
248
|
+
*
|
|
249
|
+
* @returns A string containing a complete LikeC4 `.c4` file.
|
|
250
|
+
* Write it to `<outputDir>/<applicationName>.c4` or `architecture.c4`.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* const c4 = generateLikeC4({ agents, handoffs, applicationName: "My System" });
|
|
254
|
+
* await Bun.write("output/architecture.c4", c4);
|
|
255
|
+
* // Then: npx likec4 serve output/architecture.c4
|
|
256
|
+
*/
|
|
257
|
+
export function generateLikeC4(options) {
|
|
258
|
+
const { agents, handoffs = [], applicationName } = options;
|
|
259
|
+
const extended = agents;
|
|
260
|
+
const sections = [
|
|
261
|
+
`// LikeC4 architecture model generated by Canopy`,
|
|
262
|
+
`// Application: ${applicationName}`,
|
|
263
|
+
`// Generated: ${new Date().toISOString()}`,
|
|
264
|
+
`// Reference: https://likec4.dev`,
|
|
265
|
+
`//`,
|
|
266
|
+
`// To view: npx likec4 serve <this-file>`,
|
|
267
|
+
`// To export PNG: npx likec4 export png -o diagrams/ <this-file>`,
|
|
268
|
+
"",
|
|
269
|
+
buildSpecification(),
|
|
270
|
+
"",
|
|
271
|
+
buildModel(extended, handoffs, applicationName),
|
|
272
|
+
"",
|
|
273
|
+
buildViews(extended, applicationName),
|
|
274
|
+
];
|
|
275
|
+
return sections.join("\n");
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Derive a safe output filename from the application name.
|
|
279
|
+
* e.g. "My Agent System" → "my-agent-system.c4"
|
|
280
|
+
*/
|
|
281
|
+
export function likec4Filename(applicationName) {
|
|
282
|
+
return (applicationName
|
|
283
|
+
.toLowerCase()
|
|
284
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
285
|
+
.replace(/^-+|-+$/g, "") || "architecture") + ".c4";
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=likec4-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"likec4-generator.js","sourceRoot":"","sources":["../../src/output/likec4-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InferenceProvider } from "../providers/types";
|
|
2
|
+
import type { AtlasTechnique } from "../aivss/atlas/loader";
|
|
3
|
+
import type { AgentDefinition, MetricCode, SeverityLabel, ThreatSlots } from "../aivss/types";
|
|
4
|
+
export type { ThreatSlots } from "../aivss/types.js";
|
|
5
|
+
export declare function fillThreatSlots(provider: InferenceProvider, agent: AgentDefinition, technique: AtlasTechnique, tactic: string, metric: MetricCode, subCategory: string, severityLabel: SeverityLabel): Promise<ThreatSlots>;
|
|
6
|
+
//# sourceMappingURL=slot-filler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot-filler.d.ts","sourceRoot":"","sources":["../../src/output/slot-filler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,aAAa,EACb,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAyDrD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,cAAc,EACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,WAAW,CAAC,CAkCtB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { SEVERITY_VALUES } from "../aivss/types";
|
|
2
|
+
const SYSTEM_PROMPT = `You are a security analyst filling structured threat statement fields for a Threat Composer report.
|
|
3
|
+
You must return valid JSON only — no markdown, no explanation.
|
|
4
|
+
Use precise, lowercase-first-word phrasing for prerequisites, threatAction, threatImpact, impactedAssets.
|
|
5
|
+
Base all fields on the agent profile and the specific ATLAS technique provided.`;
|
|
6
|
+
function buildUserPrompt(agent, technique, tactic, metric, subCategory, severityLabel) {
|
|
7
|
+
const score = SEVERITY_VALUES[severityLabel];
|
|
8
|
+
const toolList = agent.tools.length > 0 ? agent.tools.map((t) => t.name).join(", ") : "none";
|
|
9
|
+
return `Agent: ${agent.name}
|
|
10
|
+
Model: ${agent.model}
|
|
11
|
+
System prompt summary: ${agent.systemPrompt.slice(0, 200)}
|
|
12
|
+
Tools: ${toolList}
|
|
13
|
+
Deployment context: ${agent.deploymentContext}
|
|
14
|
+
|
|
15
|
+
ATLAS Technique: ${technique.id} — ${technique.name}
|
|
16
|
+
Tactic: ${tactic}
|
|
17
|
+
Description: ${technique.description.slice(0, 300)}
|
|
18
|
+
AIVSS Metric: ${metric} — Sub-category: ${subCategory} — Severity: ${severityLabel} (${score})
|
|
19
|
+
|
|
20
|
+
Fill these threat statement slots:
|
|
21
|
+
{
|
|
22
|
+
"threatSource": "The entity taking action (e.g. 'An internet-based adversary', 'A malicious insider')",
|
|
23
|
+
"prerequisites": "lowercase — conditions needed (e.g. 'with API access to the agent endpoint')",
|
|
24
|
+
"threatAction": "lowercase — what the adversary does (e.g. 'inject indirect prompt instructions via tool output')",
|
|
25
|
+
"threatImpact": "lowercase — what happens to the system (e.g. 'exfiltrate customer order records')",
|
|
26
|
+
"impactedGoal": ["confidentiality" | "integrity" | "availability" — pick relevant ones],
|
|
27
|
+
"impactedAssets": ["lowercase asset names from the agent profile"]
|
|
28
|
+
}
|
|
29
|
+
Return JSON only.`;
|
|
30
|
+
}
|
|
31
|
+
function stripFences(raw) {
|
|
32
|
+
const trimmed = raw.trim();
|
|
33
|
+
if (trimmed.startsWith("```")) {
|
|
34
|
+
return trimmed
|
|
35
|
+
.replace(/^```(?:json)?\s*/i, "")
|
|
36
|
+
.replace(/```\s*$/m, "")
|
|
37
|
+
.trim();
|
|
38
|
+
}
|
|
39
|
+
return trimmed;
|
|
40
|
+
}
|
|
41
|
+
function asStringArray(value) {
|
|
42
|
+
if (!Array.isArray(value))
|
|
43
|
+
return [];
|
|
44
|
+
return value.filter((v) => typeof v === "string");
|
|
45
|
+
}
|
|
46
|
+
export async function fillThreatSlots(provider, agent, technique, tactic, metric, subCategory, severityLabel) {
|
|
47
|
+
const raw = await provider.complete(SYSTEM_PROMPT, buildUserPrompt(agent, technique, tactic, metric, subCategory, severityLabel));
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(stripFences(raw));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
54
|
+
throw new Error(`Slot-filler returned non-JSON for ${agent.id}/${technique.id}: ${message}. Raw: ${raw.slice(0, 200)}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
threatSource: typeof parsed.threatSource === "string"
|
|
58
|
+
? parsed.threatSource
|
|
59
|
+
: "An unidentified adversary",
|
|
60
|
+
prerequisites: typeof parsed.prerequisites === "string" ? parsed.prerequisites : "",
|
|
61
|
+
threatAction: typeof parsed.threatAction === "string"
|
|
62
|
+
? parsed.threatAction
|
|
63
|
+
: `exploit ${metric} weaknesses`,
|
|
64
|
+
threatImpact: typeof parsed.threatImpact === "string"
|
|
65
|
+
? parsed.threatImpact
|
|
66
|
+
: "compromise agent behavior",
|
|
67
|
+
impactedGoal: asStringArray(parsed.impactedGoal),
|
|
68
|
+
impactedAssets: asStringArray(parsed.impactedAssets),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=slot-filler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slot-filler.js","sourceRoot":"","sources":["../../src/output/slot-filler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { CoverageEntry } from "../aivss/atlas/coverage-matrix";
|
|
2
|
+
import type { AtlasData } from "../aivss/atlas/loader";
|
|
3
|
+
import type { AgentDefinition, MetricAssessmentResponse, SeverityLabel } from "../aivss/types";
|
|
4
|
+
import type { HandoffEdge } from "../adapters/types";
|
|
5
|
+
import { type ScoringMode } from "../aivss/scoring/scoring-mode.js";
|
|
6
|
+
import type { ThreatSlots } from "./slot-filler";
|
|
7
|
+
import type { DataExchangeFormat } from "./tc-types";
|
|
8
|
+
export interface AssessmentBundle {
|
|
9
|
+
agents: AgentDefinition[];
|
|
10
|
+
handoffs: HandoffEdge[];
|
|
11
|
+
metricResponses: MetricAssessmentResponse[];
|
|
12
|
+
atlasData: AtlasData;
|
|
13
|
+
coverageMatrix: readonly CoverageEntry[];
|
|
14
|
+
filledSlots: Map<string, ThreatSlots>;
|
|
15
|
+
severityBySubCategory: Map<string, SeverityLabel>;
|
|
16
|
+
applicationName: string;
|
|
17
|
+
/**
|
|
18
|
+
* How the severities above were produced. Stamped per-threat and into the
|
|
19
|
+
* audit trail so a reader can tell whether these scores are reproducible.
|
|
20
|
+
*
|
|
21
|
+
* Defaults to "deterministic": it is the only mode that cannot silently vary
|
|
22
|
+
* between runs, so it is the only safe assumption when a caller does not
|
|
23
|
+
* say. A caller using model or harness inference must declare it.
|
|
24
|
+
*/
|
|
25
|
+
scoringMode?: ScoringMode;
|
|
26
|
+
}
|
|
27
|
+
export declare function slotKey(agentId: string, techniqueId: string, metric: string, subCategory: string): string;
|
|
28
|
+
export declare function severityKey(agentId: string, metric: string, subCategory: string): string;
|
|
29
|
+
export declare function assembleTCFormat(bundle: AssessmentBundle): DataExchangeFormat;
|
|
30
|
+
//# sourceMappingURL=tc-assembler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tc-assembler.d.ts","sourceRoot":"","sources":["../../src/output/tc-assembler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EACV,eAAe,EACf,wBAAwB,EACxB,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAYvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,eAAe,EAAE,wBAAwB,EAAE,CAAC;IAC5C,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,SAAS,aAAa,EAAE,CAAC;IAEzC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEtC,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAClD,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AA6BD,wBAAgB,OAAO,CACrB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAeD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,kBAAkB,CAwL7E"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { SEVERITY_VALUES } from "../aivss/types";
|
|
4
|
+
import { FRAMEWORK_REGISTRY } from "../references/index.js";
|
|
5
|
+
import { SCORING_MODE_INFO } from "../aivss/scoring/scoring-mode.js";
|
|
6
|
+
// TC native keys that must NOT be prefixed with "custom:".
|
|
7
|
+
const TC_NATIVE_KEYS = new Set(["Priority", "Comments", "STRIDE", "source"]);
|
|
8
|
+
// Prefix all non-native, non-prefixed keys with "custom:".
|
|
9
|
+
const cm = (entries) => entries.map((e) => ({
|
|
10
|
+
...e,
|
|
11
|
+
key: TC_NATIVE_KEYS.has(e.key) || e.key.startsWith("custom:") ? e.key : `custom:${e.key}`,
|
|
12
|
+
}));
|
|
13
|
+
/**
|
|
14
|
+
* Content-derived UUID.
|
|
15
|
+
*
|
|
16
|
+
* Threat and mitigation ids were `crypto.randomUUID()`, which meant two runs
|
|
17
|
+
* over identical input produced structurally identical but byte-different
|
|
18
|
+
* artifacts. That defeats `diff` as a review tool, makes baselines noisy, and
|
|
19
|
+
* quietly contradicts the promise of a deterministic scoring mode.
|
|
20
|
+
*
|
|
21
|
+
* Deriving the id from a stable content key gives the same threat the same id
|
|
22
|
+
* across runs and machines, while remaining unique per distinct threat. Format
|
|
23
|
+
* is RFC-4122-shaped (version nibble 5, correct variant bits) so consumers
|
|
24
|
+
* expecting a UUID still parse it — the bytes come from SHA-256 of the key
|
|
25
|
+
* rather than a random source, which is exactly the UUIDv5 construction with a
|
|
26
|
+
* fixed namespace.
|
|
27
|
+
*/
|
|
28
|
+
function stableId(key) {
|
|
29
|
+
const h = createHash("sha256").update(`canopy::${key}`).digest("hex");
|
|
30
|
+
const variant = ((parseInt(h.slice(16, 17), 16) & 0x3) | 0x8).toString(16);
|
|
31
|
+
return [
|
|
32
|
+
h.slice(0, 8),
|
|
33
|
+
h.slice(8, 12),
|
|
34
|
+
`5${h.slice(13, 16)}`,
|
|
35
|
+
`${variant}${h.slice(17, 20)}`,
|
|
36
|
+
h.slice(20, 32),
|
|
37
|
+
].join("-");
|
|
38
|
+
}
|
|
39
|
+
export function slotKey(agentId, techniqueId, metric, subCategory) {
|
|
40
|
+
return `${agentId}::${techniqueId}::${metric}::${subCategory}`;
|
|
41
|
+
}
|
|
42
|
+
export function severityKey(agentId, metric, subCategory) {
|
|
43
|
+
return `${agentId}::${metric}::${subCategory}`;
|
|
44
|
+
}
|
|
45
|
+
function priorityFor(severity) {
|
|
46
|
+
if (severity === "Critical" || severity === "High")
|
|
47
|
+
return "High";
|
|
48
|
+
if (severity === "Medium")
|
|
49
|
+
return "Medium";
|
|
50
|
+
return "Low";
|
|
51
|
+
}
|
|
52
|
+
function buildStatement(slots, agentName) {
|
|
53
|
+
const prereqs = slots.prerequisites ? ` ${slots.prerequisites}` : "";
|
|
54
|
+
const action = slots.threatAction || "exploit a weakness";
|
|
55
|
+
const impact = slots.threatImpact || "compromise the agent";
|
|
56
|
+
return `${slots.threatSource}${prereqs} can ${action} ${agentName} to ${impact}`;
|
|
57
|
+
}
|
|
58
|
+
export function assembleTCFormat(bundle) {
|
|
59
|
+
// Absent an explicit declaration, assume the reproducible mode — see the
|
|
60
|
+
// scoringMode field docs on AssessmentBundle.
|
|
61
|
+
const scoringMode = bundle.scoringMode ?? "deterministic";
|
|
62
|
+
const threats = [];
|
|
63
|
+
const mitigations = [];
|
|
64
|
+
const mitigationLinks = [];
|
|
65
|
+
const mitigationIdByAtlasId = new Map();
|
|
66
|
+
const usedTechniqueIds = new Set();
|
|
67
|
+
let threatCounter = 0;
|
|
68
|
+
let mitigationCounter = 0;
|
|
69
|
+
// 1. Threats from agent × metric × technique slot fills
|
|
70
|
+
for (const agent of bundle.agents) {
|
|
71
|
+
for (const entry of bundle.coverageMatrix) {
|
|
72
|
+
const severity = bundle.severityBySubCategory.get(severityKey(agent.id, entry.metric, entry.subCategory)) ?? "None";
|
|
73
|
+
if (severity === "None")
|
|
74
|
+
continue;
|
|
75
|
+
for (const techniqueId of entry.atlasTechniqueIds) {
|
|
76
|
+
const slots = bundle.filledSlots.get(slotKey(agent.id, techniqueId, entry.metric, entry.subCategory));
|
|
77
|
+
if (!slots)
|
|
78
|
+
continue;
|
|
79
|
+
const technique = bundle.atlasData.techniqueById.get(techniqueId);
|
|
80
|
+
if (!technique)
|
|
81
|
+
continue;
|
|
82
|
+
usedTechniqueIds.add(techniqueId);
|
|
83
|
+
const tactic = technique.tactics[0] ?? "unspecified";
|
|
84
|
+
const score = SEVERITY_VALUES[severity];
|
|
85
|
+
const metadata = [
|
|
86
|
+
{ key: "aivss-metric", value: entry.metric },
|
|
87
|
+
{ key: "aivss-sub-category", value: entry.subCategory },
|
|
88
|
+
{ key: "aivss-score", value: score.toFixed(2) },
|
|
89
|
+
{ key: "aivss-severity", value: severity },
|
|
90
|
+
{ key: "atlas-technique-id", value: technique.id },
|
|
91
|
+
{ key: "atlas-technique-name", value: technique.name },
|
|
92
|
+
{ key: "atlas-tactic", value: tactic },
|
|
93
|
+
{ key: "deployment-context", value: agent.deploymentContext },
|
|
94
|
+
{ key: "agent-id", value: agent.id },
|
|
95
|
+
{ key: "agent-name", value: agent.name },
|
|
96
|
+
{ key: "Priority", value: priorityFor(severity) },
|
|
97
|
+
// Provenance: how this severity was arrived at. A consumer gating CI
|
|
98
|
+
// on these numbers needs to know whether re-running can move them.
|
|
99
|
+
{ key: "scoring-mode", value: scoringMode },
|
|
100
|
+
// Cross-framework annotations — each registered adapter contributes its
|
|
101
|
+
// top matching risk for this AIVSS metric as {framework}-risk-id / category tags.
|
|
102
|
+
...FRAMEWORK_REGISTRY.flatMap((adapter) => {
|
|
103
|
+
const risk = adapter.getRisksForMetric(entry.metric)[0];
|
|
104
|
+
return risk
|
|
105
|
+
? [
|
|
106
|
+
{ key: `${adapter.citation.id}-risk-id`, value: risk.id },
|
|
107
|
+
{ key: `${adapter.citation.id}-risk-category`, value: risk.category },
|
|
108
|
+
{ key: `${adapter.citation.id}-risk-type`, value: risk.riskType },
|
|
109
|
+
]
|
|
110
|
+
: [];
|
|
111
|
+
}),
|
|
112
|
+
];
|
|
113
|
+
threatCounter += 1;
|
|
114
|
+
threats.push({
|
|
115
|
+
id: stableId(`threat::${agent.id}::${entry.metric}::${entry.subCategory}::${technique.id}`),
|
|
116
|
+
numericId: threatCounter,
|
|
117
|
+
displayOrder: threatCounter,
|
|
118
|
+
threatSource: slots.threatSource,
|
|
119
|
+
prerequisites: slots.prerequisites,
|
|
120
|
+
threatAction: slots.threatAction,
|
|
121
|
+
threatImpact: slots.threatImpact,
|
|
122
|
+
impactedGoal: slots.impactedGoal,
|
|
123
|
+
impactedAssets: slots.impactedAssets,
|
|
124
|
+
statement: buildStatement(slots, agent.name),
|
|
125
|
+
status: "threatIdentified",
|
|
126
|
+
metadata: cm(metadata),
|
|
127
|
+
tags: [entry.metric, technique.id, "ai-threat-model"],
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 2. Synthesised handoff threats — implicit trust grants between agents
|
|
133
|
+
for (const edge of bundle.handoffs) {
|
|
134
|
+
threatCounter += 1;
|
|
135
|
+
const handoffTechniqueId = "AML.T0080.000";
|
|
136
|
+
usedTechniqueIds.add(handoffTechniqueId);
|
|
137
|
+
threats.push({
|
|
138
|
+
id: stableId(`handoff::${edge.fromAgentName}::${edge.toAgentName}::${handoffTechniqueId}`),
|
|
139
|
+
numericId: threatCounter,
|
|
140
|
+
displayOrder: threatCounter,
|
|
141
|
+
threatSource: "An upstream agent",
|
|
142
|
+
prerequisites: `with control over ${edge.fromAgentName}'s output`,
|
|
143
|
+
threatAction: `inject poisoned context into ${edge.toAgentName} via agent handoff`,
|
|
144
|
+
threatImpact: `cause ${edge.toAgentName} to perform unintended actions`,
|
|
145
|
+
impactedGoal: ["integrity"],
|
|
146
|
+
impactedAssets: [edge.toAgentName.toLowerCase()],
|
|
147
|
+
statement: `An upstream agent with control over ${edge.fromAgentName}'s output can inject poisoned context into ${edge.toAgentName} via agent handoff to cause ${edge.toAgentName} to perform unintended actions`,
|
|
148
|
+
status: "threatIdentified",
|
|
149
|
+
metadata: cm([
|
|
150
|
+
{ key: "aivss-metric", value: "CS" },
|
|
151
|
+
{ key: "atlas-technique-id", value: handoffTechniqueId },
|
|
152
|
+
{ key: "atlas-tactic", value: "initial-access" },
|
|
153
|
+
{ key: "from-agent", value: edge.fromAgentName },
|
|
154
|
+
{ key: "to-agent", value: edge.toAgentName },
|
|
155
|
+
{ key: "Priority", value: "High" },
|
|
156
|
+
{ key: "scoring-mode", value: scoringMode },
|
|
157
|
+
]),
|
|
158
|
+
tags: ["CS", handoffTechniqueId, "agent-handoff", "ai-threat-model"],
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// 3. Mitigations for techniques referenced by any emitted threat.
|
|
162
|
+
// Note: the upstream ATLAS loader normalises mitigation.techniques to whatever
|
|
163
|
+
// the YAML provides — recent ATLAS YAML uses {id, use} objects, not bare
|
|
164
|
+
// strings. We read .id off each entry defensively to handle both shapes.
|
|
165
|
+
const mitigationsForTechnique = (techniqueId) => bundle.atlasData.mitigations.filter((m) => m.techniques.some((t) => {
|
|
166
|
+
if (typeof t === "string")
|
|
167
|
+
return t === techniqueId;
|
|
168
|
+
if (t && typeof t === "object")
|
|
169
|
+
return t.id === techniqueId;
|
|
170
|
+
return false;
|
|
171
|
+
}));
|
|
172
|
+
for (const techniqueId of usedTechniqueIds) {
|
|
173
|
+
const atlasMitigations = mitigationsForTechnique(techniqueId);
|
|
174
|
+
for (const mitigation of atlasMitigations) {
|
|
175
|
+
let mitigationId = mitigationIdByAtlasId.get(mitigation.id);
|
|
176
|
+
if (!mitigationId) {
|
|
177
|
+
mitigationId = stableId(`mitigation::${mitigation.id}`);
|
|
178
|
+
mitigationIdByAtlasId.set(mitigation.id, mitigationId);
|
|
179
|
+
mitigationCounter += 1;
|
|
180
|
+
mitigations.push({
|
|
181
|
+
id: mitigationId,
|
|
182
|
+
numericId: mitigationCounter,
|
|
183
|
+
displayOrder: mitigationCounter,
|
|
184
|
+
content: `${mitigation.id} — ${mitigation.name}: ${mitigation.description}`,
|
|
185
|
+
tags: ["atlas-mitigation", mitigation.id],
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
for (const threat of threats) {
|
|
189
|
+
const techMeta = threat.metadata?.find((m) => m.key === "custom:atlas-technique-id" && m.value === techniqueId);
|
|
190
|
+
if (!techMeta)
|
|
191
|
+
continue;
|
|
192
|
+
mitigationLinks.push({
|
|
193
|
+
mitigationId,
|
|
194
|
+
linkedId: threat.id,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const _require = createRequire(import.meta.url);
|
|
200
|
+
const pkg = _require("../../package.json");
|
|
201
|
+
const generatedAt = new Date().toISOString();
|
|
202
|
+
const versionStamp = [
|
|
203
|
+
`canopy@${pkg.version}`,
|
|
204
|
+
"AIVSS V4.0.8",
|
|
205
|
+
"ATLAS format-6.0.0/2026.07",
|
|
206
|
+
"ACSC 2026-05",
|
|
207
|
+
"DSTG MEAID TR-3786",
|
|
208
|
+
].join(" · ");
|
|
209
|
+
const modeInfo = SCORING_MODE_INFO[scoringMode];
|
|
210
|
+
return {
|
|
211
|
+
schema: 1,
|
|
212
|
+
applicationInfo: {
|
|
213
|
+
name: bundle.applicationName,
|
|
214
|
+
description: `AIVSS V4 + ATLAS threat model for ${bundle.agents.length} agent(s) ` +
|
|
215
|
+
`and ${bundle.handoffs.length} handoff(s).\n\n` +
|
|
216
|
+
`### Canopy Audit Trail\n\n` +
|
|
217
|
+
`Generated: ${generatedAt} \n` +
|
|
218
|
+
`Frameworks: ${versionStamp} \n` +
|
|
219
|
+
`Scoring mode: ${scoringMode} — ${modeInfo.summary} \n` +
|
|
220
|
+
`Reproducible: ${modeInfo.reproducible ? "yes — identical input yields identical scores" : "no — re-running may yield different scores"}`,
|
|
221
|
+
},
|
|
222
|
+
threats,
|
|
223
|
+
mitigations,
|
|
224
|
+
mitigationLinks,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=tc-assembler.js.map
|