@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.
- package/dist/action-executor.d.ts +44 -0
- package/dist/action-executor.d.ts.map +1 -0
- package/dist/action-executor.js +130 -0
- package/dist/action-executor.js.map +1 -0
- package/dist/adapters/default-adapter.d.ts +24 -0
- package/dist/adapters/default-adapter.d.ts.map +1 -0
- package/dist/adapters/default-adapter.js +51 -0
- package/dist/adapters/default-adapter.js.map +1 -0
- package/dist/adapters/stdio-adapter.d.ts +30 -0
- package/dist/adapters/stdio-adapter.d.ts.map +1 -0
- package/dist/adapters/stdio-adapter.js +128 -0
- package/dist/adapters/stdio-adapter.js.map +1 -0
- package/dist/badge.d.ts +42 -0
- package/dist/badge.d.ts.map +1 -0
- package/dist/badge.js +163 -0
- package/dist/badge.js.map +1 -0
- package/dist/capability-extractor.d.ts +35 -0
- package/dist/capability-extractor.d.ts.map +1 -0
- package/dist/capability-extractor.js +91 -0
- package/dist/capability-extractor.js.map +1 -0
- package/dist/cli/scan-handler.d.ts +21 -0
- package/dist/cli/scan-handler.d.ts.map +1 -0
- package/dist/cli/scan-handler.js +276 -0
- package/dist/cli/scan-handler.js.map +1 -0
- package/dist/cli/tc-pipeline.d.ts +18 -0
- package/dist/cli/tc-pipeline.d.ts.map +1 -0
- package/dist/cli/tc-pipeline.js +295 -0
- package/dist/cli/tc-pipeline.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +894 -0
- package/dist/cli.js.map +1 -0
- package/dist/content-hash.d.ts +7 -0
- package/dist/content-hash.d.ts.map +1 -0
- package/dist/content-hash.js +10 -0
- package/dist/content-hash.js.map +1 -0
- package/dist/converters/elastic.d.ts +36 -0
- package/dist/converters/elastic.d.ts.map +1 -0
- package/dist/converters/elastic.js +125 -0
- package/dist/converters/elastic.js.map +1 -0
- package/dist/converters/generic-regex.d.ts +37 -0
- package/dist/converters/generic-regex.d.ts.map +1 -0
- package/dist/converters/generic-regex.js +59 -0
- package/dist/converters/generic-regex.js.map +1 -0
- package/dist/converters/index.d.ts +32 -0
- package/dist/converters/index.d.ts.map +1 -0
- package/dist/converters/index.js +38 -0
- package/dist/converters/index.js.map +1 -0
- package/dist/converters/sarif.d.ts +18 -0
- package/dist/converters/sarif.d.ts.map +1 -0
- package/dist/converters/sarif.js +142 -0
- package/dist/converters/sarif.js.map +1 -0
- package/dist/converters/splunk.d.ts +19 -0
- package/dist/converters/splunk.d.ts.map +1 -0
- package/dist/converters/splunk.js +148 -0
- package/dist/converters/splunk.js.map +1 -0
- package/dist/coverage-analyzer.d.ts +43 -0
- package/dist/coverage-analyzer.d.ts.map +1 -0
- package/dist/coverage-analyzer.js +329 -0
- package/dist/coverage-analyzer.js.map +1 -0
- package/dist/embedding/build-corpus.d.ts +15 -0
- package/dist/embedding/build-corpus.d.ts.map +1 -0
- package/dist/embedding/build-corpus.js +105 -0
- package/dist/embedding/build-corpus.js.map +1 -0
- package/dist/embedding/model-loader.d.ts +41 -0
- package/dist/embedding/model-loader.d.ts.map +1 -0
- package/dist/embedding/model-loader.js +90 -0
- package/dist/embedding/model-loader.js.map +1 -0
- package/dist/embedding/vector-store.d.ts +41 -0
- package/dist/embedding/vector-store.d.ts.map +1 -0
- package/dist/embedding/vector-store.js +70 -0
- package/dist/embedding/vector-store.js.map +1 -0
- package/dist/engine.d.ts +222 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1185 -0
- package/dist/engine.js.map +1 -0
- package/dist/eval/corpus.d.ts +42 -0
- package/dist/eval/corpus.d.ts.map +1 -0
- package/dist/eval/corpus.js +427 -0
- package/dist/eval/corpus.js.map +1 -0
- package/dist/eval/eval-harness.d.ts +44 -0
- package/dist/eval/eval-harness.d.ts.map +1 -0
- package/dist/eval/eval-harness.js +296 -0
- package/dist/eval/eval-harness.js.map +1 -0
- package/dist/eval/index.d.ts +13 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +9 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/metrics.d.ts +74 -0
- package/dist/eval/metrics.d.ts.map +1 -0
- package/dist/eval/metrics.js +108 -0
- package/dist/eval/metrics.js.map +1 -0
- package/dist/eval/pint-corpus.d.ts +34 -0
- package/dist/eval/pint-corpus.d.ts.map +1 -0
- package/dist/eval/pint-corpus.js +113 -0
- package/dist/eval/pint-corpus.js.map +1 -0
- package/dist/eval/rule-corpus.d.ts +9 -0
- package/dist/eval/rule-corpus.d.ts.map +1 -0
- package/dist/eval/rule-corpus.js +4780 -0
- package/dist/eval/rule-corpus.js.map +1 -0
- package/dist/eval/rule-metrics.d.ts +34 -0
- package/dist/eval/rule-metrics.d.ts.map +1 -0
- package/dist/eval/rule-metrics.js +92 -0
- package/dist/eval/rule-metrics.js.map +1 -0
- package/dist/eval/run-eval.d.ts +7 -0
- package/dist/eval/run-eval.d.ts.map +1 -0
- package/dist/eval/run-eval.js +11 -0
- package/dist/eval/run-eval.js.map +1 -0
- package/dist/eval/run-pint-benchmark.d.ts +18 -0
- package/dist/eval/run-pint-benchmark.d.ts.map +1 -0
- package/dist/eval/run-pint-benchmark.js +159 -0
- package/dist/eval/run-pint-benchmark.js.map +1 -0
- package/dist/eval/skill-benchmark.d.ts +66 -0
- package/dist/eval/skill-benchmark.d.ts.map +1 -0
- package/dist/eval/skill-benchmark.js +194 -0
- package/dist/eval/skill-benchmark.js.map +1 -0
- package/dist/flywheel.d.ts +54 -0
- package/dist/flywheel.d.ts.map +1 -0
- package/dist/flywheel.js +121 -0
- package/dist/flywheel.js.map +1 -0
- package/dist/hook-handler.d.ts +61 -0
- package/dist/hook-handler.d.ts.map +1 -0
- package/dist/hook-handler.js +178 -0
- package/dist/hook-handler.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -0
- package/dist/index.js.map +1 -0
- package/dist/layer-integration.d.ts +55 -0
- package/dist/layer-integration.d.ts.map +1 -0
- package/dist/layer-integration.js +187 -0
- package/dist/layer-integration.js.map +1 -0
- package/dist/loader.d.ts +18 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +129 -0
- package/dist/loader.js.map +1 -0
- package/dist/mcp-server.d.ts +13 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +246 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-tools/coverage-gaps.d.ts +13 -0
- package/dist/mcp-tools/coverage-gaps.d.ts.map +1 -0
- package/dist/mcp-tools/coverage-gaps.js +55 -0
- package/dist/mcp-tools/coverage-gaps.js.map +1 -0
- package/dist/mcp-tools/list-rules.d.ts +17 -0
- package/dist/mcp-tools/list-rules.d.ts.map +1 -0
- package/dist/mcp-tools/list-rules.js +45 -0
- package/dist/mcp-tools/list-rules.js.map +1 -0
- package/dist/mcp-tools/scan-skill.d.ts +17 -0
- package/dist/mcp-tools/scan-skill.d.ts.map +1 -0
- package/dist/mcp-tools/scan-skill.js +65 -0
- package/dist/mcp-tools/scan-skill.js.map +1 -0
- package/dist/mcp-tools/scan.d.ts +24 -0
- package/dist/mcp-tools/scan.d.ts.map +1 -0
- package/dist/mcp-tools/scan.js +94 -0
- package/dist/mcp-tools/scan.js.map +1 -0
- package/dist/mcp-tools/submit-proposal.d.ts +12 -0
- package/dist/mcp-tools/submit-proposal.d.ts.map +1 -0
- package/dist/mcp-tools/submit-proposal.js +103 -0
- package/dist/mcp-tools/submit-proposal.js.map +1 -0
- package/dist/mcp-tools/threat-summary.d.ts +12 -0
- package/dist/mcp-tools/threat-summary.d.ts.map +1 -0
- package/dist/mcp-tools/threat-summary.js +74 -0
- package/dist/mcp-tools/threat-summary.js.map +1 -0
- package/dist/mcp-tools/validate.d.ts +15 -0
- package/dist/mcp-tools/validate.d.ts.map +1 -0
- package/dist/mcp-tools/validate.js +51 -0
- package/dist/mcp-tools/validate.js.map +1 -0
- package/dist/modules/embedding.d.ts +71 -0
- package/dist/modules/embedding.d.ts.map +1 -0
- package/dist/modules/embedding.js +141 -0
- package/dist/modules/embedding.js.map +1 -0
- package/dist/modules/index.d.ts +144 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +82 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/modules/semantic.d.ts +106 -0
- package/dist/modules/semantic.d.ts.map +1 -0
- package/dist/modules/semantic.js +359 -0
- package/dist/modules/semantic.js.map +1 -0
- package/dist/modules/session.d.ts +70 -0
- package/dist/modules/session.d.ts.map +1 -0
- package/dist/modules/session.js +128 -0
- package/dist/modules/session.js.map +1 -0
- package/dist/quality/adapters/atr.d.ts +65 -0
- package/dist/quality/adapters/atr.d.ts.map +1 -0
- package/dist/quality/adapters/atr.js +154 -0
- package/dist/quality/adapters/atr.js.map +1 -0
- package/dist/quality/adapters/index.d.ts +10 -0
- package/dist/quality/adapters/index.d.ts.map +1 -0
- package/dist/quality/adapters/index.js +10 -0
- package/dist/quality/adapters/index.js.map +1 -0
- package/dist/quality/compute-confidence.d.ts +45 -0
- package/dist/quality/compute-confidence.d.ts.map +1 -0
- package/dist/quality/compute-confidence.js +133 -0
- package/dist/quality/compute-confidence.js.map +1 -0
- package/dist/quality/index.d.ts +36 -0
- package/dist/quality/index.d.ts.map +1 -0
- package/dist/quality/index.js +39 -0
- package/dist/quality/index.js.map +1 -0
- package/dist/quality/quality-gate.d.ts +86 -0
- package/dist/quality/quality-gate.d.ts.map +1 -0
- package/dist/quality/quality-gate.js +187 -0
- package/dist/quality/quality-gate.js.map +1 -0
- package/dist/quality/types.d.ts +129 -0
- package/dist/quality/types.d.ts.map +1 -0
- package/dist/quality/types.js +10 -0
- package/dist/quality/types.js.map +1 -0
- package/dist/quality/validate-maturity.d.ts +51 -0
- package/dist/quality/validate-maturity.d.ts.map +1 -0
- package/dist/quality/validate-maturity.js +134 -0
- package/dist/quality/validate-maturity.js.map +1 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.d.ts.map +1 -0
- package/dist/quality.js +8 -0
- package/dist/quality.js.map +1 -0
- package/dist/rule-scaffolder.d.ts +53 -0
- package/dist/rule-scaffolder.d.ts.map +1 -0
- package/dist/rule-scaffolder.js +301 -0
- package/dist/rule-scaffolder.js.map +1 -0
- package/dist/session-tracker.d.ts +58 -0
- package/dist/session-tracker.d.ts.map +1 -0
- package/dist/session-tracker.js +176 -0
- package/dist/session-tracker.js.map +1 -0
- package/dist/shadow-evaluator.d.ts +48 -0
- package/dist/shadow-evaluator.d.ts.map +1 -0
- package/dist/shadow-evaluator.js +129 -0
- package/dist/shadow-evaluator.js.map +1 -0
- package/dist/skill-fingerprint.d.ts +85 -0
- package/dist/skill-fingerprint.d.ts.map +1 -0
- package/dist/skill-fingerprint.js +284 -0
- package/dist/skill-fingerprint.js.map +1 -0
- package/dist/tc-reporter.d.ts +50 -0
- package/dist/tc-reporter.d.ts.map +1 -0
- package/dist/tc-reporter.js +164 -0
- package/dist/tc-reporter.js.map +1 -0
- package/dist/tier0-invariant.d.ts +49 -0
- package/dist/tier0-invariant.d.ts.map +1 -0
- package/dist/tier0-invariant.js +185 -0
- package/dist/tier0-invariant.js.map +1 -0
- package/dist/tier1-blacklist.d.ts +48 -0
- package/dist/tier1-blacklist.d.ts.map +1 -0
- package/dist/tier1-blacklist.js +92 -0
- package/dist/tier1-blacklist.js.map +1 -0
- package/dist/types.d.ts +232 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/verdict.d.ts +26 -0
- package/dist/verdict.d.ts.map +1 -0
- package/dist/verdict.js +127 -0
- package/dist/verdict.js.map +1 -0
- package/package.json +16 -4
- package/.github/ISSUE_TEMPLATE/evasion-report.yml +0 -75
- package/.github/ISSUE_TEMPLATE/false-positive.yml +0 -31
- package/.github/ISSUE_TEMPLATE/mirofish-prediction.yml +0 -128
- package/.github/ISSUE_TEMPLATE/new-rule.yml +0 -37
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -23
- package/.github/workflows/rule-quality.yml +0 -203
- package/.github/workflows/validate.yml +0 -42
- package/CHANGELOG.md +0 -30
- package/CONTRIBUTING.md +0 -168
- package/CONTRIBUTORS.md +0 -28
- package/COVERAGE.md +0 -135
- package/LIMITATIONS.md +0 -154
- package/SECURITY.md +0 -48
- package/THREAT-MODEL.md +0 -243
- package/docs/contribution-paths.md +0 -202
- package/docs/mirofish-prediction-guide.md +0 -304
- package/docs/quick-start.md +0 -245
- package/docs/rule-writing-guide.md +0 -647
- package/docs/schema-spec.md +0 -594
- package/examples/how-to-write-a-rule.md +0 -251
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionTracker - Tracks per-session state for behavioral detection operators.
|
|
3
|
+
*
|
|
4
|
+
* Enables multi-turn injection detection, call frequency tracking,
|
|
5
|
+
* and pattern repetition counting. All state is internal; public methods
|
|
6
|
+
* return copies to preserve immutability.
|
|
7
|
+
*
|
|
8
|
+
* @module agent-threat-rules/session-tracker
|
|
9
|
+
*/
|
|
10
|
+
/** Maximum number of events stored per session */
|
|
11
|
+
const MAX_EVENTS_PER_SESSION = 1000;
|
|
12
|
+
/** Maximum number of tracked sessions */
|
|
13
|
+
const MAX_SESSIONS = 10_000;
|
|
14
|
+
export class SessionTracker {
|
|
15
|
+
sessions = new Map();
|
|
16
|
+
/**
|
|
17
|
+
* Record an agent event for the given session.
|
|
18
|
+
* Extracts tool name and patterns from event fields/content.
|
|
19
|
+
*/
|
|
20
|
+
recordEvent(sessionId, event, patterns = []) {
|
|
21
|
+
this.ensureCapacity();
|
|
22
|
+
const state = this.getOrCreateSession(sessionId);
|
|
23
|
+
const toolName = this.extractToolName(event);
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
const tracked = {
|
|
26
|
+
event: Object.freeze({ ...event }),
|
|
27
|
+
recordedAt: now,
|
|
28
|
+
toolName,
|
|
29
|
+
patterns,
|
|
30
|
+
};
|
|
31
|
+
// Evict oldest if at capacity
|
|
32
|
+
if (state.events.length >= MAX_EVENTS_PER_SESSION) {
|
|
33
|
+
state.events = state.events.slice(1);
|
|
34
|
+
}
|
|
35
|
+
state.events = [...state.events, tracked];
|
|
36
|
+
state.lastActivityAt = now;
|
|
37
|
+
// Update call counts
|
|
38
|
+
if (toolName) {
|
|
39
|
+
const prev = state.callCounts.get(toolName) ?? 0;
|
|
40
|
+
state.callCounts.set(toolName, prev + 1);
|
|
41
|
+
}
|
|
42
|
+
// Update pattern counts
|
|
43
|
+
for (const p of patterns) {
|
|
44
|
+
const prev = state.patternCounts.get(p) ?? 0;
|
|
45
|
+
state.patternCounts.set(p, prev + 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the number of calls to a specific tool within a time window.
|
|
50
|
+
*/
|
|
51
|
+
getCallFrequency(sessionId, toolName, windowMs) {
|
|
52
|
+
const state = this.sessions.get(sessionId);
|
|
53
|
+
if (!state)
|
|
54
|
+
return 0;
|
|
55
|
+
const cutoff = Date.now() - windowMs;
|
|
56
|
+
let count = 0;
|
|
57
|
+
for (const tracked of state.events) {
|
|
58
|
+
if (tracked.recordedAt >= cutoff && tracked.toolName === toolName) {
|
|
59
|
+
count++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return count;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get the number of times a pattern has been observed within a time window.
|
|
66
|
+
*/
|
|
67
|
+
getPatternFrequency(sessionId, pattern, windowMs) {
|
|
68
|
+
const state = this.sessions.get(sessionId);
|
|
69
|
+
if (!state)
|
|
70
|
+
return 0;
|
|
71
|
+
const cutoff = Date.now() - windowMs;
|
|
72
|
+
let count = 0;
|
|
73
|
+
for (const tracked of state.events) {
|
|
74
|
+
if (tracked.recordedAt >= cutoff && tracked.patterns.includes(pattern)) {
|
|
75
|
+
count++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return count;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get total event count for a session, optionally within a time window.
|
|
82
|
+
*/
|
|
83
|
+
getEventCount(sessionId, windowMs) {
|
|
84
|
+
const state = this.sessions.get(sessionId);
|
|
85
|
+
if (!state)
|
|
86
|
+
return 0;
|
|
87
|
+
if (windowMs === undefined) {
|
|
88
|
+
return state.events.length;
|
|
89
|
+
}
|
|
90
|
+
const cutoff = Date.now() - windowMs;
|
|
91
|
+
let count = 0;
|
|
92
|
+
for (const tracked of state.events) {
|
|
93
|
+
if (tracked.recordedAt >= cutoff) {
|
|
94
|
+
count++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return count;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get an immutable snapshot of session state. Returns undefined if session does not exist.
|
|
101
|
+
*/
|
|
102
|
+
getSessionSnapshot(sessionId) {
|
|
103
|
+
const state = this.sessions.get(sessionId);
|
|
104
|
+
if (!state)
|
|
105
|
+
return undefined;
|
|
106
|
+
const oldest = state.events.length > 0 ? state.events[0].recordedAt : undefined;
|
|
107
|
+
const newest = state.events.length > 0 ? state.events[state.events.length - 1].recordedAt : undefined;
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
sessionId,
|
|
110
|
+
eventCount: state.events.length,
|
|
111
|
+
oldestEventTimestamp: oldest,
|
|
112
|
+
newestEventTimestamp: newest,
|
|
113
|
+
events: Object.freeze(state.events.map((e) => e.event)),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Evict sessions that have been inactive longer than maxAgeMs.
|
|
118
|
+
* Returns the number of sessions evicted.
|
|
119
|
+
*/
|
|
120
|
+
cleanup(maxAgeMs) {
|
|
121
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
122
|
+
let evicted = 0;
|
|
123
|
+
for (const [id, state] of this.sessions) {
|
|
124
|
+
if (state.lastActivityAt < cutoff) {
|
|
125
|
+
this.sessions.delete(id);
|
|
126
|
+
evicted++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return evicted;
|
|
130
|
+
}
|
|
131
|
+
/** Get the number of tracked sessions */
|
|
132
|
+
getSessionCount() {
|
|
133
|
+
return this.sessions.size;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Ensure we don't exceed the maximum session count.
|
|
137
|
+
* Evicts the oldest session if at capacity.
|
|
138
|
+
*/
|
|
139
|
+
ensureCapacity() {
|
|
140
|
+
if (this.sessions.size < MAX_SESSIONS)
|
|
141
|
+
return;
|
|
142
|
+
let oldestId;
|
|
143
|
+
let oldestTime = Infinity;
|
|
144
|
+
for (const [id, state] of this.sessions) {
|
|
145
|
+
if (state.lastActivityAt < oldestTime) {
|
|
146
|
+
oldestTime = state.lastActivityAt;
|
|
147
|
+
oldestId = id;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (oldestId) {
|
|
151
|
+
this.sessions.delete(oldestId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
getOrCreateSession(sessionId) {
|
|
155
|
+
const existing = this.sessions.get(sessionId);
|
|
156
|
+
if (existing)
|
|
157
|
+
return existing;
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
const state = {
|
|
160
|
+
events: [],
|
|
161
|
+
callCounts: new Map(),
|
|
162
|
+
patternCounts: new Map(),
|
|
163
|
+
createdAt: now,
|
|
164
|
+
lastActivityAt: now,
|
|
165
|
+
};
|
|
166
|
+
this.sessions.set(sessionId, state);
|
|
167
|
+
return state;
|
|
168
|
+
}
|
|
169
|
+
extractToolName(event) {
|
|
170
|
+
if (event.type === 'tool_call' || event.type === 'tool_response') {
|
|
171
|
+
return event.fields?.['tool_name'] ?? event.content;
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=session-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-tracker.js","sourceRoot":"","sources":["../src/session-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow Evaluator -- runs experimental rules without affecting verdict.
|
|
3
|
+
*
|
|
4
|
+
* Experimental rules evaluate against every event but don't influence
|
|
5
|
+
* the real verdict. Their match/no-match results are tracked to measure
|
|
6
|
+
* false positive rates. Rules with FP < threshold get promoted.
|
|
7
|
+
*
|
|
8
|
+
* @module agent-threat-rules/shadow-evaluator
|
|
9
|
+
*/
|
|
10
|
+
import type { ATRRule, ATRMatch, AgentEvent } from './types.js';
|
|
11
|
+
interface ShadowRuleStats {
|
|
12
|
+
readonly ruleId: string;
|
|
13
|
+
totalEvaluations: number;
|
|
14
|
+
totalMatches: number;
|
|
15
|
+
confirmedTruePositives: number;
|
|
16
|
+
confirmedFalsePositives: number;
|
|
17
|
+
firstSeen: number;
|
|
18
|
+
lastSeen: number;
|
|
19
|
+
}
|
|
20
|
+
export interface PromotionCandidate {
|
|
21
|
+
readonly rule: ATRRule;
|
|
22
|
+
readonly stats: Readonly<ShadowRuleStats>;
|
|
23
|
+
readonly fpRate: number;
|
|
24
|
+
}
|
|
25
|
+
export declare class ShadowEvaluator {
|
|
26
|
+
private readonly rules;
|
|
27
|
+
private readonly stats;
|
|
28
|
+
private readonly compiledPatterns;
|
|
29
|
+
/** Add an experimental rule for shadow evaluation */
|
|
30
|
+
addRule(rule: ATRRule): void;
|
|
31
|
+
/** Evaluate all shadow rules against an event (does NOT affect verdict) */
|
|
32
|
+
evaluate(event: AgentEvent): readonly ATRMatch[];
|
|
33
|
+
/** Record user feedback on a shadow match */
|
|
34
|
+
recordFeedback(ruleId: string, isTruePositive: boolean): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get rules ready for promotion.
|
|
37
|
+
* Criteria: FP rate < maxFPRate AND minimum evaluations reached.
|
|
38
|
+
*/
|
|
39
|
+
getPromotionCandidates(maxFPRate?: number, minEvaluations?: number): readonly PromotionCandidate[];
|
|
40
|
+
/** Get stats for a specific rule */
|
|
41
|
+
getStats(ruleId: string): Readonly<ShadowRuleStats> | undefined;
|
|
42
|
+
/** Get all shadow rule stats */
|
|
43
|
+
getAllStats(): ReadonlyMap<string, Readonly<ShadowRuleStats>>;
|
|
44
|
+
/** Number of shadow rules */
|
|
45
|
+
size(): number;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=shadow-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadow-evaluator.d.ts","sourceRoot":"","sources":["../src/shadow-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhE,UAAU,eAAe;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsC;IAC5D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAE9D,qDAAqD;IACrD,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAc5B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,QAAQ,EAAE;IAuDhD,6CAA6C;IAC7C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,IAAI;IAU7D;;;OAGG;IACH,sBAAsB,CACpB,SAAS,GAAE,MAAc,EACzB,cAAc,GAAE,MAAa,GAC5B,SAAS,kBAAkB,EAAE;IAuBhC,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,eAAe,CAAC,GAAG,SAAS;IAK/D,gCAAgC;IAChC,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAI7D,6BAA6B;IAC7B,IAAI,IAAI,MAAM;CAGf"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow Evaluator -- runs experimental rules without affecting verdict.
|
|
3
|
+
*
|
|
4
|
+
* Experimental rules evaluate against every event but don't influence
|
|
5
|
+
* the real verdict. Their match/no-match results are tracked to measure
|
|
6
|
+
* false positive rates. Rules with FP < threshold get promoted.
|
|
7
|
+
*
|
|
8
|
+
* @module agent-threat-rules/shadow-evaluator
|
|
9
|
+
*/
|
|
10
|
+
export class ShadowEvaluator {
|
|
11
|
+
rules = [];
|
|
12
|
+
stats = new Map();
|
|
13
|
+
compiledPatterns = new Map();
|
|
14
|
+
/** Add an experimental rule for shadow evaluation */
|
|
15
|
+
addRule(rule) {
|
|
16
|
+
if (this.rules.some((r) => r.id === rule.id))
|
|
17
|
+
return; // dedup
|
|
18
|
+
this.rules.push(rule);
|
|
19
|
+
this.stats.set(rule.id, {
|
|
20
|
+
ruleId: rule.id,
|
|
21
|
+
totalEvaluations: 0,
|
|
22
|
+
totalMatches: 0,
|
|
23
|
+
confirmedTruePositives: 0,
|
|
24
|
+
confirmedFalsePositives: 0,
|
|
25
|
+
firstSeen: Date.now(),
|
|
26
|
+
lastSeen: Date.now(),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** Evaluate all shadow rules against an event (does NOT affect verdict) */
|
|
30
|
+
evaluate(event) {
|
|
31
|
+
const matches = [];
|
|
32
|
+
for (const rule of this.rules) {
|
|
33
|
+
const stat = this.stats.get(rule.id);
|
|
34
|
+
if (!stat)
|
|
35
|
+
continue;
|
|
36
|
+
stat.totalEvaluations++;
|
|
37
|
+
stat.lastSeen = Date.now();
|
|
38
|
+
// Simple regex evaluation for shadow rules
|
|
39
|
+
const detection = rule.detection;
|
|
40
|
+
const conditions = Array.isArray(detection.conditions)
|
|
41
|
+
? detection.conditions
|
|
42
|
+
: Object.values(detection.conditions);
|
|
43
|
+
let matched = false;
|
|
44
|
+
for (const cond of conditions) {
|
|
45
|
+
const c = cond;
|
|
46
|
+
const value = c['value'];
|
|
47
|
+
const field = c['field'];
|
|
48
|
+
if (!value || !field)
|
|
49
|
+
continue;
|
|
50
|
+
const text = event.fields?.[field] ?? event.content ?? '';
|
|
51
|
+
try {
|
|
52
|
+
let regex = this.compiledPatterns.get(value);
|
|
53
|
+
if (!regex) {
|
|
54
|
+
regex = new RegExp(value, 'i');
|
|
55
|
+
this.compiledPatterns.set(value, regex);
|
|
56
|
+
}
|
|
57
|
+
if (regex.test(text)) {
|
|
58
|
+
matched = true;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Invalid regex
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (matched) {
|
|
67
|
+
stat.totalMatches++;
|
|
68
|
+
matches.push({
|
|
69
|
+
rule,
|
|
70
|
+
matchedConditions: ['shadow'],
|
|
71
|
+
matchedPatterns: ['shadow-match'],
|
|
72
|
+
confidence: 0.5, // Shadow matches have reduced confidence
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
scan_context: 'native',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return matches;
|
|
79
|
+
}
|
|
80
|
+
/** Record user feedback on a shadow match */
|
|
81
|
+
recordFeedback(ruleId, isTruePositive) {
|
|
82
|
+
const stat = this.stats.get(ruleId);
|
|
83
|
+
if (!stat)
|
|
84
|
+
return;
|
|
85
|
+
if (isTruePositive) {
|
|
86
|
+
stat.confirmedTruePositives++;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
stat.confirmedFalsePositives++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get rules ready for promotion.
|
|
94
|
+
* Criteria: FP rate < maxFPRate AND minimum evaluations reached.
|
|
95
|
+
*/
|
|
96
|
+
getPromotionCandidates(maxFPRate = 0.001, minEvaluations = 1000) {
|
|
97
|
+
const candidates = [];
|
|
98
|
+
for (const rule of this.rules) {
|
|
99
|
+
const stat = this.stats.get(rule.id);
|
|
100
|
+
if (!stat || stat.totalEvaluations < minEvaluations)
|
|
101
|
+
continue;
|
|
102
|
+
const fpRate = stat.totalMatches > 0
|
|
103
|
+
? stat.confirmedFalsePositives / stat.totalMatches
|
|
104
|
+
: 0;
|
|
105
|
+
// Only promote if:
|
|
106
|
+
// 1. Has been evaluated enough times
|
|
107
|
+
// 2. FP rate is below threshold
|
|
108
|
+
// 3. Has at least some matches (not a dead rule)
|
|
109
|
+
if (fpRate <= maxFPRate && stat.totalMatches > 0) {
|
|
110
|
+
candidates.push({ rule, stats: { ...stat }, fpRate });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return candidates;
|
|
114
|
+
}
|
|
115
|
+
/** Get stats for a specific rule */
|
|
116
|
+
getStats(ruleId) {
|
|
117
|
+
const stat = this.stats.get(ruleId);
|
|
118
|
+
return stat ? { ...stat } : undefined;
|
|
119
|
+
}
|
|
120
|
+
/** Get all shadow rule stats */
|
|
121
|
+
getAllStats() {
|
|
122
|
+
return new Map(Array.from(this.stats.entries()).map(([k, v]) => [k, { ...v }]));
|
|
123
|
+
}
|
|
124
|
+
/** Number of shadow rules */
|
|
125
|
+
size() {
|
|
126
|
+
return this.rules.length;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=shadow-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadow-evaluator.js","sourceRoot":"","sources":["../src/shadow-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
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 type { AgentEvent } from './types.js';
|
|
14
|
+
/** Behavioral capabilities observed for a skill */
|
|
15
|
+
interface SkillCapabilities {
|
|
16
|
+
/** Seen filesystem operations (read/write/delete) */
|
|
17
|
+
readonly filesystemOps: ReadonlySet<string>;
|
|
18
|
+
/** Seen network destinations (hostnames) */
|
|
19
|
+
readonly networkTargets: ReadonlySet<string>;
|
|
20
|
+
/** Seen environment variable accesses */
|
|
21
|
+
readonly envAccesses: ReadonlySet<string>;
|
|
22
|
+
/** Seen child process executions */
|
|
23
|
+
readonly processExecs: ReadonlySet<string>;
|
|
24
|
+
/** Seen output patterns (categories: data, error, redirect, exfiltration) */
|
|
25
|
+
readonly outputPatterns: ReadonlySet<string>;
|
|
26
|
+
}
|
|
27
|
+
/** Immutable fingerprint snapshot */
|
|
28
|
+
export interface SkillFingerprint {
|
|
29
|
+
readonly skillName: string;
|
|
30
|
+
readonly invocationCount: number;
|
|
31
|
+
readonly firstSeen: number;
|
|
32
|
+
readonly lastSeen: number;
|
|
33
|
+
readonly isStable: boolean;
|
|
34
|
+
readonly capabilities: SkillCapabilities;
|
|
35
|
+
/** Hash of capabilities for quick comparison */
|
|
36
|
+
readonly capabilityHash: string;
|
|
37
|
+
}
|
|
38
|
+
/** Anomaly when behavior deviates from fingerprint */
|
|
39
|
+
export interface BehaviorAnomaly {
|
|
40
|
+
readonly skillName: string;
|
|
41
|
+
readonly anomalyType: 'new_filesystem_op' | 'new_network_target' | 'new_env_access' | 'new_process_exec' | 'new_output_pattern' | 'capability_expansion';
|
|
42
|
+
readonly description: string;
|
|
43
|
+
readonly severity: 'low' | 'medium' | 'high' | 'critical';
|
|
44
|
+
readonly newValue: string;
|
|
45
|
+
readonly timestamp: number;
|
|
46
|
+
}
|
|
47
|
+
export interface SkillFingerprintConfig {
|
|
48
|
+
/** Minimum invocations before fingerprint can stabilize (default: 10) */
|
|
49
|
+
stabilityThreshold?: number;
|
|
50
|
+
/** Consecutive clean invocations to mark stable (default: 5) */
|
|
51
|
+
stableStreak?: number;
|
|
52
|
+
}
|
|
53
|
+
export declare class SkillFingerprintStore {
|
|
54
|
+
private readonly fingerprints;
|
|
55
|
+
private readonly stabilityThreshold;
|
|
56
|
+
private readonly stableStreak;
|
|
57
|
+
constructor(config?: SkillFingerprintConfig);
|
|
58
|
+
/**
|
|
59
|
+
* Record a skill invocation and detect behavioral anomalies.
|
|
60
|
+
* Returns anomalies if the fingerprint was stable and new capabilities appeared.
|
|
61
|
+
*/
|
|
62
|
+
recordInvocation(skillName: string, event: AgentEvent): readonly BehaviorAnomaly[];
|
|
63
|
+
/**
|
|
64
|
+
* Get an immutable fingerprint snapshot for a skill.
|
|
65
|
+
*/
|
|
66
|
+
getFingerprint(skillName: string): SkillFingerprint | undefined;
|
|
67
|
+
/** Get all tracked skill names */
|
|
68
|
+
getTrackedSkills(): string[];
|
|
69
|
+
/** Get count of stable fingerprints */
|
|
70
|
+
getStableCount(): number;
|
|
71
|
+
/** Get total tracked skills */
|
|
72
|
+
getTrackedCount(): number;
|
|
73
|
+
/**
|
|
74
|
+
* Reset a skill's fingerprint (e.g., after a legitimate update).
|
|
75
|
+
*/
|
|
76
|
+
resetFingerprint(skillName: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Evict fingerprints not seen since cutoffMs ago.
|
|
79
|
+
*/
|
|
80
|
+
cleanup(cutoffMs: number): number;
|
|
81
|
+
private getOrCreate;
|
|
82
|
+
private computeCapabilityHash;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=skill-fingerprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-fingerprint.d.ts","sourceRoot":"","sources":["../src/skill-fingerprint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAU7C,mDAAmD;AACnD,UAAU,iBAAiB;IACzB,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,4CAA4C;IAC5C,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,yCAAyC;IACzC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,oCAAoC;IACpC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC,gDAAgD;IAChD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAChB,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,sBAAsB,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAsDD,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyC;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,MAAM,CAAC,EAAE,sBAAsB;IAK3C;;;OAGG;IACH,gBAAgB,CACd,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,GAChB,SAAS,eAAe,EAAE;IA8H7B;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAqB/D,kCAAkC;IAClC,gBAAgB,IAAI,MAAM,EAAE;IAI5B,uCAAuC;IACvC,cAAc,IAAI,MAAM;IAQxB,+BAA+B;IAC/B,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIzC;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAgBjC,OAAO,CAAC,WAAW;IAkCnB,OAAO,CAAC,qBAAqB;CAU9B"}
|