@redact-secret/core 0.1.0-beta.1 → 0.1.0-beta.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.
- package/README.md +94 -12
- package/dist/adapters/shared.js +17 -3
- package/dist/errors.d.ts +3 -6
- package/dist/errors.js +2 -6
- package/dist/runtime/browser.d.ts +28 -8
- package/dist/runtime/browser.js +34 -10
- package/dist/runtime/node.d.ts +7 -9
- package/dist/runtime/node.js +4 -9
- package/dist/runtime.js +30 -3
- package/dist/types.d.ts +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -119,16 +119,99 @@ names the finding type instead (`<JWT_1>`).
|
|
|
119
119
|
|
|
120
120
|
## Incremental and stream availability
|
|
121
121
|
|
|
122
|
-
The
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
The Node artifact builds a real incremental session, wrapping the same core
|
|
123
|
+
`IncrementalSanitizer` the Python binding does, and the browser (WebAssembly)
|
|
124
|
+
artifact builds the same kind of session over the compiled WebAssembly
|
|
125
|
+
module. `createIncrementalSanitizer` retains unresolved text until its
|
|
126
|
+
detection window closes, then emits sanitized text and findings;
|
|
127
|
+
concatenating every `append`/`finalize` result's text reconstructs the whole
|
|
128
|
+
sanitized output. Findings carry absolute UTF-16 offsets into the logical
|
|
129
|
+
whole-session input, so `input.slice(finding.start, finding.end)` selects the
|
|
130
|
+
matched span, on every runtime. Do not scan chunks independently — a
|
|
131
|
+
credential may cross a chunk boundary.
|
|
126
132
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
```ts
|
|
134
|
+
import { createIncrementalSanitizer, initialize } from "@redact-secret/core";
|
|
135
|
+
|
|
136
|
+
await initialize();
|
|
137
|
+
|
|
138
|
+
const limits = {
|
|
139
|
+
maxInputCodeUnits: 32_768,
|
|
140
|
+
maxBufferedCodeUnits: 16_512,
|
|
141
|
+
maxTokenCodeUnits: 8_192,
|
|
142
|
+
maxMultilineCodeUnits: 16_384,
|
|
143
|
+
};
|
|
144
|
+
const session = createIncrementalSanitizer({ limits });
|
|
145
|
+
|
|
146
|
+
const first = session.append("api_key=SYNTHETIC_REVOKED_");
|
|
147
|
+
const second = session.append("INCREMENTAL_VALUE\nordinary text");
|
|
148
|
+
const final = session.finalize();
|
|
149
|
+
|
|
150
|
+
const safeText = first.text + second.text + final.text;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The four limit fields keep their existing `CodeUnits` names for compatibility,
|
|
154
|
+
but both artifacts enforce their values as UTF-8 byte ceilings in the Rust
|
|
155
|
+
core. Findings and their `start`/`end` ranges still use UTF-16 code units.
|
|
156
|
+
|
|
157
|
+
A session starts `accepting` and moves to the terminal `finalized` (one
|
|
158
|
+
successful `finalize()`), `aborted` (`abort()`), or `failed` (a limit,
|
|
159
|
+
detector, policy, or placeholder failure) state; every operation outside
|
|
160
|
+
`accepting` throws `INVALID_STATE`, and every terminal transition discards
|
|
161
|
+
whatever plaintext the session still retained.
|
|
162
|
+
|
|
163
|
+
`@redact-secret/core/node-stream` wraps a session in a byte-to-byte Node
|
|
164
|
+
`Transform`, finalizing it when the stream ends normally and aborting it on
|
|
165
|
+
every other exit:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { pipeline } from "node:stream/promises";
|
|
169
|
+
|
|
170
|
+
import { initialize } from "@redact-secret/core";
|
|
171
|
+
import { createNodeStreamSanitizer } from "@redact-secret/core/node-stream";
|
|
172
|
+
|
|
173
|
+
await initialize();
|
|
174
|
+
|
|
175
|
+
await pipeline(
|
|
176
|
+
process.stdin,
|
|
177
|
+
createNodeStreamSanitizer({
|
|
178
|
+
limits: {
|
|
179
|
+
maxInputCodeUnits: 32_768,
|
|
180
|
+
maxBufferedCodeUnits: 16_512,
|
|
181
|
+
maxTokenCodeUnits: 8_192,
|
|
182
|
+
maxMultilineCodeUnits: 16_384,
|
|
183
|
+
},
|
|
184
|
+
}),
|
|
185
|
+
process.stdout,
|
|
186
|
+
);
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`@redact-secret/core/web-stream` wraps a session in a byte-to-string Web
|
|
190
|
+
`TransformStream`, finalizing it when the writable side closes normally and
|
|
191
|
+
aborting it on every other exit:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { initialize } from "@redact-secret/core";
|
|
195
|
+
import { createWebStreamSanitizer } from "@redact-secret/core/web-stream";
|
|
196
|
+
|
|
197
|
+
await initialize();
|
|
198
|
+
|
|
199
|
+
declare const source: ReadableStream<Uint8Array>;
|
|
200
|
+
declare const destination: WritableStream<string>;
|
|
201
|
+
|
|
202
|
+
await source
|
|
203
|
+
.pipeThrough(
|
|
204
|
+
createWebStreamSanitizer({
|
|
205
|
+
limits: {
|
|
206
|
+
maxInputCodeUnits: 32_768,
|
|
207
|
+
maxBufferedCodeUnits: 16_512,
|
|
208
|
+
maxTokenCodeUnits: 8_192,
|
|
209
|
+
maxMultilineCodeUnits: 16_384,
|
|
210
|
+
},
|
|
211
|
+
}),
|
|
212
|
+
)
|
|
213
|
+
.pipeTo(destination);
|
|
214
|
+
```
|
|
132
215
|
|
|
133
216
|
See the [streaming guide](https://github.com/redact-secret/redact-secret/blob/main/docs/guides/streaming.md).
|
|
134
217
|
|
|
@@ -159,9 +242,8 @@ try {
|
|
|
159
242
|
string may contain a lone UTF-16 surrogate, which has no UTF-8
|
|
160
243
|
representation, so `scan`, `redact`, `scanAndRedact`, and an incremental
|
|
161
244
|
sanitizer's `append` all reject one with this fixed code before it reaches
|
|
162
|
-
either binding, identically on Node.js and in the browser
|
|
163
|
-
|
|
164
|
-
are mapped to the same fixed error vocabulary.
|
|
245
|
+
either binding, identically on Node.js and in the browser. Core failures are
|
|
246
|
+
mapped to the same fixed error vocabulary.
|
|
165
247
|
|
|
166
248
|
## Public API
|
|
167
249
|
|
package/dist/adapters/shared.js
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { SecretScanError } from "../errors.js";
|
|
19
19
|
export function createStreamSanitizerRuntime(session) {
|
|
20
|
-
const decoder = new TextDecoder("utf-8", {
|
|
20
|
+
const decoder = new TextDecoder("utf-8", {
|
|
21
|
+
fatal: true,
|
|
22
|
+
ignoreBOM: true,
|
|
23
|
+
});
|
|
21
24
|
const findings = [];
|
|
22
25
|
/** Idempotent, and safe after the session has already left `accepting`. */
|
|
23
26
|
function abort() {
|
|
@@ -33,13 +36,23 @@ export function createStreamSanitizerRuntime(session) {
|
|
|
33
36
|
throw new SecretScanError("INVALID_UTF8");
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
function accumulateFindings(next) {
|
|
40
|
+
try {
|
|
41
|
+
for (const finding of next)
|
|
42
|
+
findings.push(finding);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
abort();
|
|
46
|
+
throw new SecretScanError("INVALID_STATE");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
function append(chunk) {
|
|
37
50
|
if (!(chunk instanceof Uint8Array)) {
|
|
38
51
|
abort();
|
|
39
52
|
throw new SecretScanError("INVALID_CHUNK");
|
|
40
53
|
}
|
|
41
54
|
const result = session.append(decode(chunk, true));
|
|
42
|
-
|
|
55
|
+
accumulateFindings(result.findings);
|
|
43
56
|
return result;
|
|
44
57
|
}
|
|
45
58
|
/**
|
|
@@ -61,7 +74,8 @@ export function createStreamSanitizerRuntime(session) {
|
|
|
61
74
|
abort();
|
|
62
75
|
throw error;
|
|
63
76
|
}
|
|
64
|
-
|
|
77
|
+
accumulateFindings(decodedResult.findings);
|
|
78
|
+
accumulateFindings(finalResult.findings);
|
|
65
79
|
return Object.freeze({
|
|
66
80
|
text: decodedResult.text + finalResult.text,
|
|
67
81
|
findings: Object.freeze([
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,11 +6,8 @@
|
|
|
6
6
|
* message (`decision-define-runtime-bindings`). Sixteen codes come from the
|
|
7
7
|
* Rust core; `NOT_INITIALIZED` and `INITIALIZATION_FAILED` are produced by the
|
|
8
8
|
* binding layer, `INVALID_CHUNK` and `INVALID_UTF8` by the stream adapters,
|
|
9
|
-
* `UNPAIRED_SURROGATE` by this package's own runtime-neutral input check
|
|
10
|
-
*
|
|
11
|
-
* incremental sanitization — the browser binding always (`bindings/wasm` has
|
|
12
|
-
* no such export by design) and the Node binding until `bindings/node` gains
|
|
13
|
-
* one; and this package normalizes all of them into the same class so
|
|
9
|
+
* `UNPAIRED_SURROGATE` by this package's own runtime-neutral input check. The
|
|
10
|
+
* package normalizes all of them into the same class so
|
|
14
11
|
* `instanceof SecretScanError` holds on every runtime and every subpath.
|
|
15
12
|
*
|
|
16
13
|
* `UNPAIRED_SURROGATE` resolves `B/F-10`
|
|
@@ -24,7 +21,7 @@
|
|
|
24
21
|
* Node and browser runtimes by construction rather than by parallel
|
|
25
22
|
* implementation.
|
|
26
23
|
*/
|
|
27
|
-
export type SecretScanErrorCode = "INVALID_INPUT" | "INVALID_OPTIONS" | "INVALID_DETECTOR" | "DETECTOR_FAILURE" | "INVALID_CANDIDATE" | "POLICY_FAILURE" | "INVALID_POLICY_ACTION" | "INVALID_FINDINGS" | "PLACEHOLDER_FAILURE" | "INVALID_PLACEHOLDER" | "INVALID_LIMITS" | "INPUT_LIMIT_EXCEEDED" | "BUFFER_LIMIT_EXCEEDED" | "TOKEN_LIMIT_EXCEEDED" | "MULTILINE_LIMIT_EXCEEDED" | "INVALID_STATE" | "NOT_INITIALIZED" | "INITIALIZATION_FAILED" | "INVALID_CHUNK" | "INVALID_UTF8" | "UNPAIRED_SURROGATE"
|
|
24
|
+
export type SecretScanErrorCode = "INVALID_INPUT" | "INVALID_OPTIONS" | "INVALID_DETECTOR" | "DETECTOR_FAILURE" | "INVALID_CANDIDATE" | "POLICY_FAILURE" | "INVALID_POLICY_ACTION" | "INVALID_FINDINGS" | "PLACEHOLDER_FAILURE" | "INVALID_PLACEHOLDER" | "INVALID_LIMITS" | "INPUT_LIMIT_EXCEEDED" | "BUFFER_LIMIT_EXCEEDED" | "TOKEN_LIMIT_EXCEEDED" | "MULTILINE_LIMIT_EXCEEDED" | "INVALID_STATE" | "NOT_INITIALIZED" | "INITIALIZATION_FAILED" | "INVALID_CHUNK" | "INVALID_UTF8" | "UNPAIRED_SURROGATE";
|
|
28
25
|
/** A sanitized failure. It carries nothing but its fixed code and message. */
|
|
29
26
|
export declare class SecretScanError extends Error {
|
|
30
27
|
readonly code: SecretScanErrorCode;
|
package/dist/errors.js
CHANGED
|
@@ -6,11 +6,8 @@
|
|
|
6
6
|
* message (`decision-define-runtime-bindings`). Sixteen codes come from the
|
|
7
7
|
* Rust core; `NOT_INITIALIZED` and `INITIALIZATION_FAILED` are produced by the
|
|
8
8
|
* binding layer, `INVALID_CHUNK` and `INVALID_UTF8` by the stream adapters,
|
|
9
|
-
* `UNPAIRED_SURROGATE` by this package's own runtime-neutral input check
|
|
10
|
-
*
|
|
11
|
-
* incremental sanitization — the browser binding always (`bindings/wasm` has
|
|
12
|
-
* no such export by design) and the Node binding until `bindings/node` gains
|
|
13
|
-
* one; and this package normalizes all of them into the same class so
|
|
9
|
+
* `UNPAIRED_SURROGATE` by this package's own runtime-neutral input check. The
|
|
10
|
+
* package normalizes all of them into the same class so
|
|
14
11
|
* `instanceof SecretScanError` holds on every runtime and every subpath.
|
|
15
12
|
*
|
|
16
13
|
* `UNPAIRED_SURROGATE` resolves `B/F-10`
|
|
@@ -47,7 +44,6 @@ const ERROR_MESSAGES = {
|
|
|
47
44
|
INVALID_CHUNK: "Stream sanitizer input must contain bytes.",
|
|
48
45
|
INVALID_UTF8: "Stream sanitizer input is not valid UTF-8.",
|
|
49
46
|
UNPAIRED_SURROGATE: "Secret scan input contains an unpaired UTF-16 surrogate.",
|
|
50
|
-
INCREMENTAL_UNAVAILABLE: "Incremental sanitization is not available on this runtime.",
|
|
51
47
|
};
|
|
52
48
|
const ERROR_CODES = new Set(Object.keys(ERROR_MESSAGES));
|
|
53
49
|
/** A sanitized failure. It carries nothing but its fixed code and message. */
|
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* binding's own idempotent `initialize()` that builds the detector registry.
|
|
13
13
|
* Wrapping both is exactly this adapter's job.
|
|
14
14
|
*
|
|
15
|
-
* `bindings/wasm`
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* `bindings/wasm` exports a real `createIncrementalSanitizer`
|
|
16
|
+
* (`decision-define-runtime-bindings`): it builds a bounded
|
|
17
|
+
* `IncrementalSanitizer` session, wrapping the same core session
|
|
18
|
+
* `bindings/node` and `bindings/python` wrap, with an explicit
|
|
19
|
+
* `accepting`/`finalized`/`aborted`/`failed` lifecycle and absolute UTF-16
|
|
20
|
+
* ranges converted chunk by chunk. `./web-stream` reaches it through this
|
|
21
|
+
* same binding.
|
|
20
22
|
*/
|
|
21
23
|
import { type NativeBinding } from "../native.js";
|
|
22
24
|
import type { PlaceholderContext, PolicyContext } from "../types.js";
|
|
@@ -58,6 +60,23 @@ export interface WasmFindingMetadata extends WasmDetectedFindingMetadata {
|
|
|
58
60
|
}
|
|
59
61
|
type WasmPolicyCallback = (finding: WasmDetectedFindingMetadata, context: PolicyContext) => string;
|
|
60
62
|
type WasmFormatterCallback = (finding: WasmFindingMetadata, context: PlaceholderContext) => string;
|
|
63
|
+
/** The position information an incremental policy callback receives. */
|
|
64
|
+
export interface WasmIncrementalPolicyContext {
|
|
65
|
+
readonly findingIndex: number;
|
|
66
|
+
}
|
|
67
|
+
type WasmIncrementalPolicyCallback = (finding: WasmDetectedFindingMetadata, context: WasmIncrementalPolicyContext) => string;
|
|
68
|
+
/** The result of one incremental `append`/`finalize` call. */
|
|
69
|
+
export interface WasmIncrementalResult {
|
|
70
|
+
readonly text: string;
|
|
71
|
+
readonly findings: readonly WasmFinding[];
|
|
72
|
+
}
|
|
73
|
+
/** The `IncrementalSanitizer` class `createIncrementalSanitizer` returns. */
|
|
74
|
+
export interface WasmIncrementalSanitizer {
|
|
75
|
+
readonly state: string;
|
|
76
|
+
append(chunk: string): WasmIncrementalResult;
|
|
77
|
+
finalize(): WasmIncrementalResult;
|
|
78
|
+
abort(): void;
|
|
79
|
+
}
|
|
61
80
|
export interface WasmModule {
|
|
62
81
|
default(): Promise<unknown>;
|
|
63
82
|
version(): string;
|
|
@@ -68,15 +87,16 @@ export interface WasmModule {
|
|
|
68
87
|
readonly text: string;
|
|
69
88
|
readonly findings: readonly WasmFinding[];
|
|
70
89
|
};
|
|
90
|
+
createIncrementalSanitizer(maxInputCodeUnits: number, maxBufferedCodeUnits: number, maxTokenCodeUnits: number, maxMultilineCodeUnits: number, policy?: WasmIncrementalPolicyCallback, formatter?: WasmFormatterCallback): WasmIncrementalSanitizer;
|
|
71
91
|
}
|
|
72
92
|
/**
|
|
73
93
|
* Builds the internal binding contract from an already-loaded WebAssembly
|
|
74
94
|
* module.
|
|
75
95
|
*
|
|
76
96
|
* Exported so a test double can exercise this exact normalization — the
|
|
77
|
-
* nested-metadata flattening above and the
|
|
78
|
-
*
|
|
79
|
-
*
|
|
97
|
+
* nested-metadata flattening above and the incremental session wiring below
|
|
98
|
+
* — against a fake module shaped like the real artifact, without loading the
|
|
99
|
+
* artifact itself.
|
|
80
100
|
*/
|
|
81
101
|
export declare function createBindingFromWasmModule(wasm: WasmModule): NativeBinding;
|
|
82
102
|
export declare const loadNativeBinding: () => Promise<NativeBinding>;
|
package/dist/runtime/browser.js
CHANGED
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
* binding's own idempotent `initialize()` that builds the detector registry.
|
|
13
13
|
* Wrapping both is exactly this adapter's job.
|
|
14
14
|
*
|
|
15
|
-
* `bindings/wasm`
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* `bindings/wasm` exports a real `createIncrementalSanitizer`
|
|
16
|
+
* (`decision-define-runtime-bindings`): it builds a bounded
|
|
17
|
+
* `IncrementalSanitizer` session, wrapping the same core session
|
|
18
|
+
* `bindings/node` and `bindings/python` wrap, with an explicit
|
|
19
|
+
* `accepting`/`finalized`/`aborted`/`failed` lifecycle and absolute UTF-16
|
|
20
|
+
* ranges converted chunk by chunk. `./web-stream` reaches it through this
|
|
21
|
+
* same binding.
|
|
20
22
|
*/
|
|
21
23
|
import { SecretScanError } from "../errors.js";
|
|
22
24
|
import { NATIVE_HANDLE, } from "../native.js";
|
|
@@ -84,6 +86,17 @@ function toWasmFormatterCallback(formatter) {
|
|
|
84
86
|
return undefined;
|
|
85
87
|
return (finding, context) => formatter(toNativeFormatterMetadata(finding), context);
|
|
86
88
|
}
|
|
89
|
+
function toWasmIncrementalPolicyCallback(policy) {
|
|
90
|
+
if (policy === undefined)
|
|
91
|
+
return undefined;
|
|
92
|
+
return (finding, context) => policy(toNativeDetectedFinding(finding), context);
|
|
93
|
+
}
|
|
94
|
+
function toNativeIncrementalResult(result) {
|
|
95
|
+
return {
|
|
96
|
+
text: result.text,
|
|
97
|
+
findings: result.findings.map(toNativeFinding),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
87
100
|
/**
|
|
88
101
|
* Loads the WebAssembly artifact published in lockstep with this package.
|
|
89
102
|
*
|
|
@@ -100,6 +113,7 @@ async function loadWasmModule() {
|
|
|
100
113
|
"scan",
|
|
101
114
|
"redact",
|
|
102
115
|
"scanAndRedact",
|
|
116
|
+
"createIncrementalSanitizer",
|
|
103
117
|
]) {
|
|
104
118
|
if (typeof module[name] !== "function") {
|
|
105
119
|
throw new SecretScanError("INITIALIZATION_FAILED");
|
|
@@ -112,9 +126,9 @@ async function loadWasmModule() {
|
|
|
112
126
|
* module.
|
|
113
127
|
*
|
|
114
128
|
* Exported so a test double can exercise this exact normalization — the
|
|
115
|
-
* nested-metadata flattening above and the
|
|
116
|
-
*
|
|
117
|
-
*
|
|
129
|
+
* nested-metadata flattening above and the incremental session wiring below
|
|
130
|
+
* — against a fake module shaped like the real artifact, without loading the
|
|
131
|
+
* artifact itself.
|
|
118
132
|
*/
|
|
119
133
|
export function createBindingFromWasmModule(wasm) {
|
|
120
134
|
return {
|
|
@@ -131,8 +145,18 @@ export function createBindingFromWasmModule(wasm) {
|
|
|
131
145
|
findings: result.findings.map(toNativeFinding),
|
|
132
146
|
};
|
|
133
147
|
},
|
|
134
|
-
createIncrementalSanitizer: () => {
|
|
135
|
-
|
|
148
|
+
createIncrementalSanitizer: (options) => {
|
|
149
|
+
const session = wasm.createIncrementalSanitizer(options.limits.maxInputCodeUnits, options.limits.maxBufferedCodeUnits, options.limits.maxTokenCodeUnits, options.limits.maxMultilineCodeUnits, toWasmIncrementalPolicyCallback(options.policy), toWasmFormatterCallback(options.formatter));
|
|
150
|
+
return {
|
|
151
|
+
get state() {
|
|
152
|
+
return session.state;
|
|
153
|
+
},
|
|
154
|
+
append: (chunk) => toNativeIncrementalResult(session.append(chunk)),
|
|
155
|
+
finalize: () => toNativeIncrementalResult(session.finalize()),
|
|
156
|
+
abort: () => {
|
|
157
|
+
session.abort();
|
|
158
|
+
},
|
|
159
|
+
};
|
|
136
160
|
},
|
|
137
161
|
};
|
|
138
162
|
}
|
package/dist/runtime/node.d.ts
CHANGED
|
@@ -16,11 +16,10 @@ import type { NativeBinding, NativeFinding, NativeFormatterCallback, NativeIncre
|
|
|
16
16
|
* which this adapter renames to the contract's `text`; everything else is
|
|
17
17
|
* already the documented UTF-16 shape.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `INCREMENTAL_UNAVAILABLE` at call time, not a load-time failure.
|
|
19
|
+
* Incremental sanitization is part of the artifact contract. A package and
|
|
20
|
+
* platform addon are released in lockstep, so an addon without that export is
|
|
21
|
+
* an invalid installation and fails initialization with the other missing
|
|
22
|
+
* required exports.
|
|
24
23
|
*/
|
|
25
24
|
interface NodeAddon {
|
|
26
25
|
version(): string;
|
|
@@ -31,7 +30,7 @@ interface NodeAddon {
|
|
|
31
30
|
readonly findings: readonly NativeFinding[];
|
|
32
31
|
readonly redacted: string;
|
|
33
32
|
};
|
|
34
|
-
createIncrementalSanitizer
|
|
33
|
+
createIncrementalSanitizer(options: NativeIncrementalOptions): NativeIncrementalSanitizer;
|
|
35
34
|
}
|
|
36
35
|
/**
|
|
37
36
|
* The addon package this host should have installed, or `undefined` on a
|
|
@@ -48,9 +47,8 @@ export declare function resolveAddonSpecifier(): string | undefined;
|
|
|
48
47
|
/**
|
|
49
48
|
* Builds the internal binding contract from an already-loaded addon.
|
|
50
49
|
*
|
|
51
|
-
* Exported so a test double can exercise this exact normalization
|
|
52
|
-
*
|
|
53
|
-
* real addon, the way `runtime/browser.ts`'s
|
|
50
|
+
* Exported so a test double can exercise this exact normalization without
|
|
51
|
+
* loading the real addon, the way `runtime/browser.ts`'s
|
|
54
52
|
* `createBindingFromWasmModule` does for the WebAssembly artifact.
|
|
55
53
|
*/
|
|
56
54
|
export declare function createBindingFromAddon(addon: NodeAddon): NativeBinding;
|
package/dist/runtime/node.js
CHANGED
|
@@ -84,6 +84,7 @@ function loadAddon() {
|
|
|
84
84
|
"scan",
|
|
85
85
|
"redact",
|
|
86
86
|
"scanAndRedact",
|
|
87
|
+
"createIncrementalSanitizer",
|
|
87
88
|
]) {
|
|
88
89
|
if (typeof addon[name] !== "function") {
|
|
89
90
|
throw new SecretScanError("INITIALIZATION_FAILED");
|
|
@@ -94,9 +95,8 @@ function loadAddon() {
|
|
|
94
95
|
/**
|
|
95
96
|
* Builds the internal binding contract from an already-loaded addon.
|
|
96
97
|
*
|
|
97
|
-
* Exported so a test double can exercise this exact normalization
|
|
98
|
-
*
|
|
99
|
-
* real addon, the way `runtime/browser.ts`'s
|
|
98
|
+
* Exported so a test double can exercise this exact normalization without
|
|
99
|
+
* loading the real addon, the way `runtime/browser.ts`'s
|
|
100
100
|
* `createBindingFromWasmModule` does for the WebAssembly artifact.
|
|
101
101
|
*/
|
|
102
102
|
export function createBindingFromAddon(addon) {
|
|
@@ -111,12 +111,7 @@ export function createBindingFromAddon(addon) {
|
|
|
111
111
|
const result = addon.scanAndRedact(input, policy, formatter);
|
|
112
112
|
return { text: result.redacted, findings: result.findings };
|
|
113
113
|
},
|
|
114
|
-
createIncrementalSanitizer: (options) =>
|
|
115
|
-
if (typeof addon.createIncrementalSanitizer !== "function") {
|
|
116
|
-
throw new SecretScanError("INCREMENTAL_UNAVAILABLE");
|
|
117
|
-
}
|
|
118
|
-
return addon.createIncrementalSanitizer(options);
|
|
119
|
-
},
|
|
114
|
+
createIncrementalSanitizer: (options) => addon.createIncrementalSanitizer(options),
|
|
120
115
|
};
|
|
121
116
|
}
|
|
122
117
|
export const loadNativeBinding = async () => createBindingFromAddon(loadAddon());
|
package/dist/runtime.js
CHANGED
|
@@ -217,6 +217,13 @@ export function createRedactSecretRuntime(loadNativeBinding) {
|
|
|
217
217
|
catch (thrown) {
|
|
218
218
|
throw toSecretScanError(thrown, "INVALID_LIMITS");
|
|
219
219
|
}
|
|
220
|
+
// Host validation cannot reach the core; abort releases its buffer and index.
|
|
221
|
+
let inputFailed = false;
|
|
222
|
+
function requireAccepting() {
|
|
223
|
+
if (inputFailed || session.state !== "accepting") {
|
|
224
|
+
throw new SecretScanError("INVALID_STATE");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
220
227
|
function run(operation, fallback) {
|
|
221
228
|
let result;
|
|
222
229
|
try {
|
|
@@ -232,11 +239,31 @@ export function createRedactSecretRuntime(loadNativeBinding) {
|
|
|
232
239
|
}
|
|
233
240
|
return Object.freeze({
|
|
234
241
|
get state() {
|
|
235
|
-
return session.state;
|
|
242
|
+
return inputFailed ? "failed" : session.state;
|
|
243
|
+
},
|
|
244
|
+
append: (chunk) => {
|
|
245
|
+
requireAccepting();
|
|
246
|
+
let text;
|
|
247
|
+
try {
|
|
248
|
+
text = requireString(chunk);
|
|
249
|
+
}
|
|
250
|
+
catch (thrown) {
|
|
251
|
+
inputFailed = true;
|
|
252
|
+
try {
|
|
253
|
+
session.abort();
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
throw toSecretScanError(thrown, "INVALID_INPUT");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return run(() => session.append(text), "DETECTOR_FAILURE");
|
|
260
|
+
},
|
|
261
|
+
finalize: () => {
|
|
262
|
+
requireAccepting();
|
|
263
|
+
return run(() => session.finalize(), "DETECTOR_FAILURE");
|
|
236
264
|
},
|
|
237
|
-
append: (chunk) => run(() => session.append(requireString(chunk)), "DETECTOR_FAILURE"),
|
|
238
|
-
finalize: () => run(() => session.finalize(), "DETECTOR_FAILURE"),
|
|
239
265
|
abort: () => {
|
|
266
|
+
requireAccepting();
|
|
240
267
|
try {
|
|
241
268
|
session.abort();
|
|
242
269
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -92,9 +92,13 @@ export interface IncrementalSecretPolicy {
|
|
|
92
92
|
* requires. There are no environment-derived or silent defaults.
|
|
93
93
|
*/
|
|
94
94
|
export interface IncrementalLimits {
|
|
95
|
+
/** UTF-8 byte ceiling; the legacy field name is retained for compatibility. */
|
|
95
96
|
readonly maxInputCodeUnits: number;
|
|
97
|
+
/** UTF-8 byte ceiling; the legacy field name is retained for compatibility. */
|
|
96
98
|
readonly maxBufferedCodeUnits: number;
|
|
99
|
+
/** UTF-8 byte ceiling; the legacy field name is retained for compatibility. */
|
|
97
100
|
readonly maxTokenCodeUnits: number;
|
|
101
|
+
/** UTF-8 byte ceiling; the legacy field name is retained for compatibility. */
|
|
98
102
|
readonly maxMultilineCodeUnits: number;
|
|
99
103
|
}
|
|
100
104
|
export interface IncrementalSanitizerOptions extends RedactOptions {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -51,16 +51,16 @@
|
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": "20.x || 22.x || 24.x"
|
|
53
53
|
},
|
|
54
|
-
"version": "0.1.0-beta.
|
|
54
|
+
"version": "0.1.0-beta.2",
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@redact-secret/wasm": "0.1.0-beta.
|
|
56
|
+
"@redact-secret/wasm": "0.1.0-beta.2"
|
|
57
57
|
},
|
|
58
58
|
"optionalDependencies": {
|
|
59
|
-
"@redact-secret/node-darwin-arm64": "0.1.0-beta.
|
|
60
|
-
"@redact-secret/node-darwin-x64": "0.1.0-beta.
|
|
61
|
-
"@redact-secret/node-linux-arm64-gnu": "0.1.0-beta.
|
|
62
|
-
"@redact-secret/node-linux-x64-gnu": "0.1.0-beta.
|
|
63
|
-
"@redact-secret/node-win32-arm64-msvc": "0.1.0-beta.
|
|
64
|
-
"@redact-secret/node-win32-x64-msvc": "0.1.0-beta.
|
|
59
|
+
"@redact-secret/node-darwin-arm64": "0.1.0-beta.2",
|
|
60
|
+
"@redact-secret/node-darwin-x64": "0.1.0-beta.2",
|
|
61
|
+
"@redact-secret/node-linux-arm64-gnu": "0.1.0-beta.2",
|
|
62
|
+
"@redact-secret/node-linux-x64-gnu": "0.1.0-beta.2",
|
|
63
|
+
"@redact-secret/node-win32-arm64-msvc": "0.1.0-beta.2",
|
|
64
|
+
"@redact-secret/node-win32-x64-msvc": "0.1.0-beta.2"
|
|
65
65
|
}
|
|
66
66
|
}
|