Skip to content

Commit 3136ca0

Browse files
Merge pull request #2 from lockllm/pii-redaction
Pii redaction
2 parents 652d59e + bd3f1ab commit 3136ca0

File tree

11 files changed

+875
-5
lines changed

11 files changed

+875
-5
lines changed

CHANGELOG.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,82 @@
11
# Changelog
22

3+
## [1.2.0] - 2026-02-21
4+
5+
### Added
6+
7+
#### PII Detection and Redaction
8+
Protect sensitive personal information in prompts before they reach AI providers. When enabled, LockLLM detects emails, phone numbers, SSNs, credit card numbers, and other PII entities. Choose how to handle detected PII with the `piiAction` option:
9+
10+
- **`block`** - Reject requests containing PII entirely. Throws a `PIIDetectedError` with entity types and count.
11+
- **`strip`** - Automatically redact PII from prompts before forwarding to the AI provider. The redacted text is available via `redacted_input` in the scan response.
12+
- **`allow_with_warning`** - Allow requests through but include PII metadata in the response for logging.
13+
14+
PII detection is opt-in and disabled by default.
15+
16+
```typescript
17+
// Block requests containing PII
18+
const openai = createOpenAI({
19+
apiKey: process.env.LOCKLLM_API_KEY,
20+
proxyOptions: {
21+
piiAction: 'strip' // Automatically redact PII before sending to AI
22+
}
23+
});
24+
25+
// Handle PII errors when using block mode
26+
try {
27+
await openai.chat.completions.create({ ... });
28+
} catch (error) {
29+
if (error instanceof PIIDetectedError) {
30+
console.log(error.pii_details.entity_types); // ['email', 'phone_number']
31+
console.log(error.pii_details.entity_count); // 3
32+
}
33+
}
34+
```
35+
36+
#### Scan API PII Support
37+
The scan endpoint now accepts a `piiAction` option alongside existing scan options:
38+
39+
```typescript
40+
const result = await lockllm.scan(
41+
{ input: 'My email is test@example.com' },
42+
{ piiAction: 'block', scanAction: 'block' }
43+
);
44+
45+
if (result.pii_result) {
46+
console.log(result.pii_result.detected); // true
47+
console.log(result.pii_result.entity_types); // ['email']
48+
console.log(result.pii_result.entity_count); // 1
49+
console.log(result.pii_result.redacted_input); // 'My email is [EMAIL]' (strip mode only)
50+
}
51+
```
52+
53+
#### Enhanced Proxy Response Metadata
54+
Proxy responses now include additional fields for better observability:
55+
56+
- **PII detection metadata** - `pii_detected` object with detection status, entity types, count, and action taken
57+
- **Blocked status** - `blocked` flag when a request was rejected by security checks
58+
- **Sensitivity and label** - `sensitivity` level used and numeric `label` (0 = safe, 1 = unsafe)
59+
- **Decoded detail fields** - `scan_detail`, `policy_detail`, and `abuse_detail` automatically decoded from base64 response headers
60+
- **Extended routing metadata** - `estimated_original_cost`, `estimated_routed_cost`, `estimated_input_tokens`, `estimated_output_tokens`, and `routing_fee_reason`
61+
62+
#### Sensitivity Header Support
63+
You can now set the detection sensitivity level via `proxyOptions` or `buildLockLLMHeaders`:
64+
65+
```typescript
66+
const openai = createOpenAI({
67+
apiKey: process.env.LOCKLLM_API_KEY,
68+
proxyOptions: {
69+
sensitivity: 'high' // 'low', 'medium', or 'high'
70+
}
71+
});
72+
```
73+
74+
### Notes
75+
- PII detection is opt-in. Existing integrations continue to work without changes.
76+
- All new types (`PIIAction`, `PIIResult`, `PIIDetectedError`, `PIIDetectedErrorData`) are fully exported for TypeScript users.
77+
78+
---
79+
380
## [1.1.0] - 2026-02-18
481

582
### Added

README.md

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
**All-in-One AI Security for LLM Applications**
1212

13-
*Keep control of your AI. Detect prompt injection, jailbreaks, and adversarial attacks in real-time across 17+ providers with zero code changes.*
13+
*Keep control of your AI. Detect prompt injection, jailbreaks, PII leakage, and adversarial attacks in real-time across 17+ providers with zero code changes.*
1414

1515
[Quick Start](#quick-start) · [Documentation](https://www.lockllm.com/docs) · [Examples](#examples) · [Benchmarks](https://www.lockllm.com) · [API Reference](#api-reference)
1616

@@ -82,6 +82,7 @@ LockLLM provides production-ready AI security that integrates seamlessly into yo
8282
| **Custom Content Policies** | Define your own content rules in the dashboard and enforce them automatically across all providers |
8383
| **AI Abuse Detection** | Detect bot-generated content, repetition attacks, and resource exhaustion from your end-users |
8484
| **Intelligent Routing** | Automatically select the optimal model for each request based on task type and complexity to save costs |
85+
| **PII Detection & Redaction** | Detect and automatically redact emails, phone numbers, SSNs, credit cards, and other personal information before they reach AI providers |
8586
| **Response Caching** | Cache identical LLM responses to reduce costs and latency on repeated queries |
8687
| **Enterprise Privacy** | Provider keys encrypted at rest, prompts never stored |
8788
| **Production Ready** | Battle-tested with automatic retries, timeouts, and error handling |
@@ -406,6 +407,7 @@ import {
406407
PromptInjectionError,
407408
PolicyViolationError,
408409
AbuseDetectedError,
410+
PIIDetectedError,
409411
InsufficientCreditsError,
410412
AuthenticationError,
411413
RateLimitError,
@@ -433,6 +435,11 @@ try {
433435
console.log("Abuse detected:", error.abuse_details.abuse_types);
434436
console.log("Confidence:", error.abuse_details.confidence);
435437

438+
} else if (error instanceof PIIDetectedError) {
439+
// Personal information detected (when piiAction is 'block')
440+
console.log("PII found:", error.pii_details.entity_types);
441+
console.log("Entity count:", error.pii_details.entity_count);
442+
436443
} else if (error instanceof InsufficientCreditsError) {
437444
// Not enough credits
438445
console.log("Balance:", error.current_balance);
@@ -548,7 +555,7 @@ LockLLM Security Gateway
548555
3. **Error Response** - Detailed error returned with threat classification and confidence scores
549556
4. **Logging** - Incident automatically logged in [dashboard](https://www.lockllm.com/dashboard) for review and monitoring
550557

551-
### Security & Privacy
558+
### Privacy & Security
552559

553560
LockLLM is built with privacy and security as core principles. Your data stays yours.
554561

@@ -617,6 +624,7 @@ interface ScanOptions {
617624
scanAction?: 'block' | 'allow_with_warning'; // Core injection behavior
618625
policyAction?: 'block' | 'allow_with_warning'; // Custom policy behavior
619626
abuseAction?: 'block' | 'allow_with_warning'; // Abuse detection (opt-in)
627+
piiAction?: 'strip' | 'block' | 'allow_with_warning'; // PII detection (opt-in)
620628
}
621629
```
622630

@@ -651,6 +659,15 @@ interface ScanResponse {
651659
abuse_warnings?: AbuseWarning;
652660
// Present when intelligent routing is enabled
653661
routing?: { task_type: string; complexity: number; selected_model?: string; };
662+
// Present when PII detection is enabled
663+
pii_result?: PIIResult;
664+
}
665+
666+
interface PIIResult {
667+
detected: boolean; // Whether PII was detected
668+
entity_types: string[]; // Types of PII entities found (e.g., 'email', 'phone_number')
669+
entity_count: number; // Number of PII entities found
670+
redacted_input?: string; // Redacted text (only present when piiAction is 'strip')
654671
}
655672
```
656673

@@ -676,7 +693,9 @@ interface GenericClientConfig {
676693
scanAction?: 'block' | 'allow_with_warning';
677694
policyAction?: 'block' | 'allow_with_warning';
678695
abuseAction?: 'block' | 'allow_with_warning' | null;
696+
piiAction?: 'strip' | 'block' | 'allow_with_warning' | null;
679697
routeAction?: 'disabled' | 'auto' | 'custom';
698+
sensitivity?: 'low' | 'medium' | 'high';
680699
cacheResponse?: boolean;
681700
cacheTTL?: number;
682701
};
@@ -727,9 +746,10 @@ const headers = buildLockLLMHeaders({
727746
scanAction: 'block',
728747
policyAction: 'allow_with_warning',
729748
abuseAction: 'block',
749+
piiAction: 'strip',
730750
routeAction: 'auto'
731751
});
732-
// Returns: { 'x-lockllm-scan-mode': 'combined', ... }
752+
// Returns: { 'x-lockllm-scan-mode': 'combined', 'x-lockllm-pii-action': 'strip', ... }
733753
```
734754

735755
**Parse proxy response metadata:**
@@ -743,6 +763,7 @@ console.log(metadata.safe); // true/false
743763
console.log(metadata.scan_mode); // 'combined'
744764
console.log(metadata.cache_status); // 'HIT' or 'MISS'
745765
console.log(metadata.routing); // { task_type, complexity, selected_model, ... }
766+
console.log(metadata.pii_detected); // { detected, entity_types, entity_count, action }
746767
```
747768

748769
## Error Types
@@ -758,6 +779,7 @@ LockLLMError (base)
758779
├── PromptInjectionError (400)
759780
├── PolicyViolationError (403)
760781
├── AbuseDetectedError (400)
782+
├── PIIDetectedError (403)
761783
├── InsufficientCreditsError (402)
762784
├── UpstreamError (502)
763785
├── ConfigurationError (400)
@@ -803,6 +825,13 @@ class AbuseDetectedError extends LockLLMError {
803825
};
804826
}
805827

828+
class PIIDetectedError extends LockLLMError {
829+
pii_details: {
830+
entity_types: string[]; // PII types found (e.g., 'email', 'phone_number')
831+
entity_count: number; // Number of PII entities detected
832+
};
833+
}
834+
806835
class InsufficientCreditsError extends LockLLMError {
807836
current_balance: number; // Current credit balance
808837
estimated_cost: number; // Estimated cost of the request
@@ -908,7 +937,8 @@ const result = await lockllm.scan(
908937
{
909938
scanAction: 'block', // Block core injection attacks
910939
policyAction: 'allow_with_warning', // Allow but warn on policy violations
911-
abuseAction: 'block' // Enable abuse detection (opt-in)
940+
abuseAction: 'block', // Enable abuse detection (opt-in)
941+
piiAction: 'strip' // Redact PII from input (opt-in)
912942
}
913943
);
914944

@@ -920,6 +950,7 @@ const openai = createOpenAI({
920950
scanAction: 'block', // Block injection attacks
921951
policyAction: 'block', // Block policy violations
922952
abuseAction: 'allow_with_warning', // Detect abuse, don't block
953+
piiAction: 'strip', // Automatically redact PII
923954
routeAction: 'auto' // Enable intelligent routing
924955
}
925956
});
@@ -939,13 +970,15 @@ const openai = createOpenAI({
939970
- `scanAction` - Controls core injection detection: `'block'` | `'allow_with_warning'`
940971
- `policyAction` - Controls custom policy violations: `'block'` | `'allow_with_warning'`
941972
- `abuseAction` - Controls abuse detection (opt-in): `'block'` | `'allow_with_warning'` | `null`
973+
- `piiAction` - Controls PII detection (opt-in): `'strip'` | `'block'` | `'allow_with_warning'` | `null`
942974
- `routeAction` - Controls intelligent routing: `'disabled'` | `'auto'` | `'custom'`
943975

944976
**Default Behavior (no headers):**
945977
- Scan Mode: `combined` (check both core + policies)
946978
- Scan Action: `allow_with_warning` (detect but don't block)
947979
- Policy Action: `allow_with_warning` (detect but don't block)
948980
- Abuse Action: `null` (disabled, opt-in only)
981+
- PII Action: `null` (disabled, opt-in only)
949982
- Route Action: `disabled` (no routing)
950983

951984
See [examples/advanced-options.ts](examples/advanced-options.ts) for complete examples.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lockllm/sdk",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "Enterprise-grade AI security SDK providing real-time protection against prompt injection, jailbreaks, and adversarial attacks. Drop-in replacement for OpenAI, Anthropic, and 17+ providers with zero code changes. Includes REST API, proxy mode, browser extension, and webhook support. Free BYOK model with unlimited scanning.",
55
"main": "./dist/index.js",
66
"module": "./dist/index.mjs",

src/errors.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
PolicyViolationErrorData,
1010
AbuseDetectedErrorData,
1111
InsufficientCreditsErrorData,
12+
PIIDetectedErrorData,
1213
} from './types/errors';
1314

1415
/**
@@ -189,6 +190,28 @@ export class AbuseDetectedError extends LockLLMError {
189190
}
190191
}
191192

193+
/**
194+
* Error thrown when PII (personal information) is detected and action is block
195+
*/
196+
export class PIIDetectedError extends LockLLMError {
197+
public readonly pii_details: {
198+
entity_types: string[];
199+
entity_count: number;
200+
};
201+
202+
constructor(data: PIIDetectedErrorData) {
203+
super({
204+
message: data.message,
205+
type: 'lockllm_pii_error',
206+
code: 'pii_detected',
207+
status: 403,
208+
requestId: data.requestId,
209+
});
210+
this.name = 'PIIDetectedError';
211+
this.pii_details = data.pii_details;
212+
}
213+
}
214+
192215
/**
193216
* Error thrown when user has insufficient credits
194217
*/
@@ -267,6 +290,18 @@ export function parseError(response: any, requestId?: string): LockLLMError {
267290
});
268291
}
269292

293+
// PII detected error
294+
if (error.code === 'pii_detected' && error.pii_details) {
295+
return new PIIDetectedError({
296+
message: error.message,
297+
type: error.type,
298+
code: error.code,
299+
status: 403,
300+
requestId: error.request_id || requestId,
301+
pii_details: error.pii_details,
302+
});
303+
}
304+
270305
// Abuse detected error
271306
if (error.code === 'abuse_detected' && error.abuse_details) {
272307
return new AbuseDetectedError({

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export {
1818
PromptInjectionError,
1919
PolicyViolationError,
2020
AbuseDetectedError,
21+
PIIDetectedError,
2122
InsufficientCreditsError,
2223
UpstreamError,
2324
ConfigurationError,
@@ -32,6 +33,7 @@ export type {
3233
ScanMode,
3334
ScanAction,
3435
RouteAction,
36+
PIIAction,
3537
ProxyRequestOptions,
3638
ProxyResponseMetadata,
3739
} from './types/common';
@@ -44,6 +46,7 @@ export type {
4446
PolicyViolation,
4547
ScanWarning,
4648
AbuseWarning,
49+
PIIResult,
4750
} from './types/scan';
4851

4952
export type {
@@ -52,6 +55,7 @@ export type {
5255
PromptInjectionErrorData,
5356
PolicyViolationErrorData,
5457
AbuseDetectedErrorData,
58+
PIIDetectedErrorData,
5559
InsufficientCreditsErrorData,
5660
} from './types/errors';
5761

src/scan.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ export class ScanClient {
8989
headers['x-lockllm-abuse-action'] = options.abuseAction;
9090
}
9191

92+
// PII action: opt-in PII detection (null/undefined means disabled)
93+
if (options?.piiAction !== undefined && options?.piiAction !== null) {
94+
headers['x-lockllm-pii-action'] = options.piiAction;
95+
}
96+
9297
// Build request body
9398
const body: Record<string, any> = {
9499
input: request.input,

0 commit comments

Comments
 (0)