@panguard-ai/atr 1.4.3 → 1.5.1

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.
Files changed (274) hide show
  1. package/dist/action-executor.d.ts +44 -0
  2. package/dist/action-executor.d.ts.map +1 -0
  3. package/dist/action-executor.js +130 -0
  4. package/dist/action-executor.js.map +1 -0
  5. package/dist/adapters/default-adapter.d.ts +24 -0
  6. package/dist/adapters/default-adapter.d.ts.map +1 -0
  7. package/dist/adapters/default-adapter.js +51 -0
  8. package/dist/adapters/default-adapter.js.map +1 -0
  9. package/dist/adapters/stdio-adapter.d.ts +30 -0
  10. package/dist/adapters/stdio-adapter.d.ts.map +1 -0
  11. package/dist/adapters/stdio-adapter.js +128 -0
  12. package/dist/adapters/stdio-adapter.js.map +1 -0
  13. package/dist/badge.d.ts +42 -0
  14. package/dist/badge.d.ts.map +1 -0
  15. package/dist/badge.js +163 -0
  16. package/dist/badge.js.map +1 -0
  17. package/dist/capability-extractor.d.ts +35 -0
  18. package/dist/capability-extractor.d.ts.map +1 -0
  19. package/dist/capability-extractor.js +91 -0
  20. package/dist/capability-extractor.js.map +1 -0
  21. package/dist/cli/scan-handler.d.ts +21 -0
  22. package/dist/cli/scan-handler.d.ts.map +1 -0
  23. package/dist/cli/scan-handler.js +276 -0
  24. package/dist/cli/scan-handler.js.map +1 -0
  25. package/dist/cli/tc-pipeline.d.ts +18 -0
  26. package/dist/cli/tc-pipeline.d.ts.map +1 -0
  27. package/dist/cli/tc-pipeline.js +295 -0
  28. package/dist/cli/tc-pipeline.js.map +1 -0
  29. package/dist/cli.d.ts +12 -0
  30. package/dist/cli.d.ts.map +1 -0
  31. package/dist/cli.js +894 -0
  32. package/dist/cli.js.map +1 -0
  33. package/dist/content-hash.d.ts +7 -0
  34. package/dist/content-hash.d.ts.map +1 -0
  35. package/dist/content-hash.js +10 -0
  36. package/dist/content-hash.js.map +1 -0
  37. package/dist/converters/elastic.d.ts +36 -0
  38. package/dist/converters/elastic.d.ts.map +1 -0
  39. package/dist/converters/elastic.js +125 -0
  40. package/dist/converters/elastic.js.map +1 -0
  41. package/dist/converters/generic-regex.d.ts +37 -0
  42. package/dist/converters/generic-regex.d.ts.map +1 -0
  43. package/dist/converters/generic-regex.js +59 -0
  44. package/dist/converters/generic-regex.js.map +1 -0
  45. package/dist/converters/index.d.ts +32 -0
  46. package/dist/converters/index.d.ts.map +1 -0
  47. package/dist/converters/index.js +38 -0
  48. package/dist/converters/index.js.map +1 -0
  49. package/dist/converters/sarif.d.ts +18 -0
  50. package/dist/converters/sarif.d.ts.map +1 -0
  51. package/dist/converters/sarif.js +142 -0
  52. package/dist/converters/sarif.js.map +1 -0
  53. package/dist/converters/splunk.d.ts +19 -0
  54. package/dist/converters/splunk.d.ts.map +1 -0
  55. package/dist/converters/splunk.js +148 -0
  56. package/dist/converters/splunk.js.map +1 -0
  57. package/dist/coverage-analyzer.d.ts +43 -0
  58. package/dist/coverage-analyzer.d.ts.map +1 -0
  59. package/dist/coverage-analyzer.js +329 -0
  60. package/dist/coverage-analyzer.js.map +1 -0
  61. package/dist/embedding/build-corpus.d.ts +15 -0
  62. package/dist/embedding/build-corpus.d.ts.map +1 -0
  63. package/dist/embedding/build-corpus.js +105 -0
  64. package/dist/embedding/build-corpus.js.map +1 -0
  65. package/dist/embedding/model-loader.d.ts +41 -0
  66. package/dist/embedding/model-loader.d.ts.map +1 -0
  67. package/dist/embedding/model-loader.js +90 -0
  68. package/dist/embedding/model-loader.js.map +1 -0
  69. package/dist/embedding/vector-store.d.ts +41 -0
  70. package/dist/embedding/vector-store.d.ts.map +1 -0
  71. package/dist/embedding/vector-store.js +70 -0
  72. package/dist/embedding/vector-store.js.map +1 -0
  73. package/dist/engine.d.ts +222 -0
  74. package/dist/engine.d.ts.map +1 -0
  75. package/dist/engine.js +1185 -0
  76. package/dist/engine.js.map +1 -0
  77. package/dist/eval/corpus.d.ts +42 -0
  78. package/dist/eval/corpus.d.ts.map +1 -0
  79. package/dist/eval/corpus.js +427 -0
  80. package/dist/eval/corpus.js.map +1 -0
  81. package/dist/eval/eval-harness.d.ts +44 -0
  82. package/dist/eval/eval-harness.d.ts.map +1 -0
  83. package/dist/eval/eval-harness.js +296 -0
  84. package/dist/eval/eval-harness.js.map +1 -0
  85. package/dist/eval/index.d.ts +13 -0
  86. package/dist/eval/index.d.ts.map +1 -0
  87. package/dist/eval/index.js +9 -0
  88. package/dist/eval/index.js.map +1 -0
  89. package/dist/eval/metrics.d.ts +74 -0
  90. package/dist/eval/metrics.d.ts.map +1 -0
  91. package/dist/eval/metrics.js +108 -0
  92. package/dist/eval/metrics.js.map +1 -0
  93. package/dist/eval/pint-corpus.d.ts +34 -0
  94. package/dist/eval/pint-corpus.d.ts.map +1 -0
  95. package/dist/eval/pint-corpus.js +113 -0
  96. package/dist/eval/pint-corpus.js.map +1 -0
  97. package/dist/eval/rule-corpus.d.ts +9 -0
  98. package/dist/eval/rule-corpus.d.ts.map +1 -0
  99. package/dist/eval/rule-corpus.js +4780 -0
  100. package/dist/eval/rule-corpus.js.map +1 -0
  101. package/dist/eval/rule-metrics.d.ts +34 -0
  102. package/dist/eval/rule-metrics.d.ts.map +1 -0
  103. package/dist/eval/rule-metrics.js +92 -0
  104. package/dist/eval/rule-metrics.js.map +1 -0
  105. package/dist/eval/run-eval.d.ts +7 -0
  106. package/dist/eval/run-eval.d.ts.map +1 -0
  107. package/dist/eval/run-eval.js +11 -0
  108. package/dist/eval/run-eval.js.map +1 -0
  109. package/dist/eval/run-pint-benchmark.d.ts +18 -0
  110. package/dist/eval/run-pint-benchmark.d.ts.map +1 -0
  111. package/dist/eval/run-pint-benchmark.js +159 -0
  112. package/dist/eval/run-pint-benchmark.js.map +1 -0
  113. package/dist/eval/skill-benchmark.d.ts +66 -0
  114. package/dist/eval/skill-benchmark.d.ts.map +1 -0
  115. package/dist/eval/skill-benchmark.js +194 -0
  116. package/dist/eval/skill-benchmark.js.map +1 -0
  117. package/dist/flywheel.d.ts +54 -0
  118. package/dist/flywheel.d.ts.map +1 -0
  119. package/dist/flywheel.js +121 -0
  120. package/dist/flywheel.js.map +1 -0
  121. package/dist/hook-handler.d.ts +61 -0
  122. package/dist/hook-handler.d.ts.map +1 -0
  123. package/dist/hook-handler.js +178 -0
  124. package/dist/hook-handler.js.map +1 -0
  125. package/dist/index.d.ts +8 -0
  126. package/dist/index.d.ts.map +1 -0
  127. package/{src/index.ts → dist/index.js} +1 -0
  128. package/dist/index.js.map +1 -0
  129. package/dist/layer-integration.d.ts +55 -0
  130. package/dist/layer-integration.d.ts.map +1 -0
  131. package/dist/layer-integration.js +187 -0
  132. package/dist/layer-integration.js.map +1 -0
  133. package/dist/loader.d.ts +18 -0
  134. package/dist/loader.d.ts.map +1 -0
  135. package/dist/loader.js +129 -0
  136. package/dist/loader.js.map +1 -0
  137. package/dist/mcp-server.d.ts +13 -0
  138. package/dist/mcp-server.d.ts.map +1 -0
  139. package/dist/mcp-server.js +246 -0
  140. package/dist/mcp-server.js.map +1 -0
  141. package/dist/mcp-tools/coverage-gaps.d.ts +13 -0
  142. package/dist/mcp-tools/coverage-gaps.d.ts.map +1 -0
  143. package/dist/mcp-tools/coverage-gaps.js +55 -0
  144. package/dist/mcp-tools/coverage-gaps.js.map +1 -0
  145. package/dist/mcp-tools/list-rules.d.ts +17 -0
  146. package/dist/mcp-tools/list-rules.d.ts.map +1 -0
  147. package/dist/mcp-tools/list-rules.js +45 -0
  148. package/dist/mcp-tools/list-rules.js.map +1 -0
  149. package/dist/mcp-tools/scan-skill.d.ts +17 -0
  150. package/dist/mcp-tools/scan-skill.d.ts.map +1 -0
  151. package/dist/mcp-tools/scan-skill.js +65 -0
  152. package/dist/mcp-tools/scan-skill.js.map +1 -0
  153. package/dist/mcp-tools/scan.d.ts +24 -0
  154. package/dist/mcp-tools/scan.d.ts.map +1 -0
  155. package/dist/mcp-tools/scan.js +94 -0
  156. package/dist/mcp-tools/scan.js.map +1 -0
  157. package/dist/mcp-tools/submit-proposal.d.ts +12 -0
  158. package/dist/mcp-tools/submit-proposal.d.ts.map +1 -0
  159. package/dist/mcp-tools/submit-proposal.js +103 -0
  160. package/dist/mcp-tools/submit-proposal.js.map +1 -0
  161. package/dist/mcp-tools/threat-summary.d.ts +12 -0
  162. package/dist/mcp-tools/threat-summary.d.ts.map +1 -0
  163. package/dist/mcp-tools/threat-summary.js +74 -0
  164. package/dist/mcp-tools/threat-summary.js.map +1 -0
  165. package/dist/mcp-tools/validate.d.ts +15 -0
  166. package/dist/mcp-tools/validate.d.ts.map +1 -0
  167. package/dist/mcp-tools/validate.js +51 -0
  168. package/dist/mcp-tools/validate.js.map +1 -0
  169. package/dist/modules/embedding.d.ts +71 -0
  170. package/dist/modules/embedding.d.ts.map +1 -0
  171. package/dist/modules/embedding.js +141 -0
  172. package/dist/modules/embedding.js.map +1 -0
  173. package/dist/modules/index.d.ts +144 -0
  174. package/dist/modules/index.d.ts.map +1 -0
  175. package/dist/modules/index.js +82 -0
  176. package/dist/modules/index.js.map +1 -0
  177. package/dist/modules/semantic.d.ts +106 -0
  178. package/dist/modules/semantic.d.ts.map +1 -0
  179. package/dist/modules/semantic.js +359 -0
  180. package/dist/modules/semantic.js.map +1 -0
  181. package/dist/modules/session.d.ts +70 -0
  182. package/dist/modules/session.d.ts.map +1 -0
  183. package/dist/modules/session.js +128 -0
  184. package/dist/modules/session.js.map +1 -0
  185. package/dist/quality/adapters/atr.d.ts +65 -0
  186. package/dist/quality/adapters/atr.d.ts.map +1 -0
  187. package/dist/quality/adapters/atr.js +154 -0
  188. package/dist/quality/adapters/atr.js.map +1 -0
  189. package/dist/quality/adapters/index.d.ts +10 -0
  190. package/dist/quality/adapters/index.d.ts.map +1 -0
  191. package/dist/quality/adapters/index.js +10 -0
  192. package/dist/quality/adapters/index.js.map +1 -0
  193. package/dist/quality/compute-confidence.d.ts +45 -0
  194. package/dist/quality/compute-confidence.d.ts.map +1 -0
  195. package/dist/quality/compute-confidence.js +133 -0
  196. package/dist/quality/compute-confidence.js.map +1 -0
  197. package/dist/quality/index.d.ts +36 -0
  198. package/dist/quality/index.d.ts.map +1 -0
  199. package/dist/quality/index.js +39 -0
  200. package/dist/quality/index.js.map +1 -0
  201. package/dist/quality/quality-gate.d.ts +86 -0
  202. package/dist/quality/quality-gate.d.ts.map +1 -0
  203. package/dist/quality/quality-gate.js +187 -0
  204. package/dist/quality/quality-gate.js.map +1 -0
  205. package/dist/quality/types.d.ts +129 -0
  206. package/dist/quality/types.d.ts.map +1 -0
  207. package/dist/quality/types.js +10 -0
  208. package/dist/quality/types.js.map +1 -0
  209. package/dist/quality/validate-maturity.d.ts +51 -0
  210. package/dist/quality/validate-maturity.d.ts.map +1 -0
  211. package/dist/quality/validate-maturity.js +134 -0
  212. package/dist/quality/validate-maturity.js.map +1 -0
  213. package/dist/quality.d.ts +8 -0
  214. package/dist/quality.d.ts.map +1 -0
  215. package/dist/quality.js +8 -0
  216. package/dist/quality.js.map +1 -0
  217. package/dist/rule-scaffolder.d.ts +53 -0
  218. package/dist/rule-scaffolder.d.ts.map +1 -0
  219. package/dist/rule-scaffolder.js +301 -0
  220. package/dist/rule-scaffolder.js.map +1 -0
  221. package/dist/session-tracker.d.ts +58 -0
  222. package/dist/session-tracker.d.ts.map +1 -0
  223. package/dist/session-tracker.js +176 -0
  224. package/dist/session-tracker.js.map +1 -0
  225. package/dist/shadow-evaluator.d.ts +48 -0
  226. package/dist/shadow-evaluator.d.ts.map +1 -0
  227. package/dist/shadow-evaluator.js +129 -0
  228. package/dist/shadow-evaluator.js.map +1 -0
  229. package/dist/skill-fingerprint.d.ts +85 -0
  230. package/dist/skill-fingerprint.d.ts.map +1 -0
  231. package/dist/skill-fingerprint.js +284 -0
  232. package/dist/skill-fingerprint.js.map +1 -0
  233. package/dist/tc-reporter.d.ts +50 -0
  234. package/dist/tc-reporter.d.ts.map +1 -0
  235. package/dist/tc-reporter.js +164 -0
  236. package/dist/tc-reporter.js.map +1 -0
  237. package/dist/tier0-invariant.d.ts +49 -0
  238. package/dist/tier0-invariant.d.ts.map +1 -0
  239. package/dist/tier0-invariant.js +185 -0
  240. package/dist/tier0-invariant.js.map +1 -0
  241. package/dist/tier1-blacklist.d.ts +48 -0
  242. package/dist/tier1-blacklist.d.ts.map +1 -0
  243. package/dist/tier1-blacklist.js +92 -0
  244. package/dist/tier1-blacklist.js.map +1 -0
  245. package/dist/types.d.ts +232 -0
  246. package/dist/types.d.ts.map +1 -0
  247. package/dist/types.js +6 -0
  248. package/dist/types.js.map +1 -0
  249. package/dist/verdict.d.ts +26 -0
  250. package/dist/verdict.d.ts.map +1 -0
  251. package/dist/verdict.js +127 -0
  252. package/dist/verdict.js.map +1 -0
  253. package/package.json +16 -4
  254. package/.github/ISSUE_TEMPLATE/evasion-report.yml +0 -75
  255. package/.github/ISSUE_TEMPLATE/false-positive.yml +0 -31
  256. package/.github/ISSUE_TEMPLATE/mirofish-prediction.yml +0 -128
  257. package/.github/ISSUE_TEMPLATE/new-rule.yml +0 -37
  258. package/.github/PULL_REQUEST_TEMPLATE.md +0 -23
  259. package/.github/workflows/rule-quality.yml +0 -203
  260. package/.github/workflows/validate.yml +0 -42
  261. package/CHANGELOG.md +0 -30
  262. package/CONTRIBUTING.md +0 -168
  263. package/CONTRIBUTORS.md +0 -28
  264. package/COVERAGE.md +0 -135
  265. package/LIMITATIONS.md +0 -154
  266. package/SECURITY.md +0 -48
  267. package/THREAT-MODEL.md +0 -243
  268. package/docs/contribution-paths.md +0 -202
  269. package/docs/mirofish-prediction-guide.md +0 -304
  270. package/docs/quick-start.md +0 -245
  271. package/docs/rule-writing-guide.md +0 -647
  272. package/docs/schema-spec.md +0 -594
  273. package/examples/how-to-write-a-rule.md +0 -251
  274. package/tsconfig.json +0 -17
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Skill Behavioral Fingerprint
3
+ *
4
+ * Tracks what each skill "normally does" across invocations, then detects
5
+ * behavioral drift when a previously-trusted skill starts acting differently.
6
+ *
7
+ * Solves the "installed then turns malicious" scenario:
8
+ * - First N invocations: build fingerprint (what APIs, what patterns, what scope)
9
+ * - After fingerprint stabilizes: flag any deviation as anomaly
10
+ *
11
+ * @module agent-threat-rules/skill-fingerprint
12
+ */
13
+ import { createHash } from 'node:crypto';
14
+ import { extractCapabilities as sharedExtractCapabilities, } from './capability-extractor.js';
15
+ // ---------------------------------------------------------------------------
16
+ // Capability extraction (shared with tier0-invariant.ts)
17
+ // ---------------------------------------------------------------------------
18
+ /** Wrapper for shared extractCapabilities, mapping to local format */
19
+ function extractCapabilities(text) {
20
+ const caps = sharedExtractCapabilities(text);
21
+ return {
22
+ filesystemOps: [...caps.filesystemOps],
23
+ networkTargets: [...caps.networkTargets],
24
+ envAccesses: [...caps.envAccesses],
25
+ processExecs: [...caps.processExecs],
26
+ outputPatterns: [...caps.outputPatterns],
27
+ };
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Fingerprint Store
31
+ // ---------------------------------------------------------------------------
32
+ /** Default invocations needed before fingerprint is considered stable */
33
+ const DEFAULT_STABILITY_THRESHOLD = 10;
34
+ /** Consecutive invocations with no new capabilities to mark stable */
35
+ const DEFAULT_STABLE_STREAK = 5;
36
+ /** Maximum number of skills to track */
37
+ const MAX_SKILLS = 5_000;
38
+ export class SkillFingerprintStore {
39
+ fingerprints = new Map();
40
+ stabilityThreshold;
41
+ stableStreak;
42
+ constructor(config) {
43
+ this.stabilityThreshold = config?.stabilityThreshold ?? DEFAULT_STABILITY_THRESHOLD;
44
+ this.stableStreak = config?.stableStreak ?? DEFAULT_STABLE_STREAK;
45
+ }
46
+ /**
47
+ * Record a skill invocation and detect behavioral anomalies.
48
+ * Returns anomalies if the fingerprint was stable and new capabilities appeared.
49
+ */
50
+ recordInvocation(skillName, event) {
51
+ const now = Date.now();
52
+ const fp = this.getOrCreate(skillName, now);
53
+ fp.invocationCount++;
54
+ fp.lastSeen = now;
55
+ // Extract capabilities from event content + fields
56
+ const content = [
57
+ event.content ?? '',
58
+ event.fields?.['tool_args'] ?? '',
59
+ event.fields?.['tool_response'] ?? '',
60
+ ].join('\n');
61
+ const caps = extractCapabilities(content);
62
+ // Check for anomalies (only if fingerprint is stable)
63
+ const anomalies = [];
64
+ const isStable = fp.stableHash !== null;
65
+ if (isStable) {
66
+ // Detect NEW capabilities not in the stable fingerprint
67
+ for (const op of caps.filesystemOps) {
68
+ if (!fp.filesystemOps.has(op)) {
69
+ anomalies.push({
70
+ skillName,
71
+ anomalyType: 'new_filesystem_op',
72
+ description: `Skill "${skillName}" performing new filesystem operation: ${op} (not in baseline)`,
73
+ severity: op === 'delete' ? 'critical' : op === 'write' ? 'high' : 'medium',
74
+ newValue: op,
75
+ timestamp: now,
76
+ });
77
+ }
78
+ }
79
+ for (const target of caps.networkTargets) {
80
+ if (!fp.networkTargets.has(target)) {
81
+ anomalies.push({
82
+ skillName,
83
+ anomalyType: 'new_network_target',
84
+ description: `Skill "${skillName}" contacting new network target: ${target}`,
85
+ severity: 'high',
86
+ newValue: target,
87
+ timestamp: now,
88
+ });
89
+ }
90
+ }
91
+ for (const env of caps.envAccesses) {
92
+ if (!fp.envAccesses.has(env)) {
93
+ const isSensitive = /(?:KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)/i.test(env);
94
+ anomalies.push({
95
+ skillName,
96
+ anomalyType: 'new_env_access',
97
+ description: `Skill "${skillName}" accessing new env var: ${env}`,
98
+ severity: isSensitive ? 'critical' : 'medium',
99
+ newValue: env,
100
+ timestamp: now,
101
+ });
102
+ }
103
+ }
104
+ for (const proc of caps.processExecs) {
105
+ if (!fp.processExecs.has(proc)) {
106
+ anomalies.push({
107
+ skillName,
108
+ anomalyType: 'new_process_exec',
109
+ description: `Skill "${skillName}" executing new process: ${proc}`,
110
+ severity: 'critical',
111
+ newValue: proc,
112
+ timestamp: now,
113
+ });
114
+ }
115
+ }
116
+ for (const pat of caps.outputPatterns) {
117
+ if (!fp.outputPatterns.has(pat)) {
118
+ anomalies.push({
119
+ skillName,
120
+ anomalyType: 'new_output_pattern',
121
+ description: `Skill "${skillName}" exhibiting new pattern: ${pat}`,
122
+ severity: pat === 'exfiltration' ? 'critical' : 'high',
123
+ newValue: pat,
124
+ timestamp: now,
125
+ });
126
+ }
127
+ }
128
+ }
129
+ // Update fingerprint with observed capabilities
130
+ let newCapsSeen = false;
131
+ for (const op of caps.filesystemOps) {
132
+ if (!fp.filesystemOps.has(op)) {
133
+ fp.filesystemOps.add(op);
134
+ newCapsSeen = true;
135
+ }
136
+ }
137
+ for (const t of caps.networkTargets) {
138
+ if (!fp.networkTargets.has(t)) {
139
+ fp.networkTargets.add(t);
140
+ newCapsSeen = true;
141
+ }
142
+ }
143
+ for (const e of caps.envAccesses) {
144
+ if (!fp.envAccesses.has(e)) {
145
+ fp.envAccesses.add(e);
146
+ newCapsSeen = true;
147
+ }
148
+ }
149
+ for (const p of caps.processExecs) {
150
+ if (!fp.processExecs.has(p)) {
151
+ fp.processExecs.add(p);
152
+ newCapsSeen = true;
153
+ }
154
+ }
155
+ for (const o of caps.outputPatterns) {
156
+ if (!fp.outputPatterns.has(o)) {
157
+ fp.outputPatterns.add(o);
158
+ newCapsSeen = true;
159
+ }
160
+ }
161
+ // Track stability
162
+ if (!isStable) {
163
+ if (newCapsSeen) {
164
+ fp.stableStreak = 0;
165
+ }
166
+ else {
167
+ fp.stableStreak++;
168
+ }
169
+ // Mark stable when threshold met
170
+ if (fp.invocationCount >= this.stabilityThreshold &&
171
+ fp.stableStreak >= this.stableStreak) {
172
+ fp.stableHash = this.computeCapabilityHash(fp);
173
+ }
174
+ }
175
+ return anomalies;
176
+ }
177
+ /**
178
+ * Get an immutable fingerprint snapshot for a skill.
179
+ */
180
+ getFingerprint(skillName) {
181
+ const fp = this.fingerprints.get(skillName);
182
+ if (!fp)
183
+ return undefined;
184
+ return {
185
+ skillName: fp.skillName,
186
+ invocationCount: fp.invocationCount,
187
+ firstSeen: fp.firstSeen,
188
+ lastSeen: fp.lastSeen,
189
+ isStable: fp.stableHash !== null,
190
+ capabilities: {
191
+ filesystemOps: new Set(fp.filesystemOps),
192
+ networkTargets: new Set(fp.networkTargets),
193
+ envAccesses: new Set(fp.envAccesses),
194
+ processExecs: new Set(fp.processExecs),
195
+ outputPatterns: new Set(fp.outputPatterns),
196
+ },
197
+ capabilityHash: fp.stableHash ?? this.computeCapabilityHash(fp),
198
+ };
199
+ }
200
+ /** Get all tracked skill names */
201
+ getTrackedSkills() {
202
+ return [...this.fingerprints.keys()];
203
+ }
204
+ /** Get count of stable fingerprints */
205
+ getStableCount() {
206
+ let count = 0;
207
+ for (const fp of this.fingerprints.values()) {
208
+ if (fp.stableHash !== null)
209
+ count++;
210
+ }
211
+ return count;
212
+ }
213
+ /** Get total tracked skills */
214
+ getTrackedCount() {
215
+ return this.fingerprints.size;
216
+ }
217
+ /**
218
+ * Reset a skill's fingerprint (e.g., after a legitimate update).
219
+ */
220
+ resetFingerprint(skillName) {
221
+ this.fingerprints.delete(skillName);
222
+ }
223
+ /**
224
+ * Evict fingerprints not seen since cutoffMs ago.
225
+ */
226
+ cleanup(cutoffMs) {
227
+ const cutoff = Date.now() - cutoffMs;
228
+ let evicted = 0;
229
+ for (const [name, fp] of this.fingerprints) {
230
+ if (fp.lastSeen < cutoff) {
231
+ this.fingerprints.delete(name);
232
+ evicted++;
233
+ }
234
+ }
235
+ return evicted;
236
+ }
237
+ // -----------------------------------------------------------------------
238
+ // Private
239
+ // -----------------------------------------------------------------------
240
+ getOrCreate(skillName, now) {
241
+ const existing = this.fingerprints.get(skillName);
242
+ if (existing)
243
+ return existing;
244
+ // Evict oldest if at capacity
245
+ if (this.fingerprints.size >= MAX_SKILLS) {
246
+ let oldestName;
247
+ let oldestTime = Infinity;
248
+ for (const [name, fp] of this.fingerprints) {
249
+ if (fp.lastSeen < oldestTime) {
250
+ oldestTime = fp.lastSeen;
251
+ oldestName = name;
252
+ }
253
+ }
254
+ if (oldestName)
255
+ this.fingerprints.delete(oldestName);
256
+ }
257
+ const fp = {
258
+ skillName,
259
+ invocationCount: 0,
260
+ firstSeen: now,
261
+ lastSeen: now,
262
+ filesystemOps: new Set(),
263
+ networkTargets: new Set(),
264
+ envAccesses: new Set(),
265
+ processExecs: new Set(),
266
+ outputPatterns: new Set(),
267
+ stableHash: null,
268
+ stableStreak: 0,
269
+ };
270
+ this.fingerprints.set(skillName, fp);
271
+ return fp;
272
+ }
273
+ computeCapabilityHash(fp) {
274
+ const parts = [
275
+ [...fp.filesystemOps].sort().join(','),
276
+ [...fp.networkTargets].sort().join(','),
277
+ [...fp.envAccesses].sort().join(','),
278
+ [...fp.processExecs].sort().join(','),
279
+ [...fp.outputPatterns].sort().join(','),
280
+ ];
281
+ return createHash('sha256').update(parts.join('|')).digest('hex').slice(0, 16);
282
+ }
283
+ }
284
+ //# sourceMappingURL=skill-fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-fingerprint.js","sourceRoot":"","sources":["../src/skill-fingerprint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Threat Cloud Reporter — turns any ATR integration into a global sensor endpoint.
3
+ *
4
+ * Usage:
5
+ * import { ATREngine, createTCReporter } from 'agent-threat-rules';
6
+ *
7
+ * const engine = new ATREngine({
8
+ * reporter: createTCReporter(), // anonymous, no API key needed
9
+ * });
10
+ *
11
+ * // or with explicit config:
12
+ * const engine = new ATREngine({
13
+ * reporter: createTCReporter({
14
+ * tcUrl: 'https://tc.panguard.ai',
15
+ * apiKey: process.env.TC_API_KEY,
16
+ * batchSize: 50,
17
+ * flushIntervalMs: 60_000,
18
+ * }),
19
+ * });
20
+ *
21
+ * Privacy: only ruleId, severity, category, confidence, contentHash, and timestamp
22
+ * are sent. scanTarget is sanitized to a short label (max 64 chars, no path separators).
23
+ * No raw content, no PII, no file paths.
24
+ *
25
+ * @module tc-reporter
26
+ */
27
+ import type { ATRReporter } from './engine.js';
28
+ export interface TCReporterConfig {
29
+ /** Threat Cloud endpoint. Default: https://tc.panguard.ai */
30
+ readonly tcUrl?: string;
31
+ /** Optional API key for authenticated reporting. Default: env TC_API_KEY */
32
+ readonly apiKey?: string;
33
+ /** Flush buffer when it reaches this size. Default: 50 */
34
+ readonly batchSize?: number;
35
+ /** Periodic flush interval in ms. Default: 60000 (60s) */
36
+ readonly flushIntervalMs?: number;
37
+ /** Unique client ID. Default: random UUID per process */
38
+ readonly clientId?: string;
39
+ /** Called on flush errors. Default: no-op (silent). Provide your own logger. */
40
+ readonly onError?: (error: Error) => void;
41
+ }
42
+ /**
43
+ * Create an ATRReporter that buffers detection events and POSTs them
44
+ * to ATR Threat Cloud in batches. Each reporter instance = one sensor endpoint.
45
+ */
46
+ export declare function createTCReporter(config?: TCReporterConfig): ATRReporter & {
47
+ flush(): Promise<void>;
48
+ destroy(): Promise<void>;
49
+ };
50
+ //# sourceMappingURL=tc-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tc-reporter.d.ts","sourceRoot":"","sources":["../src/tc-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAsC,MAAM,aAAa,CAAC;AAInF,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3C;AAYD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,WAAW,GAAG;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B,CA4HA"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Threat Cloud Reporter — turns any ATR integration into a global sensor endpoint.
3
+ *
4
+ * Usage:
5
+ * import { ATREngine, createTCReporter } from 'agent-threat-rules';
6
+ *
7
+ * const engine = new ATREngine({
8
+ * reporter: createTCReporter(), // anonymous, no API key needed
9
+ * });
10
+ *
11
+ * // or with explicit config:
12
+ * const engine = new ATREngine({
13
+ * reporter: createTCReporter({
14
+ * tcUrl: 'https://tc.panguard.ai',
15
+ * apiKey: process.env.TC_API_KEY,
16
+ * batchSize: 50,
17
+ * flushIntervalMs: 60_000,
18
+ * }),
19
+ * });
20
+ *
21
+ * Privacy: only ruleId, severity, category, confidence, contentHash, and timestamp
22
+ * are sent. scanTarget is sanitized to a short label (max 64 chars, no path separators).
23
+ * No raw content, no PII, no file paths.
24
+ *
25
+ * @module tc-reporter
26
+ */
27
+ import { randomUUID } from 'node:crypto';
28
+ const MAX_BUFFER = 1000;
29
+ /**
30
+ * Create an ATRReporter that buffers detection events and POSTs them
31
+ * to ATR Threat Cloud in batches. Each reporter instance = one sensor endpoint.
32
+ */
33
+ export function createTCReporter(config) {
34
+ const tcUrl = (config?.tcUrl ?? 'https://tc.panguard.ai').replace(/\/+$/, '');
35
+ const apiKey = config?.apiKey ?? process.env.TC_API_KEY ?? '';
36
+ const batchSize = config?.batchSize ?? 50;
37
+ const flushIntervalMs = config?.flushIntervalMs ?? 60_000;
38
+ const clientId = config?.clientId ?? randomUUID();
39
+ const onError = config?.onError ?? (() => { });
40
+ // Enforce HTTPS for non-localhost
41
+ if (tcUrl.startsWith('http://') && !isLocalhost(tcUrl)) {
42
+ throw new Error('HTTPS required for Threat Cloud endpoint (use https://)');
43
+ }
44
+ let buffer = [];
45
+ let flushing = false;
46
+ let flushTimer = null;
47
+ // Start periodic flush
48
+ flushTimer = setInterval(() => { void flushBuffer(); }, flushIntervalMs);
49
+ // Don't keep the process alive just for flushing
50
+ if (flushTimer && typeof flushTimer === 'object' && 'unref' in flushTimer) {
51
+ flushTimer.unref();
52
+ }
53
+ async function flushBuffer() {
54
+ if (flushing || buffer.length === 0)
55
+ return;
56
+ flushing = true;
57
+ const batch = [...buffer];
58
+ buffer = [];
59
+ // Map ATR events to TC ThreatDataSchema format
60
+ const tcEvents = batch.map((e) => ({
61
+ attackSourceIP: clientId,
62
+ attackType: e.category,
63
+ mitreTechnique: e.ruleId,
64
+ ruleMatched: e.ruleId,
65
+ timestamp: e.timestamp,
66
+ region: 'unknown',
67
+ // Extra fields for richer data (TC ignores unknown fields via Zod passthrough)
68
+ severity: e.severity,
69
+ confidence: e.confidence,
70
+ contentHash: e.contentHash,
71
+ scanTarget: e.scanTarget,
72
+ }));
73
+ try {
74
+ const res = await fetch(`${tcUrl}/api/threats`, {
75
+ method: 'POST',
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ 'X-Panguard-Client-Id': clientId,
79
+ ...(apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {}),
80
+ },
81
+ body: JSON.stringify({ events: tcEvents }),
82
+ signal: AbortSignal.timeout(10_000),
83
+ });
84
+ if (!res.ok) {
85
+ buffer = [...batch, ...buffer].slice(0, MAX_BUFFER);
86
+ onError(new Error(`TC upload failed: HTTP ${res.status}`));
87
+ }
88
+ }
89
+ catch (err) {
90
+ buffer = [...batch, ...buffer].slice(0, MAX_BUFFER);
91
+ onError(err instanceof Error ? err : new Error(String(err)));
92
+ }
93
+ finally {
94
+ flushing = false;
95
+ }
96
+ }
97
+ function enqueue(event) {
98
+ buffer = [...buffer, event];
99
+ // Cap buffer to prevent memory leak
100
+ if (buffer.length >= MAX_BUFFER) {
101
+ buffer = buffer.slice(-MAX_BUFFER);
102
+ }
103
+ // Auto-flush when batch size reached
104
+ if (buffer.length >= batchSize) {
105
+ void flushBuffer();
106
+ }
107
+ }
108
+ const reporter = {
109
+ onDetection: (report) => {
110
+ enqueue({
111
+ ruleId: report.ruleId,
112
+ severity: report.severity,
113
+ category: report.category,
114
+ confidence: report.confidence,
115
+ contentHash: report.contentHash,
116
+ timestamp: report.timestamp,
117
+ scanTarget: sanitizeScanTarget(report.scanTarget),
118
+ });
119
+ },
120
+ onClean: (report) => {
121
+ enqueue({
122
+ ruleId: '__clean__',
123
+ severity: 'none',
124
+ category: 'clean',
125
+ confidence: 1,
126
+ contentHash: report.contentHash,
127
+ timestamp: report.timestamp,
128
+ scanTarget: sanitizeScanTarget(report.scanTarget),
129
+ });
130
+ },
131
+ /** Manually flush all buffered events to TC. Call before process exit. */
132
+ flush: flushBuffer,
133
+ /** Stop periodic flush timer and flush remaining events. Await to ensure delivery. */
134
+ async destroy() {
135
+ if (flushTimer) {
136
+ clearInterval(flushTimer);
137
+ flushTimer = null;
138
+ }
139
+ await flushBuffer();
140
+ },
141
+ };
142
+ return reporter;
143
+ }
144
+ /** Strip path separators and truncate to prevent PII leakage via scanTarget. */
145
+ function sanitizeScanTarget(target) {
146
+ return target
147
+ .replace(/[/\\]/g, '-')
148
+ .replace(/\.\./g, '')
149
+ .slice(0, 64);
150
+ }
151
+ function isLocalhost(url) {
152
+ try {
153
+ const parsed = new URL(url);
154
+ const h = parsed.hostname;
155
+ return h === 'localhost'
156
+ || h === '127.0.0.1'
157
+ || h === '::1'
158
+ || h === '[::1]';
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
164
+ //# sourceMappingURL=tc-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tc-reporter.js","sourceRoot":"","sources":["../src/tc-reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tier 0: Invariant Enforcement
3
+ *
4
+ * Hard boundaries that enforce what a skill is ALLOWED to do,
5
+ * regardless of what its description says or how it phrases requests.
6
+ *
7
+ * This is NOT pattern matching. It is permission checking.
8
+ * A skill declares capabilities in its manifest. Any action outside
9
+ * the manifest is immediately denied with severity=critical.
10
+ *
11
+ * Inspired by Tesla's AEB (Automatic Emergency Braking):
12
+ * it doesn't care what the neural network thinks -- it enforces physics.
13
+ *
14
+ * @module agent-threat-rules/tier0-invariant
15
+ */
16
+ import type { AgentEvent, ATRMatch } from './types.js';
17
+ /** Skill capability manifest -- declares what a skill is allowed to do */
18
+ export interface SkillManifest {
19
+ readonly skillId: string;
20
+ readonly allowedPaths?: readonly string[];
21
+ readonly allowedHosts?: readonly string[];
22
+ readonly allowedEnvVars?: readonly string[];
23
+ readonly allowedCommands?: readonly string[];
24
+ readonly maxNetworkCalls?: number;
25
+ readonly allowConfigModification?: boolean;
26
+ }
27
+ export type InvariantViolationType = 'path_scope' | 'host_scope' | 'env_scope' | 'command_scope' | 'config_modification' | 'network_limit';
28
+ /** Result when an invariant is violated */
29
+ export interface InvariantViolation {
30
+ readonly skillId: string;
31
+ readonly violationType: InvariantViolationType;
32
+ readonly description: string;
33
+ readonly observedValue: string;
34
+ readonly allowedValues: readonly string[];
35
+ }
36
+ export declare class InvariantChecker {
37
+ private readonly manifests;
38
+ constructor(manifests: ReadonlyMap<string, SkillManifest> | SkillManifest[]);
39
+ /**
40
+ * Check an event against the skill's manifest.
41
+ * Returns empty array if no violations (or no manifest for the skill).
42
+ */
43
+ check(event: AgentEvent): readonly InvariantViolation[];
44
+ /** Build a synthetic ATRMatch from an invariant violation */
45
+ buildDenyMatch(violation: InvariantViolation): ATRMatch;
46
+ /** Resolve skill ID from event metadata */
47
+ private resolveSkillId;
48
+ }
49
+ //# sourceMappingURL=tier0-invariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier0-invariant.d.ts","sourceRoot":"","sources":["../src/tier0-invariant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAwB,MAAM,YAAY,CAAC;AA6B7E,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC5C;AAED,MAAM,MAAM,sBAAsB,GAC9B,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,eAAe,GACf,qBAAqB,GACrB,eAAe,CAAC;AAEpB,2CAA2C;AAC3C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,sBAAsB,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAMD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqC;gBAEnD,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,aAAa,EAAE;IAY3E;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,kBAAkB,EAAE;IAsGvD,6DAA6D;IAC7D,cAAc,CAAC,SAAS,EAAE,kBAAkB,GAAG,QAAQ;IAgCvD,2CAA2C;IAC3C,OAAO,CAAC,cAAc;CAKvB"}