@vaultcompass/vault-guard-core 1.0.6 → 1.1.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.
|
@@ -22,6 +22,6 @@ function relativeFingerprintKey(file, cwd) {
|
|
|
22
22
|
*/
|
|
23
23
|
function fingerprintForMatch(cwd, fileAbs, m) {
|
|
24
24
|
const relKey = relativeFingerprintKey(fileAbs, cwd);
|
|
25
|
-
const payload = `${relKey}|${m.type}|${m.line}|${m.
|
|
25
|
+
const payload = `${relKey}|${m.type}|${m.line}|${m.offset}|${m.matchLength}`;
|
|
26
26
|
return (0, crypto_1.createHash)('sha256').update(payload, 'utf8').digest('hex');
|
|
27
27
|
}
|
package/dist/scan-output.d.ts
CHANGED
|
@@ -33,10 +33,13 @@ export interface JsonOutput {
|
|
|
33
33
|
type: string;
|
|
34
34
|
severity: string;
|
|
35
35
|
line: number;
|
|
36
|
+
/** 0-based line-relative column. */
|
|
36
37
|
column: number;
|
|
38
|
+
/** 0-based absolute UTF-16 offset in the scanned content. */
|
|
39
|
+
offset: number;
|
|
37
40
|
/** Redacted form, e.g. `sk-a…(37c)`. Never the raw secret. */
|
|
38
41
|
value: string;
|
|
39
|
-
/** SHA-256 hex of `relPath|type|line|
|
|
42
|
+
/** SHA-256 hex of `relPath|type|line|offset|matchLength` for baselines (no raw secret). */
|
|
40
43
|
fingerprint: string;
|
|
41
44
|
}>;
|
|
42
45
|
}>;
|
package/dist/scan-output.js
CHANGED
|
@@ -30,8 +30,21 @@ const regex_safety_1 = require("../utils/regex-safety");
|
|
|
30
30
|
const BUILTIN_PATTERNS = new Map([
|
|
31
31
|
// --- AI / ML providers ---
|
|
32
32
|
['anthropic', { regex: /sk-ant-[a-zA-Z0-9_-]{20,}/g, severity: 'critical' }],
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// OpenAI key formats. All current keys embed the T3BlbkFJ watermark (base64 "OpenAI").
|
|
34
|
+
// Specific prefixes are ordered first so they get their own rule title (blast radius differs).
|
|
35
|
+
// The legacy sk- catch-all uses the watermark + token-boundary so it does not shadow the
|
|
36
|
+
// prefixed rules and avoids matching short benign identifiers.
|
|
37
|
+
//
|
|
38
|
+
// DELIBERATE: we do NOT match the pre-2023 bare `sk-<48 alphanumerics>` format (no watermark).
|
|
39
|
+
// That pattern fires on any base64/hex blob following `sk-` and floods false positives; the
|
|
40
|
+
// watermark is the only reliable discriminator, matching the gitleaks/trufflehog consensus.
|
|
41
|
+
// Do not re-add a bare `sk-[A-Za-z0-9]{N,}` rule without an entropy gate and a bench FP guard.
|
|
42
|
+
['openai-project', { regex: /sk-proj-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
43
|
+
['openai-svcacct', { regex: /sk-svcacct-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
44
|
+
['openai-admin', { regex: /sk-admin-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
45
|
+
// Legacy user key (sk-<20>T3BlbkFJ<20+>). Token-boundary anchored so benign sk- prefixes
|
|
46
|
+
// in identifiers (e.g. sk-None-short) don't fire without the watermark present.
|
|
47
|
+
['openai', { regex: /(?<![A-Za-z0-9_-])sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20,}/g, severity: 'critical' }],
|
|
35
48
|
['huggingface', { regex: /hf_[a-zA-Z0-9]{34,}/g, severity: 'critical' }],
|
|
36
49
|
['replicate', { regex: /r8_[a-zA-Z0-9]{32}/g, severity: 'critical' }],
|
|
37
50
|
// --- Payment processors ---
|
|
@@ -299,7 +312,8 @@ class SecretScanner {
|
|
|
299
312
|
type,
|
|
300
313
|
value: this.maskValue(fullMatch),
|
|
301
314
|
line,
|
|
302
|
-
column: match.index,
|
|
315
|
+
column: match.index - (lineIndex[line - 1] ?? 0),
|
|
316
|
+
offset: match.index,
|
|
303
317
|
matchLength: fullMatch.length,
|
|
304
318
|
severity,
|
|
305
319
|
});
|
|
@@ -388,15 +402,15 @@ class SecretScanner {
|
|
|
388
402
|
if (matches.length <= 1)
|
|
389
403
|
return matches;
|
|
390
404
|
// Sort by start offset so we can do a linear sweep.
|
|
391
|
-
const sorted = [...matches].sort((a, b) => a.
|
|
405
|
+
const sorted = [...matches].sort((a, b) => a.offset - b.offset || a.line - b.line);
|
|
392
406
|
const kept = [];
|
|
393
407
|
for (const candidate of sorted) {
|
|
394
|
-
const cStart = candidate.
|
|
408
|
+
const cStart = candidate.offset;
|
|
395
409
|
const cEnd = cStart + candidate.matchLength;
|
|
396
410
|
let dominated = false;
|
|
397
411
|
for (let i = kept.length - 1; i >= 0; i--) {
|
|
398
412
|
const existing = kept[i];
|
|
399
|
-
const eStart = existing.
|
|
413
|
+
const eStart = existing.offset;
|
|
400
414
|
const eEnd = eStart + existing.matchLength;
|
|
401
415
|
// No possible overlap once we've passed the candidate start by more
|
|
402
416
|
// than the max pattern length (optimisation — safe upper bound: 512).
|
package/dist/types.d.ts
CHANGED
|
@@ -5,9 +5,11 @@ export interface SecretMatch {
|
|
|
5
5
|
value: string;
|
|
6
6
|
/** 1-based line number in the file. */
|
|
7
7
|
line: number;
|
|
8
|
-
/** 0-based
|
|
8
|
+
/** 0-based line-relative UTF-16 column, for CLI/SARIF/editor display. */
|
|
9
9
|
column: number;
|
|
10
|
-
/**
|
|
10
|
+
/** 0-based absolute UTF-16 offset in the scanned content, for dedupe/baselines. */
|
|
11
|
+
offset: number;
|
|
12
|
+
/** UTF-16 code-unit length of the original match (used for SARIF region output). */
|
|
11
13
|
matchLength: number;
|
|
12
14
|
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
13
15
|
}
|
package/dist/utils/scan-file.js
CHANGED
|
@@ -89,7 +89,7 @@ async function scanTextFileAsync(scanner, filePath, options) {
|
|
|
89
89
|
const found = scanner.scanContent(slice).map(m => ({
|
|
90
90
|
...m,
|
|
91
91
|
line: lineNo,
|
|
92
|
-
|
|
92
|
+
offset: utf16Offset + m.offset,
|
|
93
93
|
}));
|
|
94
94
|
raw.push(...found);
|
|
95
95
|
utf16Offset += line.length + 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaultcompass/vault-guard-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Secret-scanning engine: vendor-anchored patterns, entropy gating, baselines, SARIF/JSON, hook helpers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/jest": "^30.0.0",
|
|
37
|
-
"@types/node": "^25.9.
|
|
37
|
+
"@types/node": "^25.9.3",
|
|
38
38
|
"jest": "^30.4.2",
|
|
39
39
|
"ts-jest": "^29.4.11",
|
|
40
40
|
"typescript": "^5.0.0"
|