@link-foundation/sensitive-data-sanitizer 0.21.4 → 0.21.5
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 +6 -0
- package/docs/API.md +1 -1
- package/package.json +1 -1
- package/src/context.js +21 -2
- package/src/credential-spans.js +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.21.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e61a218: Preserve Authorization and Proxy-Authorization scheme words when overlapping credential heuristics match exactly the scheme before a separate payload. Continue masking payloads, bare credentials, and exact known secrets.
|
|
8
|
+
|
|
3
9
|
## 0.21.4
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/docs/API.md
CHANGED
|
@@ -579,7 +579,7 @@ permission values and literal credential references. `publicKnowledge: false`
|
|
|
579
579
|
restores IP detection; explicit `knownSecrets` and `knownPersonal` retain
|
|
580
580
|
priority. No shell command or workflow expression is executed.
|
|
581
581
|
|
|
582
|
-
Authorization headers (including Proxy-Authorization)
|
|
582
|
+
Authorization headers (including Proxy-Authorization) preserve the scheme and redact the credential payload. Overlapping heuristic findings that cover exactly a scheme followed by a separate nonempty payload are ignored, including Gitleaks generic API-key findings on `SharedAccessSignature`. Bare credentials without a scheme, payload findings and exact `knownSecrets` matches remain subject to redaction. Whitespace, C0 controls (including NUL), DEL, quotes, backslashes and commas end the value, preserving subsequent log text in shell and escaped JSON prompts. Matching brackets opened within a token are included in its redaction; unmatched closing brackets remain part of the surrounding log. Already-redacted tokens and known credential references remain exempt after an optional scheme, including `<token>` placeholders. The debug callback receives value-free `gitleaks-rule` events with rule ID, input length and elapsed milliseconds; tracing is off by default.
|
|
583
583
|
|
|
584
584
|
## Caller-declared sensitive fields
|
|
585
585
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-foundation/sensitive-data-sanitizer",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.5",
|
|
4
4
|
"description": "Offline sensitive-text sanitization with credential, multilingual personal-data and scanner union detection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/context.js
CHANGED
|
@@ -198,8 +198,8 @@ export function authorizationRanges(text, start) {
|
|
|
198
198
|
if (reference) {
|
|
199
199
|
return [{ start, end: start + reference[0].length }];
|
|
200
200
|
}
|
|
201
|
-
const scheme =
|
|
202
|
-
const tokenStart =
|
|
201
|
+
const scheme = authorizationScheme(text, start);
|
|
202
|
+
const tokenStart = scheme?.payloadStart ?? start;
|
|
203
203
|
const params = scheme ? authParams(text, tokenStart) : undefined;
|
|
204
204
|
if (params) {
|
|
205
205
|
return params;
|
|
@@ -208,6 +208,25 @@ export function authorizationRanges(text, start) {
|
|
|
208
208
|
const token = text.slice(tokenStart, end);
|
|
209
209
|
return usableAuthorization(token) ? [{ start: tokenStart, end }] : [];
|
|
210
210
|
}
|
|
211
|
+
|
|
212
|
+
// A scheme is structural only when a separate, nonempty payload follows it.
|
|
213
|
+
// Share this boundary with span refinement so overlapping vendor detections
|
|
214
|
+
// cannot mask the scheme that the Authorization parser deliberately preserves.
|
|
215
|
+
export function authorizationScheme(text, start) {
|
|
216
|
+
const opening = /^(\\*)(["'])/.exec(text.slice(start));
|
|
217
|
+
start += opening?.[0].length ?? 0;
|
|
218
|
+
const scheme = /^([a-z][a-z0-9_-]*)[ \t]+/i.exec(text.slice(start));
|
|
219
|
+
if (!scheme) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
const payloadStart = start + scheme[0].length;
|
|
223
|
+
const char = text[payloadStart];
|
|
224
|
+
return char &&
|
|
225
|
+
!authorizationDelimiter(char, text.charCodeAt(payloadStart)) &&
|
|
226
|
+
!/[}\]>]/.test(char)
|
|
227
|
+
? { start, end: start + scheme[1].length, payloadStart }
|
|
228
|
+
: undefined;
|
|
229
|
+
}
|
|
211
230
|
function authorizationEnd(text, tokenStart) {
|
|
212
231
|
const brackets = [];
|
|
213
232
|
let end = tokenStart;
|
package/src/credential-spans.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { maskedValue } from './credentials.js';
|
|
2
|
+
import { authorizationScheme } from './context.js';
|
|
2
3
|
|
|
3
4
|
// Credential findings cover the secret, not the readable context around it:
|
|
4
5
|
// PEM markers, URL hosts and user names, cookie names and the rest of a
|
|
@@ -159,7 +160,22 @@ function pemMarkers(text, findings) {
|
|
|
159
160
|
|
|
160
161
|
export function refineCredentialSpans(text, findings) {
|
|
161
162
|
const markers = pemMarkers(text, findings);
|
|
163
|
+
const schemes = new Map();
|
|
164
|
+
for (const header of text.matchAll(
|
|
165
|
+
/(?<![\w-])(?:Proxy-)?Authorization(?:\\*["'])?[ \t]*:[ \t]*/gi
|
|
166
|
+
)) {
|
|
167
|
+
const scheme = authorizationScheme(text, header.index + header[0].length);
|
|
168
|
+
if (scheme) {
|
|
169
|
+
schemes.set(scheme.start, scheme.end);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
162
172
|
const narrow = findings
|
|
173
|
+
.filter(
|
|
174
|
+
(finding) =>
|
|
175
|
+
finding.category !== 'credential' ||
|
|
176
|
+
finding.rule === 'known-secret' ||
|
|
177
|
+
schemes.get(finding.start) !== finding.end
|
|
178
|
+
)
|
|
163
179
|
.filter(
|
|
164
180
|
(finding) =>
|
|
165
181
|
finding.category === 'credential' ||
|