guardrail-compliance 1.0.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/dist/audit/emitter.d.ts +97 -0
- package/dist/audit/emitter.d.ts.map +1 -0
- package/dist/audit/emitter.js +197 -0
- package/dist/audit/events.d.ts +304 -0
- package/dist/audit/events.d.ts.map +1 -0
- package/dist/audit/events.js +267 -0
- package/dist/audit/index.d.ts +11 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +51 -0
- package/dist/audit/storage.d.ts +93 -0
- package/dist/audit/storage.d.ts.map +1 -0
- package/dist/audit/storage.js +337 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts +2 -0
- package/dist/automation/__tests__/compliance-scheduler.test.d.ts.map +1 -0
- package/dist/automation/__tests__/compliance-scheduler.test.js +140 -0
- package/dist/automation/audit-logger.d.ts +129 -0
- package/dist/automation/audit-logger.d.ts.map +1 -0
- package/dist/automation/audit-logger.js +473 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts +1 -0
- package/dist/automation/compliance-scheduler-fixed.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler-fixed.js +1 -0
- package/dist/automation/compliance-scheduler.d.ts +83 -0
- package/dist/automation/compliance-scheduler.d.ts.map +1 -0
- package/dist/automation/compliance-scheduler.js +414 -0
- package/dist/automation/dashboard.d.ts +194 -0
- package/dist/automation/dashboard.d.ts.map +1 -0
- package/dist/automation/dashboard.js +768 -0
- package/dist/automation/email-service.d.ts +69 -0
- package/dist/automation/email-service.d.ts.map +1 -0
- package/dist/automation/email-service.js +218 -0
- package/dist/automation/evidence-collector.d.ts +140 -0
- package/dist/automation/evidence-collector.d.ts.map +1 -0
- package/dist/automation/evidence-collector.js +682 -0
- package/dist/automation/index.d.ts +8 -0
- package/dist/automation/index.d.ts.map +1 -0
- package/dist/automation/index.js +24 -0
- package/dist/automation/pdf-exporter.d.ts +90 -0
- package/dist/automation/pdf-exporter.d.ts.map +1 -0
- package/dist/automation/pdf-exporter.js +381 -0
- package/dist/automation/reporting-engine.d.ts +116 -0
- package/dist/automation/reporting-engine.d.ts.map +1 -0
- package/dist/automation/reporting-engine.js +329 -0
- package/dist/container/index.d.ts +4 -0
- package/dist/container/index.d.ts.map +1 -0
- package/dist/container/index.js +19 -0
- package/dist/container/kubernetes.d.ts +94 -0
- package/dist/container/kubernetes.d.ts.map +1 -0
- package/dist/container/kubernetes.js +268 -0
- package/dist/container/rules.d.ts +27 -0
- package/dist/container/rules.d.ts.map +1 -0
- package/dist/container/rules.js +216 -0
- package/dist/container/scanner.d.ts +50 -0
- package/dist/container/scanner.d.ts.map +1 -0
- package/dist/container/scanner.js +143 -0
- package/dist/frameworks/engine.d.ts +108 -0
- package/dist/frameworks/engine.d.ts.map +1 -0
- package/dist/frameworks/engine.js +206 -0
- package/dist/frameworks/gdpr.d.ts +6 -0
- package/dist/frameworks/gdpr.d.ts.map +1 -0
- package/dist/frameworks/gdpr.js +198 -0
- package/dist/frameworks/hipaa.d.ts +6 -0
- package/dist/frameworks/hipaa.d.ts.map +1 -0
- package/dist/frameworks/hipaa.js +183 -0
- package/dist/frameworks/index.d.ts +8 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/iso27001.d.ts +63 -0
- package/dist/frameworks/iso27001.d.ts.map +1 -0
- package/dist/frameworks/iso27001.js +331 -0
- package/dist/frameworks/nist.d.ts +62 -0
- package/dist/frameworks/nist.d.ts.map +1 -0
- package/dist/frameworks/nist.js +424 -0
- package/dist/frameworks/pci.d.ts +6 -0
- package/dist/frameworks/pci.d.ts.map +1 -0
- package/dist/frameworks/pci.js +201 -0
- package/dist/frameworks/soc2.d.ts +7 -0
- package/dist/frameworks/soc2.d.ts.map +1 -0
- package/dist/frameworks/soc2.js +248 -0
- package/dist/iac/drift-detector.d.ts +64 -0
- package/dist/iac/drift-detector.d.ts.map +1 -0
- package/dist/iac/drift-detector.js +134 -0
- package/dist/iac/index.d.ts +4 -0
- package/dist/iac/index.d.ts.map +1 -0
- package/dist/iac/index.js +19 -0
- package/dist/iac/rules.d.ts +17 -0
- package/dist/iac/rules.d.ts.map +1 -0
- package/dist/iac/rules.js +385 -0
- package/dist/iac/scanner.d.ts +104 -0
- package/dist/iac/scanner.d.ts.map +1 -0
- package/dist/iac/scanner.js +343 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/pii/data-flow.d.ts +58 -0
- package/dist/pii/data-flow.d.ts.map +1 -0
- package/dist/pii/data-flow.js +154 -0
- package/dist/pii/detector.d.ts +60 -0
- package/dist/pii/detector.d.ts.map +1 -0
- package/dist/pii/detector.js +267 -0
- package/dist/pii/index.d.ts +4 -0
- package/dist/pii/index.d.ts.map +1 -0
- package/dist/pii/index.js +19 -0
- package/dist/pii/patterns.d.ts +36 -0
- package/dist/pii/patterns.d.ts.map +1 -0
- package/dist/pii/patterns.js +108 -0
- package/dist/policy/index.d.ts +5 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +20 -0
- package/dist/policy/opa-engine.d.ts +121 -0
- package/dist/policy/opa-engine.d.ts.map +1 -0
- package/dist/policy/opa-engine.js +423 -0
- package/package.json +31 -0
- package/src/audit/emitter.ts +383 -0
- package/src/audit/events.ts +351 -0
- package/src/audit/index.ts +35 -0
- package/src/audit/storage.ts +394 -0
- package/src/automation/__tests__/compliance-scheduler.test.ts +183 -0
- package/src/automation/audit-logger.ts +629 -0
- package/src/automation/compliance-scheduler-fixed.ts +0 -0
- package/src/automation/compliance-scheduler.ts +516 -0
- package/src/automation/dashboard.ts +947 -0
- package/src/automation/email-service.ts +230 -0
- package/src/automation/evidence-collector.ts +866 -0
- package/src/automation/index.ts +8 -0
- package/src/automation/pdf-exporter.ts +434 -0
- package/src/automation/reporting-engine.ts +462 -0
- package/src/container/index.ts +3 -0
- package/src/container/kubernetes.ts +379 -0
- package/src/container/rules.ts +244 -0
- package/src/container/scanner.ts +202 -0
- package/src/frameworks/engine.ts +298 -0
- package/src/frameworks/gdpr.ts +204 -0
- package/src/frameworks/hipaa.ts +209 -0
- package/src/frameworks/index.ts +23 -0
- package/src/frameworks/iso27001.ts +398 -0
- package/src/frameworks/nist.ts +518 -0
- package/src/frameworks/pci.ts +226 -0
- package/src/frameworks/soc2.ts +281 -0
- package/src/iac/drift-detector.ts +197 -0
- package/src/iac/index.ts +3 -0
- package/src/iac/rules.ts +420 -0
- package/src/iac/scanner.ts +445 -0
- package/src/index.ts +17 -0
- package/src/pii/data-flow.ts +216 -0
- package/src/pii/detector.ts +327 -0
- package/src/pii/index.ts +3 -0
- package/src/pii/patterns.ts +128 -0
- package/src/policy/index.ts +5 -0
- package/src/policy/opa-engine.ts +504 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { prisma } from '@guardrail/database';
|
|
2
|
+
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { PII_PATTERNS, PII_FIELD_PATTERNS, PII_TEST_VALUES, NON_PII_CONTEXT_INDICATORS } from './patterns';
|
|
5
|
+
import { dataFlowTracker, PIIFinding, DataFlow } from './data-flow';
|
|
6
|
+
|
|
7
|
+
export interface PIIDetectionResult {
|
|
8
|
+
projectId: string;
|
|
9
|
+
summary: {
|
|
10
|
+
totalFindings: number;
|
|
11
|
+
byCategory: Record<string, number>;
|
|
12
|
+
riskLevel: 'high' | 'medium' | 'low';
|
|
13
|
+
};
|
|
14
|
+
findings: PIIFinding[];
|
|
15
|
+
dataFlows: DataFlow[];
|
|
16
|
+
recommendations: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class PIIDetector {
|
|
20
|
+
/**
|
|
21
|
+
* Detect PII in entire project
|
|
22
|
+
*/
|
|
23
|
+
async detectPII(projectPath: string, projectId: string): Promise<PIIDetectionResult> {
|
|
24
|
+
const allFindings: PIIFinding[] = [];
|
|
25
|
+
|
|
26
|
+
// Find all source files
|
|
27
|
+
const sourceFiles = this.findSourceFiles(projectPath);
|
|
28
|
+
|
|
29
|
+
// Scan each file
|
|
30
|
+
for (const filePath of sourceFiles) {
|
|
31
|
+
try {
|
|
32
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
33
|
+
const findings = this.scanContent(content, filePath);
|
|
34
|
+
allFindings.push(...findings);
|
|
35
|
+
|
|
36
|
+
// Also scan for PII field names
|
|
37
|
+
const fieldFindings = this.scanFieldNames(content, filePath);
|
|
38
|
+
allFindings.push(...fieldFindings);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Error scanning file ${filePath}:`, error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Track data flows
|
|
45
|
+
const dataFlows = await this.trackPIIDataFlows(projectPath, allFindings);
|
|
46
|
+
|
|
47
|
+
// Generate recommendations
|
|
48
|
+
const recommendations = this.generateRecommendations(allFindings, dataFlows);
|
|
49
|
+
|
|
50
|
+
// Calculate summary
|
|
51
|
+
const byCategory: Record<string, number> = {};
|
|
52
|
+
for (const finding of allFindings) {
|
|
53
|
+
byCategory[finding.category] = (byCategory[finding.category] || 0) + 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const highSeverityCount = allFindings.filter(f => f.severity === 'high').length;
|
|
57
|
+
const riskLevel: 'high' | 'medium' | 'low' =
|
|
58
|
+
highSeverityCount > 5 ? 'high' :
|
|
59
|
+
highSeverityCount > 0 ? 'medium' : 'low';
|
|
60
|
+
|
|
61
|
+
const result: PIIDetectionResult = {
|
|
62
|
+
projectId,
|
|
63
|
+
summary: {
|
|
64
|
+
totalFindings: allFindings.length,
|
|
65
|
+
byCategory,
|
|
66
|
+
riskLevel
|
|
67
|
+
},
|
|
68
|
+
findings: allFindings,
|
|
69
|
+
dataFlows,
|
|
70
|
+
recommendations
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Save to database
|
|
74
|
+
try {
|
|
75
|
+
await prisma.pIIDetection.create({
|
|
76
|
+
data: {
|
|
77
|
+
projectId
|
|
78
|
+
} as any
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Table may not exist - continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Scan file content for PII patterns
|
|
89
|
+
*/
|
|
90
|
+
private scanContent(content: string, filePath: string): PIIFinding[] {
|
|
91
|
+
const findings: PIIFinding[] = [];
|
|
92
|
+
const lines = content.split('\n');
|
|
93
|
+
|
|
94
|
+
for (const pattern of PII_PATTERNS) {
|
|
95
|
+
let match;
|
|
96
|
+
while ((match = pattern.pattern.exec(content)) !== null) {
|
|
97
|
+
const value = match[0];
|
|
98
|
+
|
|
99
|
+
// Skip test values
|
|
100
|
+
if (this.isTestValue(value)) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get line and column
|
|
105
|
+
const position = this.getLineAndColumn(content, match.index);
|
|
106
|
+
|
|
107
|
+
// Get context
|
|
108
|
+
const context = this.getContext(lines, position.line);
|
|
109
|
+
|
|
110
|
+
// Skip if in non-PII context
|
|
111
|
+
if (this.isNonPIIContext(context)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
findings.push({
|
|
116
|
+
category: pattern.category,
|
|
117
|
+
value: this.maskValue(value, pattern.category),
|
|
118
|
+
location: {
|
|
119
|
+
file: filePath,
|
|
120
|
+
line: position.line + 1,
|
|
121
|
+
column: position.column
|
|
122
|
+
},
|
|
123
|
+
context,
|
|
124
|
+
severity: pattern.severity
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Reset lastIndex for global regex
|
|
129
|
+
pattern.pattern.lastIndex = 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return findings;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Scan AST for PII field names
|
|
137
|
+
*/
|
|
138
|
+
private scanFieldNames(content: string, filePath: string): PIIFinding[] {
|
|
139
|
+
const findings: PIIFinding[] = [];
|
|
140
|
+
const lines = content.split('\n');
|
|
141
|
+
|
|
142
|
+
// Simple pattern matching for field names
|
|
143
|
+
// In production, would use proper AST parsing
|
|
144
|
+
|
|
145
|
+
for (let i = 0; i < lines.length; i++) {
|
|
146
|
+
const line = lines[i];
|
|
147
|
+
if (!line) continue;
|
|
148
|
+
|
|
149
|
+
for (const fieldPattern of PII_FIELD_PATTERNS) {
|
|
150
|
+
const matches = line.matchAll(fieldPattern.pattern);
|
|
151
|
+
|
|
152
|
+
for (const match of matches) {
|
|
153
|
+
const fieldName = match[0];
|
|
154
|
+
const context = this.getContext(lines, i);
|
|
155
|
+
|
|
156
|
+
// Skip if in comments or strings
|
|
157
|
+
if (line.trim().startsWith('//') || line.trim().startsWith('*')) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
findings.push({
|
|
162
|
+
category: fieldPattern.category,
|
|
163
|
+
value: fieldName,
|
|
164
|
+
location: {
|
|
165
|
+
file: filePath,
|
|
166
|
+
line: i + 1,
|
|
167
|
+
column: match.index || 0
|
|
168
|
+
},
|
|
169
|
+
context,
|
|
170
|
+
severity: fieldPattern.severity
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return findings;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Track data flows for PII
|
|
181
|
+
*/
|
|
182
|
+
private async trackPIIDataFlows(_projectPath: string, findings: PIIFinding[]): Promise<DataFlow[]> {
|
|
183
|
+
// Use data flow tracker to analyze flows
|
|
184
|
+
return dataFlowTracker.analyzeDataFlows(findings);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Generate recommendations based on findings
|
|
189
|
+
*/
|
|
190
|
+
private generateRecommendations(findings: PIIFinding[], dataFlows: DataFlow[]): string[] {
|
|
191
|
+
const recommendations: string[] = [];
|
|
192
|
+
|
|
193
|
+
// Count findings by category
|
|
194
|
+
const bySeverity = {
|
|
195
|
+
high: findings.filter(f => f.severity === 'high').length,
|
|
196
|
+
medium: findings.filter(f => f.severity === 'medium').length,
|
|
197
|
+
low: findings.filter(f => f.severity === 'low').length
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
if (bySeverity.high > 0) {
|
|
201
|
+
recommendations.push(`🔴 Found ${bySeverity.high} high-severity PII findings - immediate action required`);
|
|
202
|
+
recommendations.push('Implement encryption for sensitive data fields');
|
|
203
|
+
recommendations.push('Review and minimize PII storage');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Check for unencrypted storage
|
|
207
|
+
const unencryptedStorage = dataFlows.some(flow =>
|
|
208
|
+
flow.storage.some(s => !s.encrypted)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (unencryptedStorage) {
|
|
212
|
+
recommendations.push('⚠️ PII detected in unencrypted storage - enable encryption at rest');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Check for external transfers
|
|
216
|
+
const hasExternalTransfers = dataFlows.some(flow => flow.externalTransfers.length > 0);
|
|
217
|
+
|
|
218
|
+
if (hasExternalTransfers) {
|
|
219
|
+
recommendations.push('📡 PII transferred to external systems - ensure proper data processing agreements');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// General recommendations
|
|
223
|
+
if (findings.length > 0) {
|
|
224
|
+
recommendations.push('Implement data retention policies');
|
|
225
|
+
recommendations.push('Add user consent mechanisms for PII collection');
|
|
226
|
+
recommendations.push('Consider pseudonymization or anonymization techniques');
|
|
227
|
+
recommendations.push('Implement access controls for PII data');
|
|
228
|
+
recommendations.push('Add audit logging for PII access');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return recommendations;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Find source files in project
|
|
236
|
+
*/
|
|
237
|
+
private findSourceFiles(dir: string): string[] {
|
|
238
|
+
const files: string[] = [];
|
|
239
|
+
const extensions = ['.ts', '.js', '.tsx', '.jsx', '.py', '.java', '.go', '.rb', '.php'];
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const entries = readdirSync(dir);
|
|
243
|
+
|
|
244
|
+
for (const entry of entries) {
|
|
245
|
+
const fullPath = join(dir, entry);
|
|
246
|
+
const stat = statSync(fullPath);
|
|
247
|
+
|
|
248
|
+
if (stat.isDirectory() && !entry.startsWith('.') && entry !== 'node_modules' && entry !== 'dist') {
|
|
249
|
+
files.push(...this.findSourceFiles(fullPath));
|
|
250
|
+
} else if (stat.isFile()) {
|
|
251
|
+
if (extensions.some(ext => entry.endsWith(ext))) {
|
|
252
|
+
files.push(fullPath);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
} catch (error) {
|
|
257
|
+
// Ignore permission errors
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return files;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Check if value is a test value
|
|
265
|
+
*/
|
|
266
|
+
private isTestValue(value: string): boolean {
|
|
267
|
+
return PII_TEST_VALUES.some(test => value.includes(test));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Check if context indicates non-PII usage
|
|
272
|
+
*/
|
|
273
|
+
private isNonPIIContext(context: string): boolean {
|
|
274
|
+
const lowerContext = context.toLowerCase();
|
|
275
|
+
return NON_PII_CONTEXT_INDICATORS.some(indicator =>
|
|
276
|
+
lowerContext.includes(indicator)
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Get line and column from position
|
|
282
|
+
*/
|
|
283
|
+
private getLineAndColumn(content: string, position: number): { line: number; column: number } {
|
|
284
|
+
const lines = content.substring(0, position).split('\n');
|
|
285
|
+
const lastLine = lines[lines.length - 1] || '';
|
|
286
|
+
return {
|
|
287
|
+
line: lines.length - 1,
|
|
288
|
+
column: lastLine.length
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Get context around a line
|
|
294
|
+
*/
|
|
295
|
+
private getContext(lines: string[], lineIndex: number, contextSize: number = 2): string {
|
|
296
|
+
const start = Math.max(0, lineIndex - contextSize);
|
|
297
|
+
const end = Math.min(lines.length, lineIndex + contextSize + 1);
|
|
298
|
+
return lines.slice(start, end).join('\n');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Mask sensitive value
|
|
303
|
+
*/
|
|
304
|
+
private maskValue(value: string, category: string): string {
|
|
305
|
+
if (category === 'email') {
|
|
306
|
+
const parts = value.split('@');
|
|
307
|
+
const local = parts[0];
|
|
308
|
+
const domain = parts[1];
|
|
309
|
+
if (local && domain) {
|
|
310
|
+
return `${local.charAt(0)}***@${domain}`;
|
|
311
|
+
}
|
|
312
|
+
return '***@***.com';
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (category === 'phone') {
|
|
316
|
+
return `***-***-${value.slice(-4)}`;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (category === 'ssn' || category === 'credit-card') {
|
|
320
|
+
return `***-**-${value.slice(-4)}`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return `${value.slice(0, 3)}***`;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export const piiDetector = new PIIDetector();
|
package/src/pii/index.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export interface PIIPattern {
|
|
2
|
+
category: 'email' | 'phone' | 'ssn' | 'credit-card' | 'ip-address' | 'name-field' | 'address' | 'dob' | 'health' | 'financial';
|
|
3
|
+
description: string;
|
|
4
|
+
pattern: RegExp;
|
|
5
|
+
severity: 'high' | 'medium' | 'low';
|
|
6
|
+
examples: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PII Detection Patterns
|
|
11
|
+
*/
|
|
12
|
+
export const PII_PATTERNS: PIIPattern[] = [
|
|
13
|
+
// Email Addresses
|
|
14
|
+
{
|
|
15
|
+
category: 'email',
|
|
16
|
+
description: 'Email address',
|
|
17
|
+
pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g,
|
|
18
|
+
severity: 'medium',
|
|
19
|
+
examples: ['user@example.com', 'john.doe@company.co.uk']
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// Phone Numbers
|
|
23
|
+
{
|
|
24
|
+
category: 'phone',
|
|
25
|
+
description: 'US Phone number',
|
|
26
|
+
pattern: /\b(\+1[-.\s]?)?(\(?\d{3}\)?[-.\s]?)?\d{3}[-.\s]?\d{4}\b/g,
|
|
27
|
+
severity: 'medium',
|
|
28
|
+
examples: ['+1-555-123-4567', '(555) 123-4567', '555-123-4567']
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// Social Security Numbers
|
|
32
|
+
{
|
|
33
|
+
category: 'ssn',
|
|
34
|
+
description: 'US Social Security Number',
|
|
35
|
+
pattern: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
36
|
+
severity: 'high',
|
|
37
|
+
examples: ['123-45-6789']
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// Credit Card Numbers (Luhn algorithm pattern)
|
|
41
|
+
{
|
|
42
|
+
category: 'credit-card',
|
|
43
|
+
description: 'Credit card number',
|
|
44
|
+
pattern: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
|
|
45
|
+
severity: 'high',
|
|
46
|
+
examples: ['4532-1234-5678-9010', '5425 2334 3010 9903']
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// IP Addresses
|
|
50
|
+
{
|
|
51
|
+
category: 'ip-address',
|
|
52
|
+
description: 'IPv4 address',
|
|
53
|
+
pattern: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
54
|
+
severity: 'low',
|
|
55
|
+
examples: ['192.168.1.1', '10.0.0.1']
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Field name patterns that suggest PII
|
|
61
|
+
*/
|
|
62
|
+
export const PII_FIELD_PATTERNS = [
|
|
63
|
+
// Name fields
|
|
64
|
+
{ pattern: /\b(first_?name|last_?name|full_?name|given_?name|family_?name)\b/i, category: 'name-field', severity: 'medium' as const },
|
|
65
|
+
{ pattern: /\b(name)\b/i, category: 'name-field', severity: 'low' as const },
|
|
66
|
+
|
|
67
|
+
// Email fields
|
|
68
|
+
{ pattern: /\b(email|e_?mail|email_?address)\b/i, category: 'email', severity: 'medium' as const },
|
|
69
|
+
|
|
70
|
+
// Phone fields
|
|
71
|
+
{ pattern: /\b(phone|telephone|mobile|cell_?phone)\b/i, category: 'phone', severity: 'medium' as const },
|
|
72
|
+
|
|
73
|
+
// Address fields
|
|
74
|
+
{ pattern: /\b(address|street|city|state|zip|postal_?code|country)\b/i, category: 'address', severity: 'medium' as const },
|
|
75
|
+
{ pattern: /\b(billing_?address|shipping_?address|home_?address)\b/i, category: 'address', severity: 'high' as const },
|
|
76
|
+
|
|
77
|
+
// Date of Birth
|
|
78
|
+
{ pattern: /\b(dob|date_?of_?birth|birth_?date|birthday)\b/i, category: 'dob', severity: 'high' as const },
|
|
79
|
+
|
|
80
|
+
// Social Security
|
|
81
|
+
{ pattern: /\b(ssn|social_?security|national_?id)\b/i, category: 'ssn', severity: 'high' as const },
|
|
82
|
+
|
|
83
|
+
// Financial
|
|
84
|
+
{ pattern: /\b(credit_?card|card_?number|cvv|cvc|account_?number|routing_?number|iban)\b/i, category: 'financial', severity: 'high' as const },
|
|
85
|
+
{ pattern: /\b(salary|income|tax_?id|ein)\b/i, category: 'financial', severity: 'high' as const },
|
|
86
|
+
|
|
87
|
+
// Health
|
|
88
|
+
{ pattern: /\b(medical_?record|patient_?id|diagnosis|prescription|health_?insurance)\b/i, category: 'health', severity: 'high' as const },
|
|
89
|
+
{ pattern: /\b(blood_?type|allergies|medication)\b/i, category: 'health', severity: 'high' as const },
|
|
90
|
+
|
|
91
|
+
// Authentication
|
|
92
|
+
{ pattern: /\b(password|passwd|pwd|secret|token|api_?key)\b/i, category: 'financial', severity: 'high' as const }
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Test values that should be excluded
|
|
97
|
+
*/
|
|
98
|
+
export const PII_TEST_VALUES = [
|
|
99
|
+
'test@example.com',
|
|
100
|
+
'user@example.com',
|
|
101
|
+
'admin@example.com',
|
|
102
|
+
'noreply@example.com',
|
|
103
|
+
'555-0100', // Reserved for testing
|
|
104
|
+
'555-0199',
|
|
105
|
+
'000-00-0000', // Invalid SSN
|
|
106
|
+
'123-45-6789', // Well-known fake SSN
|
|
107
|
+
'127.0.0.1',
|
|
108
|
+
'localhost',
|
|
109
|
+
'0.0.0.0',
|
|
110
|
+
'example.com',
|
|
111
|
+
'test.com'
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Context indicators that suggest non-PII usage
|
|
116
|
+
*/
|
|
117
|
+
export const NON_PII_CONTEXT_INDICATORS = [
|
|
118
|
+
'example',
|
|
119
|
+
'test',
|
|
120
|
+
'demo',
|
|
121
|
+
'sample',
|
|
122
|
+
'placeholder',
|
|
123
|
+
'fake',
|
|
124
|
+
'mock',
|
|
125
|
+
'dummy',
|
|
126
|
+
'template',
|
|
127
|
+
'default'
|
|
128
|
+
];
|