mcp-rubber-duck 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +158 -1
- package/audit-ci.json +2 -1
- package/dist/config/config.d.ts +2 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +144 -1
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1084 -2
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +59 -0
- package/dist/config/types.js.map +1 -1
- package/dist/guardrails/context.d.ts +10 -0
- package/dist/guardrails/context.d.ts.map +1 -0
- package/dist/guardrails/context.js +35 -0
- package/dist/guardrails/context.js.map +1 -0
- package/dist/guardrails/errors.d.ts +26 -0
- package/dist/guardrails/errors.d.ts.map +1 -0
- package/dist/guardrails/errors.js +42 -0
- package/dist/guardrails/errors.js.map +1 -0
- package/dist/guardrails/index.d.ts +6 -0
- package/dist/guardrails/index.d.ts.map +1 -0
- package/dist/guardrails/index.js +11 -0
- package/dist/guardrails/index.js.map +1 -0
- package/dist/guardrails/plugins/base-plugin.d.ts +35 -0
- package/dist/guardrails/plugins/base-plugin.d.ts.map +1 -0
- package/dist/guardrails/plugins/base-plugin.js +70 -0
- package/dist/guardrails/plugins/base-plugin.js.map +1 -0
- package/dist/guardrails/plugins/index.d.ts +6 -0
- package/dist/guardrails/plugins/index.d.ts.map +1 -0
- package/dist/guardrails/plugins/index.js +6 -0
- package/dist/guardrails/plugins/index.js.map +1 -0
- package/dist/guardrails/plugins/pattern-blocker.d.ts +27 -0
- package/dist/guardrails/plugins/pattern-blocker.d.ts.map +1 -0
- package/dist/guardrails/plugins/pattern-blocker.js +140 -0
- package/dist/guardrails/plugins/pattern-blocker.js.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/detectors.d.ts +40 -0
- package/dist/guardrails/plugins/pii-redactor/detectors.d.ts.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/detectors.js +134 -0
- package/dist/guardrails/plugins/pii-redactor/detectors.js.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/index.d.ts +28 -0
- package/dist/guardrails/plugins/pii-redactor/index.d.ts.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/index.js +157 -0
- package/dist/guardrails/plugins/pii-redactor/index.js.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/pseudonymizer.d.ts +33 -0
- package/dist/guardrails/plugins/pii-redactor/pseudonymizer.d.ts.map +1 -0
- package/dist/guardrails/plugins/pii-redactor/pseudonymizer.js +70 -0
- package/dist/guardrails/plugins/pii-redactor/pseudonymizer.js.map +1 -0
- package/dist/guardrails/plugins/rate-limiter.d.ts +28 -0
- package/dist/guardrails/plugins/rate-limiter.d.ts.map +1 -0
- package/dist/guardrails/plugins/rate-limiter.js +91 -0
- package/dist/guardrails/plugins/rate-limiter.js.map +1 -0
- package/dist/guardrails/plugins/token-limiter.d.ts +30 -0
- package/dist/guardrails/plugins/token-limiter.d.ts.map +1 -0
- package/dist/guardrails/plugins/token-limiter.js +98 -0
- package/dist/guardrails/plugins/token-limiter.js.map +1 -0
- package/dist/guardrails/service.d.ts +38 -0
- package/dist/guardrails/service.d.ts.map +1 -0
- package/dist/guardrails/service.js +183 -0
- package/dist/guardrails/service.js.map +1 -0
- package/dist/guardrails/types.d.ts +96 -0
- package/dist/guardrails/types.d.ts.map +1 -0
- package/dist/guardrails/types.js +2 -0
- package/dist/guardrails/types.js.map +1 -0
- package/dist/providers/duck-provider-enhanced.d.ts +2 -1
- package/dist/providers/duck-provider-enhanced.d.ts.map +1 -1
- package/dist/providers/duck-provider-enhanced.js +55 -6
- package/dist/providers/duck-provider-enhanced.js.map +1 -1
- package/dist/providers/enhanced-manager.d.ts +2 -1
- package/dist/providers/enhanced-manager.d.ts.map +1 -1
- package/dist/providers/enhanced-manager.js +3 -3
- package/dist/providers/enhanced-manager.js.map +1 -1
- package/dist/providers/manager.d.ts +3 -1
- package/dist/providers/manager.d.ts.map +1 -1
- package/dist/providers/manager.js +4 -2
- package/dist/providers/manager.js.map +1 -1
- package/dist/providers/provider.d.ts +3 -1
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js +43 -3
- package/dist/providers/provider.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +28 -6
- package/dist/server.js.map +1 -1
- package/dist/services/function-bridge.d.ts +3 -1
- package/dist/services/function-bridge.d.ts.map +1 -1
- package/dist/services/function-bridge.js +40 -1
- package/dist/services/function-bridge.js.map +1 -1
- package/package.json +1 -1
- package/src/config/config.ts +187 -1
- package/src/config/types.ts +73 -0
- package/src/guardrails/context.ts +37 -0
- package/src/guardrails/errors.ts +46 -0
- package/src/guardrails/index.ts +20 -0
- package/src/guardrails/plugins/base-plugin.ts +103 -0
- package/src/guardrails/plugins/index.ts +5 -0
- package/src/guardrails/plugins/pattern-blocker.ts +190 -0
- package/src/guardrails/plugins/pii-redactor/detectors.ts +200 -0
- package/src/guardrails/plugins/pii-redactor/index.ts +203 -0
- package/src/guardrails/plugins/pii-redactor/pseudonymizer.ts +91 -0
- package/src/guardrails/plugins/rate-limiter.ts +142 -0
- package/src/guardrails/plugins/token-limiter.ts +155 -0
- package/src/guardrails/service.ts +209 -0
- package/src/guardrails/types.ts +120 -0
- package/src/providers/duck-provider-enhanced.ts +76 -7
- package/src/providers/enhanced-manager.ts +5 -3
- package/src/providers/manager.ts +6 -3
- package/src/providers/provider.ts +57 -6
- package/src/server.ts +32 -6
- package/src/services/function-bridge.ts +53 -2
- package/tests/guardrails/config.test.ts +267 -0
- package/tests/guardrails/errors.test.ts +109 -0
- package/tests/guardrails/plugins/pattern-blocker.test.ts +309 -0
- package/tests/guardrails/plugins/pii-redactor.test.ts +1004 -0
- package/tests/guardrails/plugins/rate-limiter.test.ts +310 -0
- package/tests/guardrails/plugins/token-limiter.test.ts +216 -0
- package/tests/guardrails/service.test.ts +911 -0
- package/tests/mcp-bridge.test.ts +248 -0
- package/tests/providers.test.ts +739 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type PIIType = 'email' | 'phone' | 'ssn' | 'api_key' | 'credit_card' | 'ip_address' | 'custom';
|
|
2
|
+
export interface PIIDetection {
|
|
3
|
+
type: PIIType;
|
|
4
|
+
value: string;
|
|
5
|
+
startIndex: number;
|
|
6
|
+
endIndex: number;
|
|
7
|
+
confidence: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PIIDetectorConfig {
|
|
10
|
+
detectEmails: boolean;
|
|
11
|
+
detectPhones: boolean;
|
|
12
|
+
detectSSN: boolean;
|
|
13
|
+
detectAPIKeys: boolean;
|
|
14
|
+
detectCreditCards: boolean;
|
|
15
|
+
detectIPAddresses: boolean;
|
|
16
|
+
customPatterns: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
pattern: string;
|
|
19
|
+
placeholder: string;
|
|
20
|
+
}>;
|
|
21
|
+
allowlist: string[];
|
|
22
|
+
allowlistDomains: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* PII detector using regex patterns
|
|
26
|
+
*/
|
|
27
|
+
export declare class PIIDetector {
|
|
28
|
+
private patterns;
|
|
29
|
+
private allowlist;
|
|
30
|
+
private allowlistDomains;
|
|
31
|
+
private customPatterns;
|
|
32
|
+
constructor(config: PIIDetectorConfig);
|
|
33
|
+
/**
|
|
34
|
+
* Detect PII in text
|
|
35
|
+
*/
|
|
36
|
+
detect(text: string): PIIDetection[];
|
|
37
|
+
private isAllowlisted;
|
|
38
|
+
private calculateConfidence;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=detectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.d.ts","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/detectors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GACf,OAAO,GACP,OAAO,GACP,KAAK,GACL,SAAS,GACT,aAAa,GACb,YAAY,GACZ,QAAQ,CAAC;AAEb,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,cAAc,CAAmE;gBAE7E,MAAM,EAAE,iBAAiB;IAmErC;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE;IAkDpC,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,mBAAmB;CAuB5B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII detector using regex patterns
|
|
3
|
+
*/
|
|
4
|
+
export class PIIDetector {
|
|
5
|
+
patterns = new Map();
|
|
6
|
+
allowlist;
|
|
7
|
+
allowlistDomains;
|
|
8
|
+
customPatterns = [];
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.allowlist = new Set(config.allowlist.map((a) => a.toLowerCase()));
|
|
11
|
+
this.allowlistDomains = new Set(config.allowlistDomains.map((d) => d.toLowerCase()));
|
|
12
|
+
// Initialize built-in patterns
|
|
13
|
+
if (config.detectEmails) {
|
|
14
|
+
this.patterns.set('email', /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g);
|
|
15
|
+
}
|
|
16
|
+
if (config.detectPhones) {
|
|
17
|
+
// Handles multiple phone formats: US, international, with/without country code
|
|
18
|
+
this.patterns.set('phone', /\b(?:\+?1[-.\s]?)?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}\b/g);
|
|
19
|
+
}
|
|
20
|
+
if (config.detectSSN) {
|
|
21
|
+
// US SSN format: XXX-XX-XXXX or XXXXXXXXX
|
|
22
|
+
this.patterns.set('ssn', /\b[0-9]{3}[-\s]?[0-9]{2}[-\s]?[0-9]{4}\b/g);
|
|
23
|
+
}
|
|
24
|
+
if (config.detectAPIKeys) {
|
|
25
|
+
// Common API key patterns
|
|
26
|
+
this.patterns.set('api_key', /\b(sk-[a-zA-Z0-9]{20,}|gsk_[a-zA-Z0-9]{20,}|api[_-]?key[_-]?[a-zA-Z0-9]{16,})\b/gi);
|
|
27
|
+
}
|
|
28
|
+
if (config.detectCreditCards) {
|
|
29
|
+
// Credit card patterns (Visa, Mastercard, Amex, Discover)
|
|
30
|
+
// Simplified - doesn't validate Luhn, just matches format
|
|
31
|
+
this.patterns.set('credit_card', /\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|6(?:011|5[0-9]{2})[0-9]{12})\b/g);
|
|
32
|
+
}
|
|
33
|
+
if (config.detectIPAddresses) {
|
|
34
|
+
// IPv4 addresses
|
|
35
|
+
this.patterns.set('ip_address', /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g);
|
|
36
|
+
}
|
|
37
|
+
// Custom patterns
|
|
38
|
+
for (const custom of config.customPatterns) {
|
|
39
|
+
try {
|
|
40
|
+
this.customPatterns.push({
|
|
41
|
+
name: custom.name,
|
|
42
|
+
regex: new RegExp(custom.pattern, 'g'),
|
|
43
|
+
placeholder: custom.placeholder,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Invalid regex, skip it
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Detect PII in text
|
|
53
|
+
*/
|
|
54
|
+
detect(text) {
|
|
55
|
+
const detections = [];
|
|
56
|
+
// Check built-in patterns
|
|
57
|
+
for (const [type, pattern] of this.patterns) {
|
|
58
|
+
pattern.lastIndex = 0; // Reset regex state
|
|
59
|
+
let match;
|
|
60
|
+
while ((match = pattern.exec(text)) !== null) {
|
|
61
|
+
const value = match[0];
|
|
62
|
+
// Check allowlist
|
|
63
|
+
if (this.isAllowlisted(value, type)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
detections.push({
|
|
67
|
+
type,
|
|
68
|
+
value,
|
|
69
|
+
startIndex: match.index,
|
|
70
|
+
endIndex: match.index + value.length,
|
|
71
|
+
confidence: this.calculateConfidence(type, value),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Check custom patterns
|
|
76
|
+
for (const custom of this.customPatterns) {
|
|
77
|
+
custom.regex.lastIndex = 0;
|
|
78
|
+
let match;
|
|
79
|
+
while ((match = custom.regex.exec(text)) !== null) {
|
|
80
|
+
const value = match[0];
|
|
81
|
+
if (this.allowlist.has(value.toLowerCase())) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
detections.push({
|
|
85
|
+
type: 'custom',
|
|
86
|
+
value,
|
|
87
|
+
startIndex: match.index,
|
|
88
|
+
endIndex: match.index + value.length,
|
|
89
|
+
confidence: 0.9,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Sort by position (for consistent pseudonymization)
|
|
94
|
+
return detections.sort((a, b) => a.startIndex - b.startIndex);
|
|
95
|
+
}
|
|
96
|
+
isAllowlisted(value, type) {
|
|
97
|
+
const lowerValue = value.toLowerCase();
|
|
98
|
+
if (this.allowlist.has(lowerValue)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// For emails, check domain allowlist
|
|
102
|
+
if (type === 'email') {
|
|
103
|
+
const domain = lowerValue.split('@')[1];
|
|
104
|
+
if (domain && this.allowlistDomains.has(domain)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
calculateConfidence(type, value) {
|
|
111
|
+
// Simple confidence scoring based on format strictness
|
|
112
|
+
switch (type) {
|
|
113
|
+
case 'ssn':
|
|
114
|
+
return 0.95; // High confidence for strict format
|
|
115
|
+
case 'credit_card':
|
|
116
|
+
return 0.95; // High confidence for strict format
|
|
117
|
+
case 'email':
|
|
118
|
+
return 0.9;
|
|
119
|
+
case 'phone':
|
|
120
|
+
return 0.85;
|
|
121
|
+
case 'api_key':
|
|
122
|
+
// Higher confidence for longer keys or known prefixes
|
|
123
|
+
if (value.startsWith('sk-') || value.startsWith('gsk_')) {
|
|
124
|
+
return 0.95;
|
|
125
|
+
}
|
|
126
|
+
return 0.7; // Lower confidence due to possible false positives
|
|
127
|
+
case 'ip_address':
|
|
128
|
+
return 0.8;
|
|
129
|
+
default:
|
|
130
|
+
return 0.8;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.js","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/detectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseGuardrailPlugin } from '../base-plugin.js';
|
|
2
|
+
import { GuardrailPhase, GuardrailContext, GuardrailResult } from '../../types.js';
|
|
3
|
+
import { PIIDetector } from './detectors.js';
|
|
4
|
+
import { Pseudonymizer } from './pseudonymizer.js';
|
|
5
|
+
/**
|
|
6
|
+
* PII Redactor plugin - detects and redacts sensitive data
|
|
7
|
+
*/
|
|
8
|
+
export declare class PIIRedactorPlugin extends BaseGuardrailPlugin {
|
|
9
|
+
name: string;
|
|
10
|
+
phases: GuardrailPhase[];
|
|
11
|
+
private detector;
|
|
12
|
+
private pseudonymizer;
|
|
13
|
+
private restoreOnResponse;
|
|
14
|
+
private logDetections;
|
|
15
|
+
initialize(config: Record<string, unknown>): Promise<void>;
|
|
16
|
+
execute(phase: GuardrailPhase, context: GuardrailContext): Promise<GuardrailResult>;
|
|
17
|
+
private redactPII;
|
|
18
|
+
private restorePII;
|
|
19
|
+
/**
|
|
20
|
+
* Get detector for testing
|
|
21
|
+
*/
|
|
22
|
+
getDetector(): PIIDetector;
|
|
23
|
+
/**
|
|
24
|
+
* Get pseudonymizer for testing
|
|
25
|
+
*/
|
|
26
|
+
getPseudonymizer(): Pseudonymizer;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAqB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;IACxD,IAAI,SAAkB;IACtB,MAAM,EAAE,cAAc,EAAE,CAA0E;IAElG,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,aAAa,CAAiB;IAEhC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB1D,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkBzF,OAAO,CAAC,SAAS;IA4EjB,OAAO,CAAC,UAAU;IAoDlB;;OAEG;IACH,WAAW,IAAI,WAAW;IAI1B;;OAEG;IACH,gBAAgB,IAAI,aAAa;CAGlC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { BaseGuardrailPlugin } from '../base-plugin.js';
|
|
2
|
+
import { PIIDetector } from './detectors.js';
|
|
3
|
+
import { Pseudonymizer } from './pseudonymizer.js';
|
|
4
|
+
import { logger } from '../../../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* PII Redactor plugin - detects and redacts sensitive data
|
|
7
|
+
*/
|
|
8
|
+
export class PIIRedactorPlugin extends BaseGuardrailPlugin {
|
|
9
|
+
name = 'pii_redactor';
|
|
10
|
+
phases = ['pre_request', 'post_response', 'pre_tool_input', 'post_tool_output'];
|
|
11
|
+
detector;
|
|
12
|
+
pseudonymizer;
|
|
13
|
+
restoreOnResponse = false;
|
|
14
|
+
logDetections = true;
|
|
15
|
+
async initialize(config) {
|
|
16
|
+
await super.initialize(config);
|
|
17
|
+
const typedConfig = config;
|
|
18
|
+
const detectorConfig = {
|
|
19
|
+
detectEmails: typedConfig.detect_emails ?? true,
|
|
20
|
+
detectPhones: typedConfig.detect_phones ?? true,
|
|
21
|
+
detectSSN: typedConfig.detect_ssn ?? true,
|
|
22
|
+
detectAPIKeys: typedConfig.detect_api_keys ?? true,
|
|
23
|
+
detectCreditCards: typedConfig.detect_credit_cards ?? true,
|
|
24
|
+
detectIPAddresses: typedConfig.detect_ip_addresses ?? false,
|
|
25
|
+
customPatterns: typedConfig.custom_patterns ?? [],
|
|
26
|
+
allowlist: typedConfig.allowlist ?? [],
|
|
27
|
+
allowlistDomains: typedConfig.allowlist_domains ?? [],
|
|
28
|
+
};
|
|
29
|
+
this.detector = new PIIDetector(detectorConfig);
|
|
30
|
+
this.pseudonymizer = new Pseudonymizer();
|
|
31
|
+
this.restoreOnResponse = typedConfig.restore_on_response ?? false;
|
|
32
|
+
this.logDetections = typedConfig.log_detections ?? true;
|
|
33
|
+
this.priority = typedConfig.priority ?? 25;
|
|
34
|
+
}
|
|
35
|
+
async execute(phase, context) {
|
|
36
|
+
switch (phase) {
|
|
37
|
+
case 'pre_request':
|
|
38
|
+
case 'pre_tool_input':
|
|
39
|
+
return this.redactPII(context, phase);
|
|
40
|
+
case 'post_response':
|
|
41
|
+
case 'post_tool_output':
|
|
42
|
+
if (this.restoreOnResponse) {
|
|
43
|
+
return this.restorePII(context, phase);
|
|
44
|
+
}
|
|
45
|
+
return this.allow(context);
|
|
46
|
+
default:
|
|
47
|
+
return this.allow(context);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
redactPII(context, phase) {
|
|
51
|
+
let textToScan;
|
|
52
|
+
let field;
|
|
53
|
+
if (phase === 'pre_request') {
|
|
54
|
+
textToScan = context.prompt || '';
|
|
55
|
+
field = 'prompt';
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
textToScan = JSON.stringify(context.toolArgs || {});
|
|
59
|
+
field = 'toolArgs';
|
|
60
|
+
}
|
|
61
|
+
if (!textToScan) {
|
|
62
|
+
return Promise.resolve(this.allow(context));
|
|
63
|
+
}
|
|
64
|
+
const detections = this.detector.detect(textToScan);
|
|
65
|
+
if (detections.length === 0) {
|
|
66
|
+
return Promise.resolve(this.allow(context));
|
|
67
|
+
}
|
|
68
|
+
// Log detections
|
|
69
|
+
if (this.logDetections) {
|
|
70
|
+
logger.info(`PII detected in ${field}:`, {
|
|
71
|
+
requestId: context.requestId,
|
|
72
|
+
types: [...new Set(detections.map((d) => d.type))],
|
|
73
|
+
count: detections.length,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Pseudonymize
|
|
77
|
+
const { text: redactedText, mappings } = this.pseudonymizer.pseudonymize(textToScan, detections);
|
|
78
|
+
// Store mappings for potential restoration
|
|
79
|
+
context.metadata.set('pii_mappings', mappings);
|
|
80
|
+
// Record modification
|
|
81
|
+
this.addModification(context, phase, field, `Redacted ${detections.length} PII items: ${[...new Set(detections.map((d) => d.type))].join(', ')}`, undefined, // Don't store original (contains PII)
|
|
82
|
+
undefined // Don't store new (would expose placeholder patterns)
|
|
83
|
+
);
|
|
84
|
+
// Apply modification
|
|
85
|
+
if (phase === 'pre_request') {
|
|
86
|
+
context.prompt = redactedText;
|
|
87
|
+
// Also update the last message if present
|
|
88
|
+
if (context.messages.length > 0) {
|
|
89
|
+
const lastIndex = context.messages.length - 1;
|
|
90
|
+
context.messages[lastIndex] = {
|
|
91
|
+
...context.messages[lastIndex],
|
|
92
|
+
content: redactedText,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
try {
|
|
98
|
+
context.toolArgs = JSON.parse(redactedText);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// If parse fails, store as string
|
|
102
|
+
context.toolArgs = { _redacted: redactedText };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return Promise.resolve(this.modify(context));
|
|
106
|
+
}
|
|
107
|
+
restorePII(context, phase) {
|
|
108
|
+
const mappings = context.metadata.get('pii_mappings');
|
|
109
|
+
if (!mappings || mappings.size === 0) {
|
|
110
|
+
return Promise.resolve(this.allow(context));
|
|
111
|
+
}
|
|
112
|
+
let textToRestore;
|
|
113
|
+
if (phase === 'post_response') {
|
|
114
|
+
textToRestore = context.response || '';
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
textToRestore =
|
|
118
|
+
typeof context.toolResult === 'string'
|
|
119
|
+
? context.toolResult
|
|
120
|
+
: JSON.stringify(context.toolResult);
|
|
121
|
+
}
|
|
122
|
+
if (!textToRestore) {
|
|
123
|
+
return Promise.resolve(this.allow(context));
|
|
124
|
+
}
|
|
125
|
+
const restoredText = this.pseudonymizer.restore(textToRestore, mappings);
|
|
126
|
+
// Only modify if something changed
|
|
127
|
+
if (restoredText === textToRestore) {
|
|
128
|
+
return Promise.resolve(this.allow(context));
|
|
129
|
+
}
|
|
130
|
+
this.addModification(context, phase, phase === 'post_response' ? 'response' : 'toolResult', `Restored ${mappings.size} PII placeholders`);
|
|
131
|
+
if (phase === 'post_response') {
|
|
132
|
+
context.response = restoredText;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
try {
|
|
136
|
+
context.toolResult = JSON.parse(restoredText);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
context.toolResult = restoredText;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return Promise.resolve(this.modify(context));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get detector for testing
|
|
146
|
+
*/
|
|
147
|
+
getDetector() {
|
|
148
|
+
return this.detector;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get pseudonymizer for testing
|
|
152
|
+
*/
|
|
153
|
+
getPseudonymizer() {
|
|
154
|
+
return this.pseudonymizer;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PIIDetection } from './detectors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pseudonymizer - replaces PII with numbered placeholders
|
|
4
|
+
* and supports optional restoration
|
|
5
|
+
*/
|
|
6
|
+
export declare class Pseudonymizer {
|
|
7
|
+
private counters;
|
|
8
|
+
/**
|
|
9
|
+
* Pseudonymize text by replacing PII with placeholders
|
|
10
|
+
* Returns the modified text and a mapping for restoration
|
|
11
|
+
*/
|
|
12
|
+
pseudonymize(text: string, detections: PIIDetection[]): {
|
|
13
|
+
text: string;
|
|
14
|
+
mappings: Map<string, string>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Restore placeholders in text with original values
|
|
18
|
+
*/
|
|
19
|
+
restore(text: string, mappings: Map<string, string>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Generate a placeholder for a PII type
|
|
22
|
+
*/
|
|
23
|
+
private generatePlaceholder;
|
|
24
|
+
/**
|
|
25
|
+
* Escape special regex characters in a string
|
|
26
|
+
*/
|
|
27
|
+
private escapeRegex;
|
|
28
|
+
/**
|
|
29
|
+
* Reset counters (for testing)
|
|
30
|
+
*/
|
|
31
|
+
reset(): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=pseudonymizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudonymizer.d.ts","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/pseudonymizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAW,MAAM,gBAAgB,CAAC;AAEvD;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAmC;IAEnD;;;OAGG;IACH,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,YAAY,EAAE,GACzB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IAwBlD;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM;IAc5D;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAiB3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pseudonymizer - replaces PII with numbered placeholders
|
|
3
|
+
* and supports optional restoration
|
|
4
|
+
*/
|
|
5
|
+
export class Pseudonymizer {
|
|
6
|
+
counters = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* Pseudonymize text by replacing PII with placeholders
|
|
9
|
+
* Returns the modified text and a mapping for restoration
|
|
10
|
+
*/
|
|
11
|
+
pseudonymize(text, detections) {
|
|
12
|
+
const mappings = new Map();
|
|
13
|
+
let result = text;
|
|
14
|
+
let offset = 0;
|
|
15
|
+
// Reset counters for consistent numbering
|
|
16
|
+
this.counters.clear();
|
|
17
|
+
for (const detection of detections) {
|
|
18
|
+
const placeholder = this.generatePlaceholder(detection.type);
|
|
19
|
+
mappings.set(placeholder, detection.value);
|
|
20
|
+
// Replace in text (accounting for previous replacements)
|
|
21
|
+
const start = detection.startIndex + offset;
|
|
22
|
+
const end = detection.endIndex + offset;
|
|
23
|
+
result = result.substring(0, start) + placeholder + result.substring(end);
|
|
24
|
+
// Adjust offset for next replacement
|
|
25
|
+
offset += placeholder.length - detection.value.length;
|
|
26
|
+
}
|
|
27
|
+
return { text: result, mappings };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Restore placeholders in text with original values
|
|
31
|
+
*/
|
|
32
|
+
restore(text, mappings) {
|
|
33
|
+
let result = text;
|
|
34
|
+
for (const [placeholder, original] of mappings) {
|
|
35
|
+
// Use global replace to handle multiple occurrences
|
|
36
|
+
result = result.replace(new RegExp(this.escapeRegex(placeholder), 'g'), original);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate a placeholder for a PII type
|
|
42
|
+
*/
|
|
43
|
+
generatePlaceholder(type) {
|
|
44
|
+
const count = (this.counters.get(type) || 0) + 1;
|
|
45
|
+
this.counters.set(type, count);
|
|
46
|
+
const typeLabels = {
|
|
47
|
+
email: 'EMAIL',
|
|
48
|
+
phone: 'PHONE',
|
|
49
|
+
ssn: 'SSN',
|
|
50
|
+
api_key: 'API_KEY',
|
|
51
|
+
credit_card: 'CARD',
|
|
52
|
+
ip_address: 'IP',
|
|
53
|
+
custom: 'REDACTED',
|
|
54
|
+
};
|
|
55
|
+
return `[${typeLabels[type]}_${count}]`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Escape special regex characters in a string
|
|
59
|
+
*/
|
|
60
|
+
escapeRegex(str) {
|
|
61
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Reset counters (for testing)
|
|
65
|
+
*/
|
|
66
|
+
reset() {
|
|
67
|
+
this.counters.clear();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=pseudonymizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudonymizer.js","sourceRoot":"","sources":["../../../../src/guardrails/plugins/pii-redactor/pseudonymizer.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,aAAa;IAChB,QAAQ,GAAyB,IAAI,GAAG,EAAE,CAAC;IAEnD;;;OAGG;IACH,YAAY,CACV,IAAY,EACZ,UAA0B;QAE1B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,0CAA0C;QAC1C,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QAEtB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC7D,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;YAE3C,yDAAyD;YACzD,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,GAAG,MAAM,CAAC;YAC5C,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC;YACxC,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAE1E,qCAAqC;YACrC,MAAM,IAAI,WAAW,CAAC,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,IAAY,EAAE,QAA6B;QACjD,IAAI,MAAM,GAAG,IAAI,CAAC;QAElB,KAAK,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,IAAI,QAAQ,EAAE,CAAC;YAC/C,oDAAoD;YACpD,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,GAAG,CAAC,EAC9C,QAAQ,CACT,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,IAAa;QACvC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE/B,MAAM,UAAU,GAA4B;YAC1C,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,OAAO;YACd,GAAG,EAAE,KAAK;YACV,OAAO,EAAE,SAAS;YAClB,WAAW,EAAE,MAAM;YACnB,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,UAAU;SACnB,CAAC;QAEF,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC;IAC1C,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,GAAW;QAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BaseGuardrailPlugin } from './base-plugin.js';
|
|
2
|
+
import { GuardrailPhase, GuardrailContext, GuardrailResult } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rate limiter plugin - limits requests per minute/hour
|
|
5
|
+
*/
|
|
6
|
+
export declare class RateLimiterPlugin extends BaseGuardrailPlugin {
|
|
7
|
+
name: string;
|
|
8
|
+
phases: GuardrailPhase[];
|
|
9
|
+
private requestsPerMinute;
|
|
10
|
+
private requestsPerHour;
|
|
11
|
+
private perProvider;
|
|
12
|
+
private burstAllowance;
|
|
13
|
+
private requestHistory;
|
|
14
|
+
initialize(config: Record<string, unknown>): Promise<void>;
|
|
15
|
+
execute(phase: GuardrailPhase, context: GuardrailContext): Promise<GuardrailResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Get current request counts (for testing/monitoring)
|
|
18
|
+
*/
|
|
19
|
+
getRequestCounts(key?: string): {
|
|
20
|
+
lastMinute: number;
|
|
21
|
+
lastHour: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Reset request history (for testing)
|
|
25
|
+
*/
|
|
26
|
+
reset(): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/guardrails/plugins/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAOhF;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,mBAAmB;IACxD,IAAI,SAAkB;IACtB,MAAM,EAAE,cAAc,EAAE,CAAmB;IAE3C,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAa;IAGnC,OAAO,CAAC,cAAc,CAA2C;IAE3D,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhE,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAsFnF;;OAEG;IACH,gBAAgB,CAAC,GAAG,GAAE,MAAiB,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAYlF;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BaseGuardrailPlugin } from './base-plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Rate limiter plugin - limits requests per minute/hour
|
|
4
|
+
*/
|
|
5
|
+
export class RateLimiterPlugin extends BaseGuardrailPlugin {
|
|
6
|
+
name = 'rate_limiter';
|
|
7
|
+
phases = ['pre_request'];
|
|
8
|
+
requestsPerMinute = 60;
|
|
9
|
+
requestsPerHour = 1000;
|
|
10
|
+
perProvider = false;
|
|
11
|
+
burstAllowance = 5;
|
|
12
|
+
// Request history: key is provider (or 'global'), value is array of timestamps
|
|
13
|
+
requestHistory = new Map();
|
|
14
|
+
async initialize(config) {
|
|
15
|
+
await super.initialize(config);
|
|
16
|
+
const typedConfig = config;
|
|
17
|
+
this.requestsPerMinute = typedConfig.requests_per_minute ?? 60;
|
|
18
|
+
this.requestsPerHour = typedConfig.requests_per_hour ?? 1000;
|
|
19
|
+
this.perProvider = typedConfig.per_provider ?? false;
|
|
20
|
+
this.burstAllowance = typedConfig.burst_allowance ?? 5;
|
|
21
|
+
this.priority = typedConfig.priority ?? 10;
|
|
22
|
+
}
|
|
23
|
+
execute(phase, context) {
|
|
24
|
+
if (phase !== 'pre_request') {
|
|
25
|
+
return Promise.resolve(this.allow(context));
|
|
26
|
+
}
|
|
27
|
+
const key = this.perProvider ? context.provider : 'global';
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
// Get or create request history for this key
|
|
30
|
+
let history = this.requestHistory.get(key);
|
|
31
|
+
if (!history) {
|
|
32
|
+
history = [];
|
|
33
|
+
this.requestHistory.set(key, history);
|
|
34
|
+
}
|
|
35
|
+
// Clean up old entries (older than 1 hour)
|
|
36
|
+
const oneHourAgo = now - 60 * 60 * 1000;
|
|
37
|
+
history = history.filter((r) => r.timestamp > oneHourAgo);
|
|
38
|
+
// Remove empty keys to prevent unbounded Map growth with perProvider mode
|
|
39
|
+
if (history.length === 0) {
|
|
40
|
+
this.requestHistory.delete(key);
|
|
41
|
+
history = [];
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.requestHistory.set(key, history);
|
|
45
|
+
}
|
|
46
|
+
// Count requests in last minute and last hour
|
|
47
|
+
const oneMinuteAgo = now - 60 * 1000;
|
|
48
|
+
const requestsLastMinute = history.filter((r) => r.timestamp > oneMinuteAgo).length;
|
|
49
|
+
const requestsLastHour = history.length;
|
|
50
|
+
// Check rate limits (with burst allowance)
|
|
51
|
+
const effectiveMinuteLimit = this.requestsPerMinute + this.burstAllowance;
|
|
52
|
+
const effectiveHourLimit = this.requestsPerHour + this.burstAllowance;
|
|
53
|
+
if (requestsLastMinute >= effectiveMinuteLimit) {
|
|
54
|
+
this.addViolation(context, phase, 'requests_per_minute', 'error', `Rate limit exceeded: ${requestsLastMinute} requests in the last minute (limit: ${this.requestsPerMinute})`, { requestsLastMinute, limit: this.requestsPerMinute });
|
|
55
|
+
return Promise.resolve(this.block(context, `Rate limit exceeded: ${requestsLastMinute}/${this.requestsPerMinute} requests per minute`));
|
|
56
|
+
}
|
|
57
|
+
if (requestsLastHour >= effectiveHourLimit) {
|
|
58
|
+
this.addViolation(context, phase, 'requests_per_hour', 'error', `Rate limit exceeded: ${requestsLastHour} requests in the last hour (limit: ${this.requestsPerHour})`, { requestsLastHour, limit: this.requestsPerHour });
|
|
59
|
+
return Promise.resolve(this.block(context, `Rate limit exceeded: ${requestsLastHour}/${this.requestsPerHour} requests per hour`));
|
|
60
|
+
}
|
|
61
|
+
// Log warning if approaching limit
|
|
62
|
+
if (requestsLastMinute >= this.requestsPerMinute * 0.8) {
|
|
63
|
+
this.addViolation(context, phase, 'requests_per_minute_warning', 'warning', `Approaching rate limit: ${requestsLastMinute}/${this.requestsPerMinute} requests per minute`, { requestsLastMinute, limit: this.requestsPerMinute });
|
|
64
|
+
}
|
|
65
|
+
// Record this request
|
|
66
|
+
history.push({ timestamp: now });
|
|
67
|
+
// Ensure history is stored in Map (needed after empty cleanup)
|
|
68
|
+
this.requestHistory.set(key, history);
|
|
69
|
+
return Promise.resolve(this.allow(context));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get current request counts (for testing/monitoring)
|
|
73
|
+
*/
|
|
74
|
+
getRequestCounts(key = 'global') {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
const history = this.requestHistory.get(key) || [];
|
|
77
|
+
const oneMinuteAgo = now - 60 * 1000;
|
|
78
|
+
const oneHourAgo = now - 60 * 60 * 1000;
|
|
79
|
+
return {
|
|
80
|
+
lastMinute: history.filter((r) => r.timestamp > oneMinuteAgo).length,
|
|
81
|
+
lastHour: history.filter((r) => r.timestamp > oneHourAgo).length,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reset request history (for testing)
|
|
86
|
+
*/
|
|
87
|
+
reset() {
|
|
88
|
+
this.requestHistory.clear();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/guardrails/plugins/rate-limiter.ts"],"names":[],"mappings":"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"}
|