@vaultcompass/vault-guard-core 1.1.0 → 1.1.2
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,10 +30,15 @@ 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
|
-
// OpenAI key formats. All
|
|
33
|
+
// OpenAI key formats. All current keys embed the T3BlbkFJ watermark (base64 "OpenAI").
|
|
34
34
|
// Specific prefixes are ordered first so they get their own rule title (blast radius differs).
|
|
35
35
|
// The legacy sk- catch-all uses the watermark + token-boundary so it does not shadow the
|
|
36
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.
|
|
37
42
|
['openai-project', { regex: /sk-proj-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
38
43
|
['openai-svcacct', { regex: /sk-svcacct-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
39
44
|
['openai-admin', { regex: /sk-admin-[A-Za-z0-9_-]{20,100}T3BlbkFJ[A-Za-z0-9_-]{20,100}/g, severity: 'critical' }],
|
|
@@ -307,7 +312,8 @@ class SecretScanner {
|
|
|
307
312
|
type,
|
|
308
313
|
value: this.maskValue(fullMatch),
|
|
309
314
|
line,
|
|
310
|
-
column: match.index,
|
|
315
|
+
column: match.index - (lineIndex[line - 1] ?? 0),
|
|
316
|
+
offset: match.index,
|
|
311
317
|
matchLength: fullMatch.length,
|
|
312
318
|
severity,
|
|
313
319
|
});
|
|
@@ -396,15 +402,15 @@ class SecretScanner {
|
|
|
396
402
|
if (matches.length <= 1)
|
|
397
403
|
return matches;
|
|
398
404
|
// Sort by start offset so we can do a linear sweep.
|
|
399
|
-
const sorted = [...matches].sort((a, b) => a.
|
|
405
|
+
const sorted = [...matches].sort((a, b) => a.offset - b.offset || a.line - b.line);
|
|
400
406
|
const kept = [];
|
|
401
407
|
for (const candidate of sorted) {
|
|
402
|
-
const cStart = candidate.
|
|
408
|
+
const cStart = candidate.offset;
|
|
403
409
|
const cEnd = cStart + candidate.matchLength;
|
|
404
410
|
let dominated = false;
|
|
405
411
|
for (let i = kept.length - 1; i >= 0; i--) {
|
|
406
412
|
const existing = kept[i];
|
|
407
|
-
const eStart = existing.
|
|
413
|
+
const eStart = existing.offset;
|
|
408
414
|
const eEnd = eStart + existing.matchLength;
|
|
409
415
|
// No possible overlap once we've passed the candidate start by more
|
|
410
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.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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"
|