@sandrobuilds/tracerney 0.9.21 → 0.9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -139
- package/dist/application/ShieldApplicationService.d.ts +8 -0
- package/dist/application/ShieldApplicationService.d.ts.map +1 -1
- package/dist/application/ShieldApplicationService.js +48 -2
- package/dist/application/ShieldApplicationService.js.map +1 -1
- package/dist/domain/events/SecurityEventType.d.ts +5 -1
- package/dist/domain/events/SecurityEventType.d.ts.map +1 -1
- package/dist/domain/events/SecurityEventType.js +4 -0
- package/dist/domain/events/SecurityEventType.js.map +1 -1
- package/dist/domain/pii/DeterministicFilter.d.ts +79 -0
- package/dist/domain/pii/DeterministicFilter.d.ts.map +1 -0
- package/dist/domain/pii/DeterministicFilter.js +151 -0
- package/dist/domain/pii/DeterministicFilter.js.map +1 -0
- package/dist/domain/pii/PIIPattern.d.ts +41 -0
- package/dist/domain/pii/PIIPattern.d.ts.map +1 -0
- package/dist/domain/pii/PIIPattern.js +253 -0
- package/dist/domain/pii/PIIPattern.js.map +1 -0
- package/dist/domain/pii/index.d.ts +9 -0
- package/dist/domain/pii/index.d.ts.map +1 -0
- package/dist/domain/pii/index.js +7 -0
- package/dist/domain/pii/index.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tracerney
|
|
2
2
|
|
|
3
|
-
Lightweight prompt injection detection for LLM applications. Runs 100% locally with zero data leaving your server.
|
|
3
|
+
Lightweight prompt injection detection and outbound PII/secret filtering for LLM applications. Runs 100% locally with zero data leaving your server.
|
|
4
4
|
|
|
5
5
|
> 🚀 **Explore the full platform at [tracerney.com](https://www.tracerney.com)** — includes dashboard, analytics, API management, and team collaboration tools.
|
|
6
6
|
|
|
@@ -10,199 +10,194 @@ Lightweight prompt injection detection for LLM applications. Runs 100% locally w
|
|
|
10
10
|
npm install @sandrobuilds/tracerney
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Layer 1: The Deterministic Filter
|
|
16
|
+
|
|
17
|
+
A high-speed, synchronous sensor that scans every LLM **response** before it reaches your user. Target latency: **<5ms**. No LLM needed — pure regex.
|
|
18
|
+
|
|
19
|
+
The SDK never decides for you. It labels every finding and hands you the keys.
|
|
20
|
+
|
|
21
|
+
### The `validate()` API
|
|
14
22
|
|
|
15
23
|
```typescript
|
|
16
24
|
import { Tracerney } from '@sandrobuilds/tracerney';
|
|
17
25
|
|
|
18
|
-
const
|
|
26
|
+
const tracerney = new Tracerney();
|
|
19
27
|
|
|
20
|
-
const
|
|
28
|
+
const trace = tracerney.validate(agentOutput);
|
|
21
29
|
|
|
22
|
-
if (
|
|
23
|
-
console.log(
|
|
24
|
-
|
|
30
|
+
if (trace.isSuspicious) {
|
|
31
|
+
console.log(`[${trace.label}]: ${trace.reason}`);
|
|
32
|
+
|
|
33
|
+
// Option A: Hard block
|
|
34
|
+
if (trace.label === 'SUSPICIOUS_EGRESS') {
|
|
35
|
+
throw new Error('Security Policy Violation');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Option B: Surgical scrub — return pre-computed redacted version
|
|
39
|
+
return trace.redactedContent;
|
|
25
40
|
}
|
|
26
41
|
```
|
|
27
42
|
|
|
28
|
-
|
|
43
|
+
### The Suspicious Manifest
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
- **Local detection** — <5ms latency per prompt, zero network overhead
|
|
32
|
-
- **Zero dependencies** — single npm package
|
|
33
|
-
- **Privacy-first** — no data leaves your server, 100% local processing
|
|
45
|
+
Every finding is labeled so you can route your own reaction:
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
| Trigger | Label | `redactedContent` token | Recommended action |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| Email / Phone | `SUSPICIOUS_PII` | `[SUSPICIOUS_PII:EMAIL]` | Usually Redact |
|
|
50
|
+
| API Keys / SSH / CC / SSN | `SUSPICIOUS_SECRET` | `[SUSPICIOUS_SECRET:ANTHROPIC_API_KEY]` | Usually Block |
|
|
51
|
+
| External URL smuggling | `SUSPICIOUS_EGRESS` | `[SUSPICIOUS_EGRESS:MARKDOWN_IMAGE_SMUGGLING]` | Always Block |
|
|
52
|
+
| Zero-width / BiDi / Base64 | `SUSPICIOUS_ENCODING` | `[SUSPICIOUS_ENCODING:BIDI_OVERRIDE]` | Audit / Block |
|
|
36
53
|
|
|
37
|
-
###
|
|
38
|
-
```typescript
|
|
39
|
-
{
|
|
40
|
-
suspicious: boolean; // true if pattern matched
|
|
41
|
-
patternName?: string; // e.g., "Ignore Instructions"
|
|
42
|
-
severity?: string; // "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"
|
|
43
|
-
blocked: boolean; // false (Layer 1 only marks suspicious)
|
|
44
|
-
}
|
|
45
|
-
```
|
|
54
|
+
### `SuspiciousTrace` object
|
|
46
55
|
|
|
47
|
-
### Layer 2 (LLM Sentinel)
|
|
48
56
|
```typescript
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
const trace = tracerney.validate(agentOutput);
|
|
58
|
+
|
|
59
|
+
trace.isSuspicious // boolean — true if any pattern matched
|
|
60
|
+
trace.label // 'SUSPICIOUS_PII' | 'SUSPICIOUS_SECRET' | 'SUSPICIOUS_EGRESS' | 'SUSPICIOUS_ENCODING' | null
|
|
61
|
+
trace.reason // "Detected 2 finding(s): Email Address, AWS Access Key ID"
|
|
62
|
+
trace.redactedContent // pre-scrubbed version of the input — use it or throw, your call
|
|
63
|
+
trace.findings // full per-pattern breakdown for logging/telemetry
|
|
55
64
|
```
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
### What Layer 1 detects
|
|
67
|
+
|
|
68
|
+
**`SUSPICIOUS_PII`** — Accidental personal data exposure
|
|
69
|
+
- Email addresses
|
|
70
|
+
- US phone numbers
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
- Code execution risks
|
|
65
|
-
- And 254 more...
|
|
72
|
+
**`SUSPICIOUS_SECRET`** — High-value credential leaks
|
|
73
|
+
- Anthropic, OpenAI, Stripe, GitHub, AWS, Google, Slack, SendGrid, Twilio API keys
|
|
74
|
+
- SSH / PEM private key blocks
|
|
75
|
+
- Credit card numbers (Visa, Mastercard, Amex, Discover)
|
|
76
|
+
- US Social Security Numbers
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
**`SUSPICIOUS_EGRESS`** — Active data exfiltration attempts
|
|
79
|
+
- Markdown image tags with URL query params: ``
|
|
80
|
+
- Markdown links smuggling data via query params
|
|
81
|
+
- Credential-embedded URLs: `https://user:password@hostname`
|
|
82
|
+
- Base64 payloads in URL parameters
|
|
68
83
|
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
- Local processing only
|
|
74
|
-
- Detects: instruction overrides, role-play jailbreaks, context confusion, code execution risks, data extraction attempts, and more
|
|
84
|
+
**`SUSPICIOUS_ENCODING`** — Obfuscation and hidden data
|
|
85
|
+
- Zero-width characters (`\u200B`, `\u200C`, `\uFEFF`, etc.)
|
|
86
|
+
- Unicode bidirectional override characters (BiDi attacks)
|
|
87
|
+
- Standalone base64 blobs (≥120 chars) outside of URLs
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
- **AI-powered response verification** — LLM-based analysis for novel attack patterns
|
|
78
|
-
- **Context-aware scanning** — understands your application's specific security policies
|
|
79
|
-
- **Delimiter salting** — prevents prompt injection through response boundaries
|
|
80
|
-
- **Zero prompt storage** — responses are analyzed in-memory, never saved or logged
|
|
81
|
-
- **Structured threat metadata** — detailed fingerprints for audit trails and tracking
|
|
82
|
-
- **Advanced rate limiting** — prevents cost spikes with intelligent throttling
|
|
89
|
+
### Developer decision guide
|
|
83
90
|
|
|
84
|
-
|
|
91
|
+
```typescript
|
|
92
|
+
const trace = tracerney.validate(agentOutput);
|
|
93
|
+
|
|
94
|
+
if (!trace.isSuspicious) {
|
|
95
|
+
return agentOutput; // clean
|
|
96
|
+
}
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
switch (trace.label) {
|
|
99
|
+
|
|
100
|
+
case 'SUSPICIOUS_PII':
|
|
101
|
+
// Low risk — agent is being too "talkative"
|
|
102
|
+
// Redact and continue; don't break the user's flow
|
|
103
|
+
return trace.redactedContent;
|
|
104
|
+
|
|
105
|
+
case 'SUSPICIOUS_SECRET':
|
|
106
|
+
// High risk — agent leaked a credential
|
|
107
|
+
// Redact before sending, fire an alert
|
|
108
|
+
myAlerts.fire({ label: trace.label, reason: trace.reason });
|
|
109
|
+
return trace.redactedContent;
|
|
110
|
+
|
|
111
|
+
case 'SUSPICIOUS_EGRESS':
|
|
112
|
+
// Critical — agent is trying to exfiltrate data
|
|
113
|
+
// Kill the process. The caller gets nothing.
|
|
114
|
+
throw new SecurityError('Egress attack detected');
|
|
115
|
+
|
|
116
|
+
case 'SUSPICIOUS_ENCODING':
|
|
117
|
+
// Critical — hidden/obfuscated payload
|
|
118
|
+
// Audit and block; send for review
|
|
119
|
+
myAlerts.fire({ label: trace.label, reason: trace.reason });
|
|
120
|
+
throw new SecurityError('Encoding attack detected');
|
|
121
|
+
}
|
|
122
|
+
```
|
|
87
123
|
|
|
88
|
-
|
|
124
|
+
---
|
|
89
125
|
|
|
90
|
-
|
|
91
|
-
|---|---|
|
|
92
|
-
| Local pattern matching | Server-side verification |
|
|
93
|
-
| 258 attack patterns | Output validation |
|
|
94
|
-
| <5ms latency | JSON safety checks |
|
|
95
|
-
| No data leaves device | Delimiter salting |
|
|
96
|
-
| Zero network calls | Context-aware analysis |
|
|
126
|
+
## Layer 1 + LLM Pipeline: `wrap()`
|
|
97
127
|
|
|
98
|
-
|
|
128
|
+
If you want automatic protection wired into your LLM call, use `wrap()`. It runs Layer 1 on the response before returning it to you, and applies the same label-based routing:
|
|
99
129
|
|
|
100
|
-
|
|
130
|
+
- `SUSPICIOUS_EGRESS` → throws `ShieldBlockError` (caller gets nothing)
|
|
131
|
+
- `SUSPICIOUS_SECRET` / `SUSPICIOUS_ENCODING` → emits a `PII_LEAK` telemetry event, returns scrubbed response
|
|
132
|
+
- `SUSPICIOUS_PII` → returns scrubbed response silently
|
|
101
133
|
|
|
102
134
|
```typescript
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
135
|
+
const response = await tracerney.wrap(
|
|
136
|
+
() => openai.chat.completions.create({ model: 'gpt-4o', messages }),
|
|
137
|
+
{ prompt: userInput } // optional: also scan the inbound prompt
|
|
138
|
+
);
|
|
139
|
+
// response.choices[0].message.content is already scrubbed
|
|
107
140
|
```
|
|
108
141
|
|
|
109
|
-
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Layer 2: LLM Sentinel
|
|
145
|
+
|
|
146
|
+
Layer 2 adds AI-powered verification for novel attack patterns not covered by regex. It runs after Layer 1 marks a prompt suspicious, using a hosted LLM to confirm or clear the threat.
|
|
110
147
|
|
|
111
|
-
|
|
148
|
+
**Layer 1 (Free)** | **Layer 2 (Pro — $9/month)**
|
|
149
|
+
---|---
|
|
150
|
+
Local regex, <5ms | Server-side LLM analysis
|
|
151
|
+
258 attack patterns | Context-aware threat detection
|
|
152
|
+
No network calls | Zero prompt storage
|
|
153
|
+
Always on | Activates only on Layer 1 hits
|
|
112
154
|
|
|
113
|
-
|
|
155
|
+
### Enable Layer 2
|
|
114
156
|
|
|
115
157
|
```typescript
|
|
116
|
-
const
|
|
158
|
+
const tracerney = new Tracerney({
|
|
117
159
|
apiKey: process.env.TRACERNEY_API_KEY,
|
|
118
160
|
sentinelEnabled: true,
|
|
119
|
-
baseUrl: process.env.TRACERNEY_BASE_URL, // e.g., http://localhost:3000 or https://myapp.com
|
|
120
|
-
sentinelEndpoint: process.env.TRACERNEY_SENTINEL_ENDPOINT, // e.g., /api/v1/verify-prompt
|
|
121
161
|
});
|
|
122
162
|
```
|
|
123
163
|
|
|
124
|
-
|
|
164
|
+
### Layer 2 response format
|
|
125
165
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
166
|
+
```json
|
|
167
|
+
// Safe
|
|
168
|
+
{ "action": "ALLOW", "confidence": 0.15, "class": "safe_content", "fingerprint": "a3f7k2" }
|
|
129
169
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Scan input (Layer 1 + Layer 2)
|
|
133
|
-
const result = await tracer.scanPrompt(userInput);
|
|
134
|
-
// If we get here, input is safe. Call LLM
|
|
135
|
-
const llmResponse = await llm.chat(userInput);
|
|
136
|
-
// Verify LLM output wasn't compromised
|
|
137
|
-
const outputCheck = await tracer.verifyOutput(llmResponse);
|
|
138
|
-
return llmResponse;
|
|
139
|
-
} catch (err) {
|
|
140
|
-
if (err instanceof ShieldBlockError) {
|
|
141
|
-
return NextResponse.json(
|
|
142
|
-
{ error: "Input content is flagged as suspicious" },
|
|
143
|
-
{ status: 400 }
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
throw err;
|
|
147
|
-
}
|
|
170
|
+
// Blocked
|
|
171
|
+
{ "action": "BLOCK", "confidence": 0.99, "class": "jailbreak_semantic_pattern", "fingerprint": "c1p5n3" }
|
|
148
172
|
```
|
|
149
173
|
|
|
150
|
-
|
|
174
|
+
---
|
|
151
175
|
|
|
152
|
-
|
|
176
|
+
## Inbound prompt scanning: `scanPrompt()`
|
|
153
177
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
{
|
|
157
|
-
"action": "ALLOW",
|
|
158
|
-
"confidence": 0.15,
|
|
159
|
-
"class": "safe_content",
|
|
160
|
-
"fingerprint": "a3f7k2"
|
|
161
|
-
}
|
|
162
|
-
```
|
|
178
|
+
```typescript
|
|
179
|
+
const result = await tracerney.scanPrompt(userInput);
|
|
163
180
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
{
|
|
167
|
-
"action": "BLOCK",
|
|
168
|
-
"confidence": 0.99,
|
|
169
|
-
"class": "jailbreak_semantic_pattern",
|
|
170
|
-
"fingerprint": "c1p5n3"
|
|
181
|
+
if (result.suspicious) {
|
|
182
|
+
console.log('Suspicious:', result.patternName, result.severity);
|
|
171
183
|
}
|
|
172
184
|
```
|
|
173
185
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"scansUsed": 50,
|
|
180
|
-
"limit": 50,
|
|
181
|
-
"message": "Free plan limit reached (50/month)..."
|
|
182
|
-
}
|
|
186
|
+
```typescript
|
|
187
|
+
result.suspicious // boolean
|
|
188
|
+
result.patternName // e.g. "Ignore Instructions"
|
|
189
|
+
result.severity // "CRITICAL" | "HIGH" | "MEDIUM" | "LOW"
|
|
190
|
+
result.blocked // true only if Layer 2 confirmed the attack
|
|
183
191
|
```
|
|
184
192
|
|
|
185
193
|
---
|
|
186
194
|
|
|
187
|
-
## Pricing
|
|
188
|
-
|
|
189
|
-
- **Free Tier:** 50 scans/month with Layer 1 pattern detection
|
|
190
|
-
- **Pro Tier:** 2,500 scans/month with Layer 1 + Layer 2 LLM verification ($9/month)
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
## Ready for Advanced Protection?
|
|
195
|
-
|
|
196
|
-
Layer 2 (LLM Sentinel) adds AI-powered verification with **context-aware** threat detection and **zero prompt storage** — all responses are analyzed in-memory and immediately discarded.
|
|
195
|
+
## Pricing
|
|
197
196
|
|
|
198
|
-
**
|
|
197
|
+
- **Free:** 50 scans/month — Layer 1 pattern detection
|
|
198
|
+
- **Pro ($9/month):** 2,500 scans/month — Layer 1 + Layer 2 LLM Sentinel
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
- Dashboard with threat analytics
|
|
202
|
-
- API key management
|
|
203
|
-
- Team collaboration features
|
|
204
|
-
- Detailed threat fingerprints for compliance
|
|
205
|
-
- Priority support for Pro members
|
|
200
|
+
**[Start free or upgrade at tracerney.com](https://www.tracerney.com/docs)**
|
|
206
201
|
|
|
207
202
|
---
|
|
208
203
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Core orchestrator: wires domain services + infrastructure adapters
|
|
4
4
|
* Fixes all 5 integration gaps from the original design
|
|
5
5
|
*/
|
|
6
|
+
import { type SuspiciousTrace } from "../domain/pii/DeterministicFilter";
|
|
6
7
|
import { type ToolPolicy } from "../domain/guard/ToolPolicy";
|
|
7
8
|
import { ILLMProvider, type LLMResponse } from "./ports/ILLMProvider";
|
|
8
9
|
import { ISentinel } from "./ports/ISentinel";
|
|
@@ -30,6 +31,7 @@ export interface ScanResult {
|
|
|
30
31
|
severity?: string;
|
|
31
32
|
blocked: boolean;
|
|
32
33
|
}
|
|
34
|
+
export type { SuspiciousTrace };
|
|
33
35
|
export interface ServiceStatus {
|
|
34
36
|
patternMatcher: {
|
|
35
37
|
ready: boolean;
|
|
@@ -46,6 +48,7 @@ export interface ServiceStatus {
|
|
|
46
48
|
export declare class ShieldApplicationService {
|
|
47
49
|
private readonly config;
|
|
48
50
|
private patternMatcher;
|
|
51
|
+
private readonly deterministicFilter;
|
|
49
52
|
private toolGuard;
|
|
50
53
|
private telemetrySink?;
|
|
51
54
|
private llmProvider?;
|
|
@@ -76,6 +79,11 @@ export declare class ShieldApplicationService {
|
|
|
76
79
|
* - Jitter: Add random delay to obfuscate timing
|
|
77
80
|
*/
|
|
78
81
|
scanPrompt(prompt: string, requestId?: string): Promise<ScanResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Layer 1: Scan any string for suspicious content.
|
|
84
|
+
* Returns a SuspiciousTrace — the SDK never decides for you.
|
|
85
|
+
*/
|
|
86
|
+
validate(text: string): SuspiciousTrace;
|
|
79
87
|
/**
|
|
80
88
|
* Update tool policy at runtime
|
|
81
89
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShieldApplicationService.d.ts","sourceRoot":"","sources":["../../src/application/ShieldApplicationService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"ShieldApplicationService.d.ts","sourceRoot":"","sources":["../../src/application/ShieldApplicationService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAuB,KAAK,eAAe,EAAsB,MAAM,mCAAmC,CAAC;AAElH,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAO/E,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAyB,MAAM,6BAA6B,CAAC;AAIvF,MAAM,WAAW,8BAA8B;IAC7C,iBAAiB,EAAE,kBAAkB,CAAC;IACtC,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE;QACd,KAAK,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,SAAS,EAAE;QACT,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;KACjC,CAAC;IACF,SAAS,EAAE;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED,qBAAa,wBAAwB;IAUvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAsB;IAC1D,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,CAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAEP,MAAM,EAAE,8BAA8B;IAkBnE;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAmB5B;;;;;;OAMG;IACG,IAAI,CAAC,CAAC,SAAS,WAAW,EAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,CAAC,CAAC;IA6Gb;;;;;;;;;OASG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAmFzE;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe;IAIvC;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAK/C;;OAEG;IACH,SAAS,IAAI,aAAa;IAgB1B;;OAEG;IACH,OAAO,CAAC,MAAM;IAMd;;OAEG;IACH,OAAO,IAAI,IAAI;IAIf;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAG1B"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Fixes all 5 integration gaps from the original design
|
|
5
5
|
*/
|
|
6
6
|
import { PatternMatcher } from '../domain/detection/PatternMatcher.js';
|
|
7
|
+
import { DeterministicFilter } from '../domain/pii/DeterministicFilter.js';
|
|
7
8
|
import { ToolGuard } from '../domain/guard/ToolGuard.js';
|
|
8
9
|
import { createToolPolicy } from '../domain/guard/ToolPolicy.js';
|
|
9
10
|
import { createSecurityEvent, SecurityEventType, ThreatSeverity, } from '../domain/events/index.js';
|
|
@@ -20,6 +21,8 @@ export class ShieldApplicationService {
|
|
|
20
21
|
this.sentinel = config.sentinel; // Layer 2
|
|
21
22
|
// this.shadowLogSink = config.shadowLogSink; // Disabled for now
|
|
22
23
|
this.sdkVersion = config.sdkVersion ?? "0.2.0";
|
|
24
|
+
// Layer 1: deterministic outbound filter (singleton, stateless)
|
|
25
|
+
this.deterministicFilter = new DeterministicFilter();
|
|
23
26
|
// Initialize patterns synchronously (bundled patterns always available)
|
|
24
27
|
this.patternMatcher = new PatternMatcher(BUNDLED_PATTERNS);
|
|
25
28
|
// Load remote patterns in background if configured (zero-day updates)
|
|
@@ -63,9 +66,45 @@ export class ShieldApplicationService {
|
|
|
63
66
|
// Layer 1 just marks suspicious=true, doesn't block
|
|
64
67
|
}
|
|
65
68
|
// Execute the LLM call
|
|
66
|
-
const
|
|
67
|
-
//
|
|
69
|
+
const rawResponse = await llmCall();
|
|
70
|
+
// ── Layer 1 (Outbound): Deterministic Filter ─────────────────────────
|
|
71
|
+
// Synchronous regex pass — <5ms, runs before the caller ever sees the text.
|
|
72
|
+
//
|
|
73
|
+
// The filter is a SENSOR, not a policy enforcer.
|
|
74
|
+
// It labels every finding with a SuspiciousLabel and pre-computes redactedContent.
|
|
75
|
+
// ShieldApplicationService acts on the label:
|
|
76
|
+
//
|
|
77
|
+
// SUSPICIOUS_EGRESS → throw (active exfiltration, caller gets nothing)
|
|
78
|
+
// SUSPICIOUS_SECRET → emit PII_LEAK alert, return redacted response
|
|
79
|
+
// SUSPICIOUS_ENCODING → emit PII_LEAK alert, return redacted response
|
|
80
|
+
// SUSPICIOUS_PII → return redacted response silently
|
|
81
|
+
const { response, outcome: filterOutcome } = this.deterministicFilter.filterResponse(rawResponse);
|
|
68
82
|
const latencyMs = Date.now() - startTime;
|
|
83
|
+
const trace = filterOutcome.trace;
|
|
84
|
+
if (trace.isSuspicious && trace.label) {
|
|
85
|
+
if (trace.label === "SUSPICIOUS_EGRESS") {
|
|
86
|
+
// Active exfiltration — kill the process, caller gets nothing.
|
|
87
|
+
const event = createSecurityEvent(requestId, SecurityEventType.SUSPICIOUS_EGRESS, ThreatSeverity.CRITICAL, `Layer 1: ${trace.reason}`, {
|
|
88
|
+
patternName: trace.findings[0]?.patternName,
|
|
89
|
+
blockLatencyMs: latencyMs,
|
|
90
|
+
modelName: options?.modelName,
|
|
91
|
+
provider: options?.provider,
|
|
92
|
+
}, startTime);
|
|
93
|
+
this.report(event);
|
|
94
|
+
throw new ShieldBlockError("Tracerney Block: Suspicious Egress Detected", event);
|
|
95
|
+
}
|
|
96
|
+
if (trace.label === "SUSPICIOUS_SECRET" || trace.label === "SUSPICIOUS_ENCODING") {
|
|
97
|
+
// Accidental leak of high-value data — alert, but return the scrubbed response.
|
|
98
|
+
const event = createSecurityEvent(requestId, SecurityEventType.PII_LEAK, ThreatSeverity.HIGH, `Layer 1: ${trace.reason}`, {
|
|
99
|
+
patternName: trace.findings[0]?.patternName,
|
|
100
|
+
blockLatencyMs: latencyMs,
|
|
101
|
+
modelName: options?.modelName,
|
|
102
|
+
provider: options?.provider,
|
|
103
|
+
}, startTime);
|
|
104
|
+
this.report(event);
|
|
105
|
+
}
|
|
106
|
+
// SUSPICIOUS_PII (email/phone) → return scrubbed response silently, no event.
|
|
107
|
+
}
|
|
69
108
|
// Validate tool calls against policy
|
|
70
109
|
const toolCalls = response.choices?.[0]?.message?.tool_calls;
|
|
71
110
|
const violation = this.toolGuard.validate(toolCalls, requestId);
|
|
@@ -171,6 +210,13 @@ export class ShieldApplicationService {
|
|
|
171
210
|
await jitter();
|
|
172
211
|
}
|
|
173
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Layer 1: Scan any string for suspicious content.
|
|
215
|
+
* Returns a SuspiciousTrace — the SDK never decides for you.
|
|
216
|
+
*/
|
|
217
|
+
validate(text) {
|
|
218
|
+
return this.deterministicFilter.validate(text);
|
|
219
|
+
}
|
|
174
220
|
/**
|
|
175
221
|
* Update tool policy at runtime
|
|
176
222
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShieldApplicationService.js","sourceRoot":"","sources":["../../src/application/ShieldApplicationService.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAiB,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAmB,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GAEf,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,qDAAqD,CAAC;
|
|
1
|
+
{"version":3,"file":"ShieldApplicationService.js","sourceRoot":"","sources":["../../src/application/ShieldApplicationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,11 @@ export declare enum SecurityEventType {
|
|
|
6
6
|
PROMPT_INJECTION = "PROMPT_INJECTION",
|
|
7
7
|
UNAUTHORIZED_TOOL = "UNAUTHORIZED_TOOL",
|
|
8
8
|
PATTERN_MATCH = "PATTERN_MATCH",
|
|
9
|
-
SCHEMA_VIOLATION = "SCHEMA_VIOLATION"
|
|
9
|
+
SCHEMA_VIOLATION = "SCHEMA_VIOLATION",
|
|
10
|
+
/** Layer 1: agent attempted to exfiltrate data to an unauthorized destination */
|
|
11
|
+
SUSPICIOUS_EGRESS = "SUSPICIOUS_EGRESS",
|
|
12
|
+
/** Layer 1: agent response contained PII/secrets that were redacted before delivery */
|
|
13
|
+
PII_LEAK = "PII_LEAK"
|
|
10
14
|
}
|
|
11
15
|
export type SecurityEventTypeString = keyof typeof SecurityEventType;
|
|
12
16
|
export declare function isSecurityEventType(value: unknown): value is SecurityEventType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityEventType.d.ts","sourceRoot":"","sources":["../../../src/domain/events/SecurityEventType.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oBAAY,iBAAiB;IAC3B,gBAAgB,qBAAqB;IACrC,iBAAiB,sBAAsB;IACvC,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;
|
|
1
|
+
{"version":3,"file":"SecurityEventType.d.ts","sourceRoot":"","sources":["../../../src/domain/events/SecurityEventType.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oBAAY,iBAAiB;IAC3B,gBAAgB,qBAAqB;IACrC,iBAAiB,sBAAsB;IACvC,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;IACrC,iFAAiF;IACjF,iBAAiB,sBAAsB;IACvC,uFAAuF;IACvF,QAAQ,aAAa;CACtB;AAED,MAAM,MAAM,uBAAuB,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAErE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E"}
|
|
@@ -8,6 +8,10 @@ export var SecurityEventType;
|
|
|
8
8
|
SecurityEventType["UNAUTHORIZED_TOOL"] = "UNAUTHORIZED_TOOL";
|
|
9
9
|
SecurityEventType["PATTERN_MATCH"] = "PATTERN_MATCH";
|
|
10
10
|
SecurityEventType["SCHEMA_VIOLATION"] = "SCHEMA_VIOLATION";
|
|
11
|
+
/** Layer 1: agent attempted to exfiltrate data to an unauthorized destination */
|
|
12
|
+
SecurityEventType["SUSPICIOUS_EGRESS"] = "SUSPICIOUS_EGRESS";
|
|
13
|
+
/** Layer 1: agent response contained PII/secrets that were redacted before delivery */
|
|
14
|
+
SecurityEventType["PII_LEAK"] = "PII_LEAK";
|
|
11
15
|
})(SecurityEventType || (SecurityEventType = {}));
|
|
12
16
|
export function isSecurityEventType(value) {
|
|
13
17
|
return Object.values(SecurityEventType).includes(value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityEventType.js","sourceRoot":"","sources":["../../../src/domain/events/SecurityEventType.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"SecurityEventType.js","sourceRoot":"","sources":["../../../src/domain/events/SecurityEventType.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAN,IAAY,iBASX;AATD,WAAY,iBAAiB;IAC3B,0DAAqC,CAAA;IACrC,4DAAuC,CAAA;IACvC,oDAA+B,CAAA;IAC/B,0DAAqC,CAAA;IACrC,iFAAiF;IACjF,4DAAuC,CAAA;IACvC,uFAAuF;IACvF,0CAAqB,CAAA;AACvB,CAAC,EATW,iBAAiB,KAAjB,iBAAiB,QAS5B;AAID,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,OAAO,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,KAA0B,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic Filter
|
|
3
|
+
* Domain service: Layer 1 outbound intelligent gatekeeper.
|
|
4
|
+
*
|
|
5
|
+
* Philosophy — The SDK is a HIGH-PRECISION SENSOR, not a policy enforcer.
|
|
6
|
+
*
|
|
7
|
+
* Every match is surfaced as a SuspiciousTrace. The developer owns the reaction:
|
|
8
|
+
*
|
|
9
|
+
* const trace = tracerney.validate(agentOutput);
|
|
10
|
+
*
|
|
11
|
+
* if (trace.isSuspicious) {
|
|
12
|
+
* console.log(`Alert: ${trace.reason}`);
|
|
13
|
+
*
|
|
14
|
+
* // Option A: Hard Block
|
|
15
|
+
* // throw new Error("Security Policy Violation");
|
|
16
|
+
*
|
|
17
|
+
* // Option B: Surgical Scrub
|
|
18
|
+
* // return trace.redactedContent;
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* See the "Suspicious Manifest" in PIIPattern.ts for the label-to-action guide.
|
|
22
|
+
*
|
|
23
|
+
* Pure synchronous — no I/O, no async. Target latency <5ms.
|
|
24
|
+
*/
|
|
25
|
+
import { type PIICategory, type PIIPattern, type SuspiciousLabel } from "./PIIPattern";
|
|
26
|
+
import type { LLMResponse } from "../../application/ports/ILLMProvider";
|
|
27
|
+
export type { SuspiciousLabel };
|
|
28
|
+
export interface PIIFinding {
|
|
29
|
+
readonly patternId: string;
|
|
30
|
+
readonly patternName: string;
|
|
31
|
+
readonly category: PIICategory;
|
|
32
|
+
/** Semantic label — use this for routing decisions */
|
|
33
|
+
readonly label: SuspiciousLabel;
|
|
34
|
+
/** How many times this pattern matched */
|
|
35
|
+
readonly count: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The result of a single validate() call.
|
|
39
|
+
*
|
|
40
|
+
* isSuspicious — true if any pattern fired; false means clean.
|
|
41
|
+
* label — highest-severity label across all findings (null when clean).
|
|
42
|
+
* reason — human-readable summary for logging, e.g. "Detected 2 finding(s): Email Address, AWS Access Key ID"
|
|
43
|
+
* redactedContent — pre-computed version of the input with all matches replaced.
|
|
44
|
+
* Present regardless of isSuspicious (equals original text when clean).
|
|
45
|
+
* Use it if you want surgical scrubbing. Throw if you want a hard block.
|
|
46
|
+
* findings — granular list for detailed logging or telemetry.
|
|
47
|
+
*/
|
|
48
|
+
export interface SuspiciousTrace {
|
|
49
|
+
readonly isSuspicious: boolean;
|
|
50
|
+
readonly label: SuspiciousLabel | null;
|
|
51
|
+
readonly reason: string;
|
|
52
|
+
readonly redactedContent: string;
|
|
53
|
+
readonly findings: readonly PIIFinding[];
|
|
54
|
+
}
|
|
55
|
+
/** @internal Used by ShieldApplicationService.wrap() */
|
|
56
|
+
export interface FilterOutcome {
|
|
57
|
+
readonly trace: SuspiciousTrace;
|
|
58
|
+
/** Response with redactedContent applied to all choice messages */
|
|
59
|
+
readonly redactedResponse?: unknown;
|
|
60
|
+
}
|
|
61
|
+
export declare class DeterministicFilter {
|
|
62
|
+
private readonly patterns;
|
|
63
|
+
constructor(patterns?: readonly PIIPattern[]);
|
|
64
|
+
/**
|
|
65
|
+
* Scan a plain string and return a SuspiciousTrace.
|
|
66
|
+
* Always returns — never throws. The caller decides what to do.
|
|
67
|
+
*/
|
|
68
|
+
validate(text: string): SuspiciousTrace;
|
|
69
|
+
/**
|
|
70
|
+
* Scan all text content in an LLMResponse.
|
|
71
|
+
* Returns a merged SuspiciousTrace (highest label across all choices)
|
|
72
|
+
* and a new response with redactedContent applied — never mutates the original.
|
|
73
|
+
*/
|
|
74
|
+
filterResponse<T extends LLMResponse>(response: T): {
|
|
75
|
+
response: T;
|
|
76
|
+
outcome: FilterOutcome;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=DeterministicFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeterministicFilter.d.ts","sourceRoot":"","sources":["../../../src/domain/pii/DeterministicFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,sCAAsC,CAAC;AAInF,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAC;CAC1C;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,mEAAmE;IACnE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CACrC;AAID,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;gBAErC,QAAQ,GAAE,SAAS,UAAU,EAAiB;IAI1D;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe;IAyDvC;;;;OAIG;IACH,cAAc,CAAC,CAAC,SAAS,WAAW,EAAE,QAAQ,EAAE,CAAC,GAAG;QAClD,QAAQ,EAAE,CAAC,CAAC;QACZ,OAAO,EAAE,aAAa,CAAC;KACxB;CAoEF"}
|