shieldcortex 2.6.4 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -5
- package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
- package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
- package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_25b1b286._.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/00fd16d311d2adfc.css +3 -0
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/{3255204bf9fadccd.js → 8d38247f89b93596.js} +1 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/{d0e13004c4367a98.js → a4856321b5a33f59.js} +1 -1
- package/dist/api/visualization-server.d.ts.map +1 -1
- package/dist/api/visualization-server.js +29 -1
- package/dist/api/visualization-server.js.map +1 -1
- package/dist/cloud/cli.d.ts.map +1 -1
- package/dist/cloud/cli.js +18 -3
- package/dist/cloud/cli.js.map +1 -1
- package/dist/cloud/config.d.ts +9 -0
- package/dist/cloud/config.d.ts.map +1 -1
- package/dist/cloud/config.js +23 -0
- package/dist/cloud/config.js.map +1 -1
- package/dist/defence/__tests__/credential-leak.test.d.ts +8 -0
- package/dist/defence/__tests__/credential-leak.test.d.ts.map +1 -0
- package/dist/defence/__tests__/credential-leak.test.js +403 -0
- package/dist/defence/__tests__/credential-leak.test.js.map +1 -0
- package/dist/defence/credential-leak/entropy.d.ts +42 -0
- package/dist/defence/credential-leak/entropy.d.ts.map +1 -0
- package/dist/defence/credential-leak/entropy.js +105 -0
- package/dist/defence/credential-leak/entropy.js.map +1 -0
- package/dist/defence/credential-leak/index.d.ts +54 -0
- package/dist/defence/credential-leak/index.d.ts.map +1 -0
- package/dist/defence/credential-leak/index.js +168 -0
- package/dist/defence/credential-leak/index.js.map +1 -0
- package/dist/defence/credential-leak/patterns.d.ts +26 -0
- package/dist/defence/credential-leak/patterns.d.ts.map +1 -0
- package/dist/defence/credential-leak/patterns.js +304 -0
- package/dist/defence/credential-leak/patterns.js.map +1 -0
- package/dist/defence/index.d.ts +4 -2
- package/dist/defence/index.d.ts.map +1 -1
- package/dist/defence/index.js +3 -1
- package/dist/defence/index.js.map +1 -1
- package/dist/defence/pipeline.d.ts +1 -1
- package/dist/defence/pipeline.d.ts.map +1 -1
- package/dist/defence/pipeline.js +26 -3
- package/dist/defence/pipeline.js.map +1 -1
- package/dist/defence/types.d.ts +1 -0
- package/dist/defence/types.d.ts.map +1 -1
- package/dist/defence/types.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dashboard/.next/standalone/dashboard/.next/static/chunks/ddb21377f32257df.css +0 -3
- /package/dashboard/.next/standalone/dashboard/.next/static/{ku0ZhFF_US0fWJ3A7pvJa → d3yvcqXn0tuMKtjxk5bhH}/_buildManifest.js +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{ku0ZhFF_US0fWJ3A7pvJa → d3yvcqXn0tuMKtjxk5bhH}/_clientMiddlewareManifest.json +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{ku0ZhFF_US0fWJ3A7pvJa → d3yvcqXn0tuMKtjxk5bhH}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Leak Detection — Entropy Analysis
|
|
3
|
+
*
|
|
4
|
+
* Shannon entropy calculation for detecting high-entropy strings
|
|
5
|
+
* that look like secrets (random tokens, keys, passwords).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Calculate Shannon entropy of a string (bits per character).
|
|
9
|
+
* Higher entropy = more random/unpredictable.
|
|
10
|
+
*
|
|
11
|
+
* Reference values:
|
|
12
|
+
* - English text: ~3.5-4.0
|
|
13
|
+
* - Base64 encoded: ~5.0-5.5
|
|
14
|
+
* - Random hex: ~3.5-4.0
|
|
15
|
+
* - Random alphanumeric: ~5.5-5.9
|
|
16
|
+
* - Crypto keys: ~5.5-6.0+
|
|
17
|
+
*/
|
|
18
|
+
export function shannonEntropy(str) {
|
|
19
|
+
if (str.length === 0)
|
|
20
|
+
return 0;
|
|
21
|
+
const freq = new Map();
|
|
22
|
+
for (const ch of str) {
|
|
23
|
+
freq.set(ch, (freq.get(ch) ?? 0) + 1);
|
|
24
|
+
}
|
|
25
|
+
let entropy = 0;
|
|
26
|
+
const len = str.length;
|
|
27
|
+
for (const count of freq.values()) {
|
|
28
|
+
const p = count / len;
|
|
29
|
+
entropy -= p * Math.log2(p);
|
|
30
|
+
}
|
|
31
|
+
return entropy;
|
|
32
|
+
}
|
|
33
|
+
/** Minimum string length for entropy-based detection */
|
|
34
|
+
export const MIN_ENTROPY_LENGTH = 20;
|
|
35
|
+
/** Entropy threshold — strings above this are flagged */
|
|
36
|
+
export const ENTROPY_THRESHOLD = 4.5;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a string looks like a high-entropy secret.
|
|
39
|
+
* Returns confidence score (0-1) or null if not suspicious.
|
|
40
|
+
*/
|
|
41
|
+
export function checkHighEntropy(str) {
|
|
42
|
+
if (str.length < MIN_ENTROPY_LENGTH)
|
|
43
|
+
return null;
|
|
44
|
+
const entropy = shannonEntropy(str);
|
|
45
|
+
if (entropy < ENTROPY_THRESHOLD)
|
|
46
|
+
return null;
|
|
47
|
+
// Confidence scales with entropy above threshold
|
|
48
|
+
// 4.5 → 0.50, 5.0 → 0.65, 5.5 → 0.80, 6.0 → 0.95
|
|
49
|
+
const confidence = Math.min(0.95, 0.50 + (entropy - ENTROPY_THRESHOLD) * 0.30);
|
|
50
|
+
return { entropy, confidence };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract candidate high-entropy tokens from content.
|
|
54
|
+
* Looks for long contiguous alphanumeric+special strings
|
|
55
|
+
* that could be secrets.
|
|
56
|
+
*/
|
|
57
|
+
export function extractHighEntropyTokens(content) {
|
|
58
|
+
// Match long strings of alphanumeric + common secret chars
|
|
59
|
+
const tokenRegex = /[A-Za-z0-9\-_./+=]{20,}/g;
|
|
60
|
+
const results = [];
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
let match;
|
|
63
|
+
while ((match = tokenRegex.exec(content)) !== null) {
|
|
64
|
+
const token = match[0];
|
|
65
|
+
if (seen.has(token))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(token);
|
|
68
|
+
// Skip common false positives
|
|
69
|
+
if (isLikelyFalsePositive(token))
|
|
70
|
+
continue;
|
|
71
|
+
const result = checkHighEntropy(token);
|
|
72
|
+
if (result) {
|
|
73
|
+
results.push({
|
|
74
|
+
token,
|
|
75
|
+
position: match.index,
|
|
76
|
+
entropy: result.entropy,
|
|
77
|
+
confidence: result.confidence,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Heuristic filter to reduce false positives from entropy detection.
|
|
85
|
+
*/
|
|
86
|
+
function isLikelyFalsePositive(token) {
|
|
87
|
+
// Pure lowercase with dashes — likely a slug or CSS class
|
|
88
|
+
if (/^[a-z\-]+$/.test(token))
|
|
89
|
+
return true;
|
|
90
|
+
// Looks like a file path
|
|
91
|
+
if (token.includes('/') && !token.includes('//') && /\.[a-z]{2,4}$/i.test(token))
|
|
92
|
+
return true;
|
|
93
|
+
// Repeated character sequences (aaaaaaa...)
|
|
94
|
+
if (/^(.)\1{10,}$/.test(token))
|
|
95
|
+
return true;
|
|
96
|
+
// Common base64 padding pattern (just padding)
|
|
97
|
+
if (/^=+$/.test(token) || /^[A-Za-z0-9+/]*={3,}$/.test(token))
|
|
98
|
+
return true;
|
|
99
|
+
// Long runs of a single character class with low variety
|
|
100
|
+
const uniqueChars = new Set(token).size;
|
|
101
|
+
if (uniqueChars < 6 && token.length > 20)
|
|
102
|
+
return true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=entropy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entropy.js","sourceRoot":"","sources":["../../../src/defence/credential-leak/entropy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Leak Detection — Layer 6
|
|
3
|
+
*
|
|
4
|
+
* Detects credentials, secrets, and sensitive tokens accidentally
|
|
5
|
+
* persisted in AI agent memory writes. Supports known API key formats,
|
|
6
|
+
* generic secrets, private keys, connection strings, environment
|
|
7
|
+
* variable patterns, and high-entropy string heuristics.
|
|
8
|
+
*/
|
|
9
|
+
import { type CredentialPattern, type CredentialType, type CredentialSeverity } from './patterns.js';
|
|
10
|
+
export interface CredentialFinding {
|
|
11
|
+
type: CredentialType;
|
|
12
|
+
provider?: string;
|
|
13
|
+
confidence: number;
|
|
14
|
+
severity: CredentialSeverity;
|
|
15
|
+
/** Redacted version showing first/last 4 chars */
|
|
16
|
+
match: string;
|
|
17
|
+
/** Char offset in content */
|
|
18
|
+
position: number;
|
|
19
|
+
action: 'blocked' | 'warned' | 'logged';
|
|
20
|
+
}
|
|
21
|
+
export interface CredentialScanResult {
|
|
22
|
+
leaked: boolean;
|
|
23
|
+
findings: CredentialFinding[];
|
|
24
|
+
redactedContent?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface CredentialDetectionConfig {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
blockOnCritical: boolean;
|
|
29
|
+
blockOnHigh: boolean;
|
|
30
|
+
warnOnMedium: boolean;
|
|
31
|
+
customPatterns: CredentialPattern[];
|
|
32
|
+
allowlist: string[];
|
|
33
|
+
}
|
|
34
|
+
export declare const DEFAULT_CREDENTIAL_CONFIG: CredentialDetectionConfig;
|
|
35
|
+
/**
|
|
36
|
+
* Scan content for credential leaks.
|
|
37
|
+
*
|
|
38
|
+
* Checks known API key formats, generic secrets, private keys,
|
|
39
|
+
* connection strings, env variable patterns, and high-entropy strings.
|
|
40
|
+
*
|
|
41
|
+
* @param content - The text content to scan
|
|
42
|
+
* @param config - Optional credential detection configuration
|
|
43
|
+
* @returns Scan result with findings and optional redacted content
|
|
44
|
+
*/
|
|
45
|
+
export declare function scanForCredentials(content: string, config?: Partial<CredentialDetectionConfig>): CredentialScanResult;
|
|
46
|
+
/**
|
|
47
|
+
* Replace all detected secrets in content with [REDACTED-{type}] placeholders.
|
|
48
|
+
* Useful for agents that want to store memory but strip the secrets.
|
|
49
|
+
*/
|
|
50
|
+
export declare function redactCredentials(content: string, config?: Partial<CredentialDetectionConfig>): string;
|
|
51
|
+
export type { CredentialPattern, CredentialType, CredentialSeverity } from './patterns.js';
|
|
52
|
+
export { shannonEntropy, checkHighEntropy } from './entropy.js';
|
|
53
|
+
export { ALL_CREDENTIAL_PATTERNS } from './patterns.js';
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/defence/credential-leak/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAKvB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,eAAO,MAAM,yBAAyB,EAAE,yBAOvC,CAAC;AAuCF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,GAC1C,oBAAoB,CAmGtB;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,GAC1C,MAAM,CAGR;AAmBD,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Leak Detection — Layer 6
|
|
3
|
+
*
|
|
4
|
+
* Detects credentials, secrets, and sensitive tokens accidentally
|
|
5
|
+
* persisted in AI agent memory writes. Supports known API key formats,
|
|
6
|
+
* generic secrets, private keys, connection strings, environment
|
|
7
|
+
* variable patterns, and high-entropy string heuristics.
|
|
8
|
+
*/
|
|
9
|
+
import { ALL_CREDENTIAL_PATTERNS, } from './patterns.js';
|
|
10
|
+
import { extractHighEntropyTokens } from './entropy.js';
|
|
11
|
+
export const DEFAULT_CREDENTIAL_CONFIG = {
|
|
12
|
+
enabled: true,
|
|
13
|
+
blockOnCritical: true,
|
|
14
|
+
blockOnHigh: true,
|
|
15
|
+
warnOnMedium: true,
|
|
16
|
+
customPatterns: [],
|
|
17
|
+
allowlist: [],
|
|
18
|
+
};
|
|
19
|
+
// ── Redaction ──
|
|
20
|
+
/**
|
|
21
|
+
* Redact a matched secret, showing first and last 4 chars.
|
|
22
|
+
* Very short matches get fully redacted.
|
|
23
|
+
*/
|
|
24
|
+
function redactMatch(value, type) {
|
|
25
|
+
if (value.length <= 12)
|
|
26
|
+
return `[REDACTED-${type}]`;
|
|
27
|
+
return `${value.slice(0, 4)}...${value.slice(-4)}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Determine action based on severity and config.
|
|
31
|
+
*/
|
|
32
|
+
function actionForSeverity(severity, config) {
|
|
33
|
+
if (severity === 'critical' && config.blockOnCritical)
|
|
34
|
+
return 'blocked';
|
|
35
|
+
if (severity === 'high' && config.blockOnHigh)
|
|
36
|
+
return 'blocked';
|
|
37
|
+
if (severity === 'medium' && config.warnOnMedium)
|
|
38
|
+
return 'warned';
|
|
39
|
+
return 'logged';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if a match is in the allowlist.
|
|
43
|
+
* Allowlist entries can be literal prefixes or glob-like patterns.
|
|
44
|
+
*/
|
|
45
|
+
function isAllowlisted(value, allowlist) {
|
|
46
|
+
for (const entry of allowlist) {
|
|
47
|
+
if (value.startsWith(entry) || value === entry)
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// ── Scanner ──
|
|
53
|
+
/**
|
|
54
|
+
* Scan content for credential leaks.
|
|
55
|
+
*
|
|
56
|
+
* Checks known API key formats, generic secrets, private keys,
|
|
57
|
+
* connection strings, env variable patterns, and high-entropy strings.
|
|
58
|
+
*
|
|
59
|
+
* @param content - The text content to scan
|
|
60
|
+
* @param config - Optional credential detection configuration
|
|
61
|
+
* @returns Scan result with findings and optional redacted content
|
|
62
|
+
*/
|
|
63
|
+
export function scanForCredentials(content, config) {
|
|
64
|
+
const cfg = { ...DEFAULT_CREDENTIAL_CONFIG, ...config };
|
|
65
|
+
if (!cfg.enabled || !content || content.length === 0) {
|
|
66
|
+
return { leaked: false, findings: [] };
|
|
67
|
+
}
|
|
68
|
+
const findings = [];
|
|
69
|
+
const matchedRanges = [];
|
|
70
|
+
const patterns = [...ALL_CREDENTIAL_PATTERNS, ...cfg.customPatterns];
|
|
71
|
+
// Run all pattern matchers
|
|
72
|
+
for (const pattern of patterns) {
|
|
73
|
+
// Reset regex lastIndex for each scan
|
|
74
|
+
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
75
|
+
let match;
|
|
76
|
+
while ((match = regex.exec(content)) !== null) {
|
|
77
|
+
const fullMatch = match[0];
|
|
78
|
+
// For patterns with capture groups, use the group; otherwise the full match
|
|
79
|
+
const secretValue = match[1] ?? fullMatch;
|
|
80
|
+
// Skip if below minimum length
|
|
81
|
+
if (pattern.minLength && secretValue.length < pattern.minLength)
|
|
82
|
+
continue;
|
|
83
|
+
// Skip allowlisted values
|
|
84
|
+
if (isAllowlisted(secretValue, cfg.allowlist))
|
|
85
|
+
continue;
|
|
86
|
+
// Skip if this range is already covered by a higher-priority pattern
|
|
87
|
+
const start = match.index;
|
|
88
|
+
const end = start + fullMatch.length;
|
|
89
|
+
if (matchedRanges.some(r => start >= r.start && end <= r.end))
|
|
90
|
+
continue;
|
|
91
|
+
const action = actionForSeverity(pattern.severity, cfg);
|
|
92
|
+
const redacted = redactMatch(secretValue, pattern.type);
|
|
93
|
+
findings.push({
|
|
94
|
+
type: pattern.type,
|
|
95
|
+
provider: pattern.provider,
|
|
96
|
+
confidence: pattern.confidence,
|
|
97
|
+
severity: pattern.severity,
|
|
98
|
+
match: redacted,
|
|
99
|
+
position: start,
|
|
100
|
+
action,
|
|
101
|
+
});
|
|
102
|
+
const replacement = `[REDACTED-${pattern.type}${pattern.provider ? `-${pattern.provider}` : ''}]`;
|
|
103
|
+
matchedRanges.push({ start, end, replacement });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Run entropy-based detection for anything not already caught
|
|
107
|
+
const entropyTokens = extractHighEntropyTokens(content);
|
|
108
|
+
for (const token of entropyTokens) {
|
|
109
|
+
const start = token.position;
|
|
110
|
+
const end = start + token.token.length;
|
|
111
|
+
// Skip if already caught by pattern matching
|
|
112
|
+
if (matchedRanges.some(r => (start >= r.start && start < r.end) ||
|
|
113
|
+
(end > r.start && end <= r.end)))
|
|
114
|
+
continue;
|
|
115
|
+
// Skip allowlisted
|
|
116
|
+
if (isAllowlisted(token.token, cfg.allowlist))
|
|
117
|
+
continue;
|
|
118
|
+
const severity = token.confidence >= 0.8 ? 'medium' : 'low';
|
|
119
|
+
const action = actionForSeverity(severity, cfg);
|
|
120
|
+
findings.push({
|
|
121
|
+
type: 'high_entropy',
|
|
122
|
+
confidence: token.confidence,
|
|
123
|
+
severity,
|
|
124
|
+
match: redactMatch(token.token, 'high_entropy'),
|
|
125
|
+
position: start,
|
|
126
|
+
action,
|
|
127
|
+
});
|
|
128
|
+
matchedRanges.push({ start, end, replacement: '[REDACTED-high_entropy]' });
|
|
129
|
+
}
|
|
130
|
+
// Sort findings by position
|
|
131
|
+
findings.sort((a, b) => a.position - b.position);
|
|
132
|
+
const leaked = findings.length > 0;
|
|
133
|
+
const hasBlocked = findings.some(f => f.action === 'blocked');
|
|
134
|
+
// Build redacted content if any findings
|
|
135
|
+
let redactedContent;
|
|
136
|
+
if (leaked) {
|
|
137
|
+
redactedContent = buildRedactedContent(content, matchedRanges);
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
leaked,
|
|
141
|
+
findings,
|
|
142
|
+
redactedContent: hasBlocked ? redactedContent : redactedContent,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// ── Redaction Helper ──
|
|
146
|
+
/**
|
|
147
|
+
* Replace all detected secrets in content with [REDACTED-{type}] placeholders.
|
|
148
|
+
* Useful for agents that want to store memory but strip the secrets.
|
|
149
|
+
*/
|
|
150
|
+
export function redactCredentials(content, config) {
|
|
151
|
+
const result = scanForCredentials(content, config);
|
|
152
|
+
return result.redactedContent ?? content;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build redacted content by replacing matched ranges.
|
|
156
|
+
*/
|
|
157
|
+
function buildRedactedContent(content, ranges) {
|
|
158
|
+
// Sort by start position descending to replace from end to start
|
|
159
|
+
const sorted = [...ranges].sort((a, b) => b.start - a.start);
|
|
160
|
+
let result = content;
|
|
161
|
+
for (const range of sorted) {
|
|
162
|
+
result = result.slice(0, range.start) + range.replacement + result.slice(range.end);
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
export { shannonEntropy, checkHighEntropy } from './entropy.js';
|
|
167
|
+
export { ALL_CREDENTIAL_PATTERNS } from './patterns.js';
|
|
168
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/defence/credential-leak/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Leak Detection — Pattern Definitions
|
|
3
|
+
*
|
|
4
|
+
* Known API key formats, secret patterns, and heuristic matchers
|
|
5
|
+
* for detecting accidentally persisted credentials in AI agent memory.
|
|
6
|
+
*/
|
|
7
|
+
export interface CredentialPattern {
|
|
8
|
+
name: string;
|
|
9
|
+
type: CredentialType;
|
|
10
|
+
provider?: string;
|
|
11
|
+
regex: RegExp;
|
|
12
|
+
severity: CredentialSeverity;
|
|
13
|
+
/** Base confidence when pattern matches (can be boosted by entropy) */
|
|
14
|
+
confidence: number;
|
|
15
|
+
/** Minimum match length to avoid false positives */
|
|
16
|
+
minLength?: number;
|
|
17
|
+
}
|
|
18
|
+
export type CredentialType = 'api_key' | 'jwt' | 'private_key' | 'connection_string' | 'env_secret' | 'high_entropy';
|
|
19
|
+
export type CredentialSeverity = 'critical' | 'high' | 'medium' | 'low';
|
|
20
|
+
export declare const API_KEY_PATTERNS: CredentialPattern[];
|
|
21
|
+
export declare const GENERIC_SECRET_PATTERNS: CredentialPattern[];
|
|
22
|
+
export declare const PRIVATE_KEY_PATTERNS: CredentialPattern[];
|
|
23
|
+
export declare const CONNECTION_STRING_PATTERNS: CredentialPattern[];
|
|
24
|
+
export declare const ENV_SECRET_PATTERNS: CredentialPattern[];
|
|
25
|
+
export declare const ALL_CREDENTIAL_PATTERNS: CredentialPattern[];
|
|
26
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/defence/credential-leak/patterns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,KAAK,GACL,aAAa,GACb,mBAAmB,GACnB,YAAY,GACZ,cAAc,CAAC;AAEnB,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAIxE,eAAO,MAAM,gBAAgB,EAAE,iBAAiB,EAgK/C,CAAC;AAIF,eAAO,MAAM,uBAAuB,EAAE,iBAAiB,EAyBtD,CAAC;AAIF,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAgCnD,CAAC;AAIF,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,EAiCzD,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,iBAAiB,EA6BlD,CAAC;AAIF,eAAO,MAAM,uBAAuB,EAAE,iBAAiB,EAMtD,CAAC"}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential Leak Detection — Pattern Definitions
|
|
3
|
+
*
|
|
4
|
+
* Known API key formats, secret patterns, and heuristic matchers
|
|
5
|
+
* for detecting accidentally persisted credentials in AI agent memory.
|
|
6
|
+
*/
|
|
7
|
+
// ── Known API Key Patterns ──
|
|
8
|
+
export const API_KEY_PATTERNS = [
|
|
9
|
+
// OpenAI
|
|
10
|
+
{
|
|
11
|
+
name: 'OpenAI API Key',
|
|
12
|
+
type: 'api_key',
|
|
13
|
+
provider: 'openai',
|
|
14
|
+
regex: /sk-[A-Za-z0-9]{20,}/g,
|
|
15
|
+
severity: 'critical',
|
|
16
|
+
confidence: 0.95,
|
|
17
|
+
minLength: 24,
|
|
18
|
+
},
|
|
19
|
+
// Anthropic
|
|
20
|
+
{
|
|
21
|
+
name: 'Anthropic API Key',
|
|
22
|
+
type: 'api_key',
|
|
23
|
+
provider: 'anthropic',
|
|
24
|
+
regex: /sk-ant-[A-Za-z0-9\-_]{20,}/g,
|
|
25
|
+
severity: 'critical',
|
|
26
|
+
confidence: 0.98,
|
|
27
|
+
},
|
|
28
|
+
// AWS Access Key
|
|
29
|
+
{
|
|
30
|
+
name: 'AWS Access Key ID',
|
|
31
|
+
type: 'api_key',
|
|
32
|
+
provider: 'aws',
|
|
33
|
+
regex: /AKIA[0-9A-Z]{16}/g,
|
|
34
|
+
severity: 'critical',
|
|
35
|
+
confidence: 0.97,
|
|
36
|
+
},
|
|
37
|
+
// AWS Secret Key (typically base64-like, 40 chars)
|
|
38
|
+
{
|
|
39
|
+
name: 'AWS Secret Access Key',
|
|
40
|
+
type: 'api_key',
|
|
41
|
+
provider: 'aws',
|
|
42
|
+
regex: /(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY|SecretAccessKey)\s*[=:]\s*["']?([A-Za-z0-9/+=]{40})["']?/g,
|
|
43
|
+
severity: 'critical',
|
|
44
|
+
confidence: 0.95,
|
|
45
|
+
},
|
|
46
|
+
// GitHub tokens
|
|
47
|
+
{
|
|
48
|
+
name: 'GitHub Personal Access Token',
|
|
49
|
+
type: 'api_key',
|
|
50
|
+
provider: 'github',
|
|
51
|
+
regex: /ghp_[A-Za-z0-9]{36,}/g,
|
|
52
|
+
severity: 'critical',
|
|
53
|
+
confidence: 0.98,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'GitHub OAuth Token',
|
|
57
|
+
type: 'api_key',
|
|
58
|
+
provider: 'github',
|
|
59
|
+
regex: /gho_[A-Za-z0-9]{36,}/g,
|
|
60
|
+
severity: 'critical',
|
|
61
|
+
confidence: 0.98,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'GitHub Fine-grained PAT',
|
|
65
|
+
type: 'api_key',
|
|
66
|
+
provider: 'github',
|
|
67
|
+
regex: /github_pat_[A-Za-z0-9_]{22,}/g,
|
|
68
|
+
severity: 'critical',
|
|
69
|
+
confidence: 0.98,
|
|
70
|
+
},
|
|
71
|
+
// Stripe
|
|
72
|
+
{
|
|
73
|
+
name: 'Stripe Live Key',
|
|
74
|
+
type: 'api_key',
|
|
75
|
+
provider: 'stripe',
|
|
76
|
+
regex: /sk_live_[A-Za-z0-9]{24,}/g,
|
|
77
|
+
severity: 'critical',
|
|
78
|
+
confidence: 0.98,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'Stripe Test Key',
|
|
82
|
+
type: 'api_key',
|
|
83
|
+
provider: 'stripe',
|
|
84
|
+
regex: /sk_test_[A-Za-z0-9]{24,}/g,
|
|
85
|
+
severity: 'medium',
|
|
86
|
+
confidence: 0.95,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Stripe Publishable Key',
|
|
90
|
+
type: 'api_key',
|
|
91
|
+
provider: 'stripe',
|
|
92
|
+
regex: /pk_(?:live|test)_[A-Za-z0-9]{24,}/g,
|
|
93
|
+
severity: 'medium',
|
|
94
|
+
confidence: 0.90,
|
|
95
|
+
},
|
|
96
|
+
// Twilio
|
|
97
|
+
{
|
|
98
|
+
name: 'Twilio API Key',
|
|
99
|
+
type: 'api_key',
|
|
100
|
+
provider: 'twilio',
|
|
101
|
+
regex: /SK[a-f0-9]{32}/g,
|
|
102
|
+
severity: 'critical',
|
|
103
|
+
confidence: 0.90,
|
|
104
|
+
},
|
|
105
|
+
// SendGrid
|
|
106
|
+
{
|
|
107
|
+
name: 'SendGrid API Key',
|
|
108
|
+
type: 'api_key',
|
|
109
|
+
provider: 'sendgrid',
|
|
110
|
+
regex: /SG\.[A-Za-z0-9\-_]{22,}\.[A-Za-z0-9\-_]{22,}/g,
|
|
111
|
+
severity: 'critical',
|
|
112
|
+
confidence: 0.97,
|
|
113
|
+
},
|
|
114
|
+
// Slack
|
|
115
|
+
{
|
|
116
|
+
name: 'Slack Bot Token',
|
|
117
|
+
type: 'api_key',
|
|
118
|
+
provider: 'slack',
|
|
119
|
+
regex: /xoxb-[0-9]{10,}-[0-9A-Za-z\-]{20,}/g,
|
|
120
|
+
severity: 'critical',
|
|
121
|
+
confidence: 0.96,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'Slack Webhook URL',
|
|
125
|
+
type: 'api_key',
|
|
126
|
+
provider: 'slack',
|
|
127
|
+
regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Z0-9]{8,}\/B[A-Z0-9]{8,}\/[A-Za-z0-9]{20,}/g,
|
|
128
|
+
severity: 'high',
|
|
129
|
+
confidence: 0.95,
|
|
130
|
+
},
|
|
131
|
+
// Google
|
|
132
|
+
{
|
|
133
|
+
name: 'Google API Key',
|
|
134
|
+
type: 'api_key',
|
|
135
|
+
provider: 'google',
|
|
136
|
+
regex: /AIza[A-Za-z0-9\-_]{35}/g,
|
|
137
|
+
severity: 'critical',
|
|
138
|
+
confidence: 0.95,
|
|
139
|
+
},
|
|
140
|
+
// Mailgun
|
|
141
|
+
{
|
|
142
|
+
name: 'Mailgun API Key',
|
|
143
|
+
type: 'api_key',
|
|
144
|
+
provider: 'mailgun',
|
|
145
|
+
regex: /key-[A-Za-z0-9]{32}/g,
|
|
146
|
+
severity: 'critical',
|
|
147
|
+
confidence: 0.85,
|
|
148
|
+
},
|
|
149
|
+
// npm
|
|
150
|
+
{
|
|
151
|
+
name: 'npm Access Token',
|
|
152
|
+
type: 'api_key',
|
|
153
|
+
provider: 'npm',
|
|
154
|
+
regex: /npm_[A-Za-z0-9]{36,}/g,
|
|
155
|
+
severity: 'critical',
|
|
156
|
+
confidence: 0.97,
|
|
157
|
+
},
|
|
158
|
+
// Heroku
|
|
159
|
+
{
|
|
160
|
+
name: 'Heroku API Key',
|
|
161
|
+
type: 'api_key',
|
|
162
|
+
provider: 'heroku',
|
|
163
|
+
regex: /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/g,
|
|
164
|
+
severity: 'low',
|
|
165
|
+
confidence: 0.30,
|
|
166
|
+
// UUIDs are very common — only flagged as low confidence
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
// ── Generic Secret Patterns ──
|
|
170
|
+
export const GENERIC_SECRET_PATTERNS = [
|
|
171
|
+
// JWT tokens
|
|
172
|
+
{
|
|
173
|
+
name: 'JWT Token',
|
|
174
|
+
type: 'jwt',
|
|
175
|
+
regex: /eyJ[A-Za-z0-9\-_]+\.eyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+/g,
|
|
176
|
+
severity: 'high',
|
|
177
|
+
confidence: 0.92,
|
|
178
|
+
},
|
|
179
|
+
// Bearer tokens in headers
|
|
180
|
+
{
|
|
181
|
+
name: 'Bearer Token',
|
|
182
|
+
type: 'api_key',
|
|
183
|
+
regex: /(?:Authorization|authorization)\s*:\s*Bearer\s+([A-Za-z0-9\-_./+=]{20,})/g,
|
|
184
|
+
severity: 'high',
|
|
185
|
+
confidence: 0.90,
|
|
186
|
+
},
|
|
187
|
+
// Basic auth headers
|
|
188
|
+
{
|
|
189
|
+
name: 'Basic Auth Header',
|
|
190
|
+
type: 'api_key',
|
|
191
|
+
regex: /(?:Authorization|authorization)\s*:\s*Basic\s+([A-Za-z0-9+/=]{8,})/g,
|
|
192
|
+
severity: 'high',
|
|
193
|
+
confidence: 0.88,
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
// ── Private Key Patterns ──
|
|
197
|
+
export const PRIVATE_KEY_PATTERNS = [
|
|
198
|
+
{
|
|
199
|
+
name: 'RSA Private Key',
|
|
200
|
+
type: 'private_key',
|
|
201
|
+
provider: 'rsa',
|
|
202
|
+
regex: /-----BEGIN\s+RSA\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+RSA\s+PRIVATE\s+KEY-----/g,
|
|
203
|
+
severity: 'critical',
|
|
204
|
+
confidence: 0.99,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'EC Private Key',
|
|
208
|
+
type: 'private_key',
|
|
209
|
+
provider: 'ec',
|
|
210
|
+
regex: /-----BEGIN\s+EC\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+EC\s+PRIVATE\s+KEY-----/g,
|
|
211
|
+
severity: 'critical',
|
|
212
|
+
confidence: 0.99,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
name: 'Generic Private Key',
|
|
216
|
+
type: 'private_key',
|
|
217
|
+
regex: /-----BEGIN\s+(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(?:ENCRYPTED\s+)?PRIVATE\s+KEY-----/g,
|
|
218
|
+
severity: 'critical',
|
|
219
|
+
confidence: 0.99,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'SSH Private Key',
|
|
223
|
+
type: 'private_key',
|
|
224
|
+
provider: 'ssh',
|
|
225
|
+
regex: /-----BEGIN\s+OPENSSH\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+OPENSSH\s+PRIVATE\s+KEY-----/g,
|
|
226
|
+
severity: 'critical',
|
|
227
|
+
confidence: 0.99,
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
// ── Connection String Patterns ──
|
|
231
|
+
export const CONNECTION_STRING_PATTERNS = [
|
|
232
|
+
{
|
|
233
|
+
name: 'PostgreSQL Connection String',
|
|
234
|
+
type: 'connection_string',
|
|
235
|
+
provider: 'postgres',
|
|
236
|
+
regex: /postgres(?:ql)?:\/\/[^\s"'`]+:[^\s"'`@]+@[^\s"'`]+/g,
|
|
237
|
+
severity: 'critical',
|
|
238
|
+
confidence: 0.95,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'MySQL Connection String',
|
|
242
|
+
type: 'connection_string',
|
|
243
|
+
provider: 'mysql',
|
|
244
|
+
regex: /mysql:\/\/[^\s"'`]+:[^\s"'`@]+@[^\s"'`]+/g,
|
|
245
|
+
severity: 'critical',
|
|
246
|
+
confidence: 0.95,
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'MongoDB Connection String',
|
|
250
|
+
type: 'connection_string',
|
|
251
|
+
provider: 'mongodb',
|
|
252
|
+
regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+:[^\s"'`@]+@[^\s"'`]+/g,
|
|
253
|
+
severity: 'critical',
|
|
254
|
+
confidence: 0.95,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'Redis Connection String',
|
|
258
|
+
type: 'connection_string',
|
|
259
|
+
provider: 'redis',
|
|
260
|
+
regex: /redis(?:s)?:\/\/[^\s"'`]*:[^\s"'`@]+@[^\s"'`]+/g,
|
|
261
|
+
severity: 'critical',
|
|
262
|
+
confidence: 0.93,
|
|
263
|
+
},
|
|
264
|
+
];
|
|
265
|
+
// ── Environment Variable Patterns ──
|
|
266
|
+
export const ENV_SECRET_PATTERNS = [
|
|
267
|
+
{
|
|
268
|
+
name: 'Password Assignment',
|
|
269
|
+
type: 'env_secret',
|
|
270
|
+
regex: /(?:PASSWORD|PASSWD|DB_PASS|DB_PASSWORD|ADMIN_PASSWORD|ROOT_PASSWORD)\s*[=:]\s*["']?([^\s"']{8,})["']?/gi,
|
|
271
|
+
severity: 'high',
|
|
272
|
+
confidence: 0.85,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'Secret Assignment',
|
|
276
|
+
type: 'env_secret',
|
|
277
|
+
regex: /(?:SECRET|SECRET_KEY|APP_SECRET|JWT_SECRET|SESSION_SECRET|ENCRYPTION_KEY)\s*[=:]\s*["']?([^\s"']{8,})["']?/gi,
|
|
278
|
+
severity: 'high',
|
|
279
|
+
confidence: 0.85,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'Token Assignment',
|
|
283
|
+
type: 'env_secret',
|
|
284
|
+
regex: /(?:TOKEN|ACCESS_TOKEN|REFRESH_TOKEN|AUTH_TOKEN|API_TOKEN|BEARER_TOKEN)\s*[=:]\s*["']?([^\s"']{8,})["']?/gi,
|
|
285
|
+
severity: 'high',
|
|
286
|
+
confidence: 0.82,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'API Key Assignment',
|
|
290
|
+
type: 'env_secret',
|
|
291
|
+
regex: /(?:API_KEY|APIKEY|API_SECRET)\s*[=:]\s*["']?([^\s"']{8,})["']?/gi,
|
|
292
|
+
severity: 'high',
|
|
293
|
+
confidence: 0.82,
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
// ── All Patterns Combined (in priority order) ──
|
|
297
|
+
export const ALL_CREDENTIAL_PATTERNS = [
|
|
298
|
+
...PRIVATE_KEY_PATTERNS,
|
|
299
|
+
...API_KEY_PATTERNS,
|
|
300
|
+
...CONNECTION_STRING_PATTERNS,
|
|
301
|
+
...GENERIC_SECRET_PATTERNS,
|
|
302
|
+
...ENV_SECRET_PATTERNS,
|
|
303
|
+
];
|
|
304
|
+
//# sourceMappingURL=patterns.js.map
|