@descryy/runtime-evidence-store 0.3.0 → 0.4.0
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/LICENSE +6 -0
- package/dist/evidence-store.d.ts +60 -118
- package/dist/evidence-store.d.ts.map +1 -1
- package/dist/evidence-store.js +80 -174
- package/dist/evidence-store.js.map +1 -1
- package/dist/redaction.d.ts +34 -89
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +63 -156
- package/dist/redaction.js.map +1 -1
- package/dist/replay.d.ts +26 -71
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +24 -62
- package/dist/replay.js.map +1 -1
- package/package.json +7 -2
package/dist/redaction.js
CHANGED
|
@@ -1,96 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Redaction (plan §28):
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* different owners in a real system (security vs. privacy), and
|
|
22
|
-
* collapsing them into one pattern would make either impossible to
|
|
23
|
-
* reason about or configure on its own.
|
|
24
|
-
*
|
|
25
|
-
* **Configurable by whom, refused how (the two questions this row asked to
|
|
26
|
-
* have decided rather than defaulted):** by whoever constructs the
|
|
27
|
-
* `EvidenceStore` (`RedactionPolicy`, plumbed through
|
|
28
|
-
* `EvidenceStoreOptions` -- see `evidence-store.ts`), and every filter
|
|
29
|
-
* fails closed, disclosed, not silent: a value the body/PII filter cannot
|
|
30
|
-
* classify at all (not JSON, not text that looks safe to pattern-scan) is
|
|
31
|
-
* withheld wholesale rather than passed through on the assumption that
|
|
32
|
-
* "couldn't parse it" means "couldn't be sensitive." A marker replaces the
|
|
33
|
-
* value in place -- the key survives, so a reader can see *that* something
|
|
34
|
-
* was withheld, never just find it missing (same discipline the original
|
|
35
|
-
* key-based pass already established with `[REDACTED]`).
|
|
36
|
-
*
|
|
37
|
-
* **Not a claim of completeness.** Pattern-based text scanning is
|
|
38
|
-
* inherently partial -- a sensitive string shaped unlike every pattern
|
|
39
|
-
* here passes through unredacted, honestly, not silently promised as
|
|
40
|
-
* covered. What the "fail closed" guarantee actually claims: content this
|
|
41
|
-
* module cannot even classify never leaks by default; content it can
|
|
42
|
-
* classify gets the patterns below applied, not more.
|
|
2
|
+
* Redaction (plan §28): raw runtime evidence must not automatically become
|
|
3
|
+
* unrestricted AI context. Runs at write time, unconditionally — a
|
|
4
|
+
* read-time pass is one forgotten call site away from failing.
|
|
5
|
+
*
|
|
6
|
+
* Extends the key-name-only pass with two capabilities a pure key walk
|
|
7
|
+
* can't reach: body filtering (bodies are JSON/text-*encoded strings*, not
|
|
8
|
+
* parsed objects, so a secret inside one was invisible to key matching —
|
|
9
|
+
* body-bearing keys are now parsed if JSON and recursed into, or
|
|
10
|
+
* pattern-scanned as text), and PII filtering, kept as a separate
|
|
11
|
+
* independently toggleable pattern from credentials.
|
|
12
|
+
*
|
|
13
|
+
* Configurable via `RedactionPolicy` (`EvidenceStoreOptions`). Every filter
|
|
14
|
+
* fails closed: content it can't classify at all is withheld wholesale
|
|
15
|
+
* rather than assumed safe. A marker replaces the value in place — the key
|
|
16
|
+
* survives so a reader can see *that* something was withheld.
|
|
17
|
+
*
|
|
18
|
+
* Not a claim of completeness — pattern-based text scanning is inherently
|
|
19
|
+
* partial. Fail-closed only claims: unclassifiable content never leaks by
|
|
20
|
+
* default; classifiable content gets the patterns below, not more.
|
|
43
21
|
*/
|
|
44
22
|
const SENSITIVE_KEY_PATTERN = /^(authorization|cookie|set-cookie|token|secret|password|passwd|api[-_]?key|access[-_]?token|refresh[-_]?token|client[-_]?secret|private[-_]?key|session(id)?|bearer)$/i;
|
|
45
23
|
const PII_KEY_PATTERN = /^(email|e-?mail(?:[-_]?address)?|phone(?:[-_]?number)?|mobile|ssn|social[-_]?security(?:[-_]?number)?|dob|date[-_]?of[-_]?birth|address|street[-_]?address|first[-_]?name|last[-_]?name|full[-_]?name|credit[-_]?card(?:[-_]?number)?|card[-_]?number|cvv|national[-_]?id|passport(?:[-_]?number)?)$/i;
|
|
46
24
|
/**
|
|
47
|
-
* Free-text-bearing keys: request/response bodies
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* (`backend-observation/collector.ts`), the same "an app can print a
|
|
51
|
-
* secret as plain text, not just carry one as a structured field" problem
|
|
52
|
-
* body filtering exists for, just from a different producer.
|
|
25
|
+
* Free-text-bearing keys: request/response bodies plus `raw`
|
|
26
|
+
* (`LogCollector`'s captured `BACKEND_LOG`/`EXCEPTION`/`STACK_TRACE` text)
|
|
27
|
+
* — an app can print a secret as plain text, not just carry it structured.
|
|
53
28
|
*/
|
|
54
29
|
const BODY_BEARING_KEY_PATTERN = /^(body|post[-_]?data|request[-_]?body|response[-_]?body|raw)$/i;
|
|
55
30
|
/**
|
|
56
|
-
* Keys whose value is a
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* and nothing here caught one. Measured: `node server.mjs --api-key=SEKRET`
|
|
63
|
-
* went through `redact()` untouched, `redactedKeys: []`. `command` is not
|
|
64
|
-
* body-bearing, so it was never scanned; and even body scanning would have
|
|
65
|
-
* missed it, since the text patterns look for emails, card numbers and long
|
|
66
|
-
* tokens, and a short flag value is none of those.
|
|
67
|
-
*
|
|
68
|
-
* A command line needs its own scanner because its secrets are **positional
|
|
69
|
-
* rather than lexical**: what marks `SEKRET` as sensitive is not the value's
|
|
70
|
-
* shape but the *flag in front of it*.
|
|
31
|
+
* Keys whose value is a command line (RT-069: measured
|
|
32
|
+
* `node server.mjs --api-key=SEKRET` passed through untouched,
|
|
33
|
+
* `redactedKeys: []` — `command` isn't body-bearing so it was never
|
|
34
|
+
* scanned, and text patterns look for emails/cards/long tokens, not short
|
|
35
|
+
* flag values). Needs its own scanner because secrets here are
|
|
36
|
+
* **positional**: the flag in front marks the value, not its shape.
|
|
71
37
|
*/
|
|
72
38
|
const COMMAND_BEARING_KEY_PATTERN = /^(command|args|argv|cmd)$/i;
|
|
73
39
|
/**
|
|
74
|
-
* A flag
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* Deliberately name-driven and deliberately narrow. The alternative --
|
|
79
|
-
* redacting anything that looks high-entropy -- destroys ordinary arguments
|
|
80
|
-
* (a commit sha, a port, a base64 fixture path) and would make a redacted
|
|
81
|
-
* command unreadable, which costs the reader the thing a command line is
|
|
82
|
-
* kept for.
|
|
40
|
+
* A flag/env assignment whose NAME says the value is a credential:
|
|
41
|
+
* `--api-key=x`, `DATABASE_PASSWORD=x`. Deliberately narrow — redacting
|
|
42
|
+
* anything high-entropy would destroy ordinary arguments (a sha, a port)
|
|
43
|
+
* and make the command unreadable.
|
|
83
44
|
*/
|
|
84
45
|
const SENSITIVE_ARG_NAME = /(?:key|token|secret|password|passwd|auth|credential|bearer|cookie)/i;
|
|
85
46
|
/**
|
|
86
|
-
* Names
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* is a false positive that costs a reader a real path and protects nothing --
|
|
91
|
-
* the secret is the file's *contents*, which never appear on the command
|
|
92
|
-
* line. Excluded so the scanner keeps the information it has no reason to
|
|
93
|
-
* destroy.
|
|
47
|
+
* Names with a sensitive word but a **location** value: `--keys-dir`,
|
|
48
|
+
* `--token-path`. Measured false positive: `--keys-dir keys/dev.json` was
|
|
49
|
+
* redacted for no protection (the secret is the file's contents, not the
|
|
50
|
+
* path). Excluded.
|
|
94
51
|
*/
|
|
95
52
|
const LOCATION_ARG_NAME = /(?:[-_](?:dir|directory|path|file|store|folder)|file|path)$/i;
|
|
96
53
|
const ARG_ASSIGNMENT = /^(-{0,2}[A-Za-z0-9_.-]*?)([=:])(.+)$/;
|
|
@@ -100,39 +57,19 @@ const REDACTED_BODY_MARKER = "[REDACTED-BODY]";
|
|
|
100
57
|
// inside what would also match the generic token pattern gets the more
|
|
101
58
|
// specific marker.
|
|
102
59
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* | input | old | new |
|
|
113
|
-
* | --- | --- | --- |
|
|
114
|
-
* | 16 KB | 98 ms | 2.8 ms |
|
|
115
|
-
* | 64 KB | 2,073 ms | 10.7 ms |
|
|
116
|
-
* | 2 MB | ~35 min (extrapolated) | 218 ms |
|
|
117
|
-
*
|
|
118
|
-
* **This was a denial of service on our own collection path, reachable by
|
|
119
|
-
* ordinary application output.** Any backend log line of a few tens of
|
|
120
|
-
* kilobytes without an `@` — a serialised object, a stack dump, a base64
|
|
121
|
-
* blob — would wedge `redact()`, and `redact()` sits on the only write path
|
|
122
|
-
* into the store. Found by a §19 "large payload" test that hung instead of
|
|
123
|
-
* failing, which is the one symptom easy to mistake for a slow test.
|
|
124
|
-
*
|
|
125
|
-
* The bounds are RFC-shaped rather than invented: 64 characters for the local
|
|
126
|
-
* part (RFC 5321's limit), 63 per domain label, up to eight labels, 2–24 for
|
|
127
|
-
* the TLD. Verified behaviourally identical to the old pattern on every
|
|
128
|
-
* address form in this file's tests — the change costs no match and removes
|
|
129
|
-
* the quadratic.
|
|
60
|
+
* Every quantifier here is bounded — a fix, not style (RT-069). The old
|
|
61
|
+
* unbounded-`+` form backtracked catastrophically with no `@` in the text
|
|
62
|
+
* (O(n²)): 16KB/98ms → 2.8ms, 64KB/2073ms → 10.7ms, 2MB/~35min → 218ms. A
|
|
63
|
+
* real DoS on the only write path into the store, reachable by ordinary
|
|
64
|
+
* backend log output with no `@` (a stack dump, base64 blob).
|
|
65
|
+
*
|
|
66
|
+
* Bounds are RFC-shaped: 64 chars local part (RFC 5321), 63 per label, up
|
|
67
|
+
* to 8 labels, 2-24 for TLD. Verified behaviorally identical to the old
|
|
68
|
+
* pattern in this file's tests.
|
|
130
69
|
*/
|
|
131
70
|
const EMAIL_PATTERN = /[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9-]{1,63}(?:\.[a-zA-Z0-9-]{1,63}){0,8}\.[a-zA-Z]{2,24}/g;
|
|
132
|
-
// Anchored to
|
|
133
|
-
//
|
|
134
|
-
// (caught by a real test: "card 4111 1111 1111 1111 successfully" matched
|
|
135
|
-
// through the space before "successfully").
|
|
71
|
+
// Anchored to end on a digit — (?:\d[ -]?){13,16} over-consumed a trailing
|
|
72
|
+
// space/dash (caught by a test: matched through "successfully" after a card number).
|
|
136
73
|
const CREDIT_CARD_PATTERN = /\b\d(?:[ -]?\d){12,15}\b/g;
|
|
137
74
|
const GENERIC_TOKEN_PATTERN = /\b[A-Za-z0-9_-]{24,}\b/g;
|
|
138
75
|
const TEXT_SCAN_PATTERNS = [EMAIL_PATTERN, CREDIT_CARD_PATTERN, GENERIC_TOKEN_PATTERN];
|
|
@@ -147,14 +84,7 @@ export function redact(payload, policy = {}) {
|
|
|
147
84
|
redactedKeys,
|
|
148
85
|
};
|
|
149
86
|
}
|
|
150
|
-
/**
|
|
151
|
-
* Which keys the default policy would redact, without redacting anything.
|
|
152
|
-
*
|
|
153
|
-
* The tool a collector author needs *before* shipping a payload shape: hand
|
|
154
|
-
* it the key names, get back the ones that collide and would be wiped. The
|
|
155
|
-
* `sessionId` collision (RT-063) was a name nobody thought to check against
|
|
156
|
-
* a pattern list nobody thought to read, and the check costs one call.
|
|
157
|
-
*/
|
|
87
|
+
/** Which keys the default policy would redact, without redacting anything — check a payload shape before shipping it (RT-063's `sessionId` collision cost one call to catch). */
|
|
158
88
|
export function collidingKeys(keys, policy = {}) {
|
|
159
89
|
const resolved = { ...DEFAULT_POLICY, ...policy };
|
|
160
90
|
return keys.filter((key) => SENSITIVE_KEY_PATTERN.test(key) ||
|
|
@@ -203,26 +133,20 @@ function redactValue(value, policy, path, redactedKeys) {
|
|
|
203
133
|
}
|
|
204
134
|
return { value: result, redacted };
|
|
205
135
|
}
|
|
206
|
-
//
|
|
207
|
-
// primitive some future producer emits) has nothing this module can
|
|
208
|
-
// scan -- left as-is, since a bare number or boolean can't itself carry
|
|
209
|
-
// a secret the way an encoded string can.
|
|
136
|
+
// Non-string primitives can't carry a secret the way an encoded string can.
|
|
210
137
|
return { value, redacted: false };
|
|
211
138
|
}
|
|
212
139
|
/** JSON-shaped body content is parsed and recursed into; anything else is scanned as text, or withheld wholesale if it doesn't even look like text. */
|
|
213
140
|
function redactBodyString(text, policy, path, redactedKeys) {
|
|
214
141
|
const parsed = tryParseJson(text);
|
|
215
142
|
if (parsed !== undefined) {
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
// producers, different fixes.
|
|
143
|
+
// Reported under the body's own path so `headers.authorization` stays
|
|
144
|
+
// distinct from `body.authorization` — different producers, different fixes.
|
|
219
145
|
const inner = redactValue(parsed, policy, path, redactedKeys);
|
|
220
146
|
return { value: JSON.stringify(inner.value), redacted: inner.redacted };
|
|
221
147
|
}
|
|
222
148
|
if (!looksLikeText(text)) {
|
|
223
|
-
// Fails closed: content
|
|
224
|
-
// withheld wholesale rather than passed through on the assumption
|
|
225
|
-
// that "couldn't parse it" means "couldn't be sensitive."
|
|
149
|
+
// Fails closed: unclassifiable content is withheld wholesale.
|
|
226
150
|
redactedKeys.push(path);
|
|
227
151
|
return { value: REDACTED_BODY_MARKER, redacted: true };
|
|
228
152
|
}
|
|
@@ -231,9 +155,8 @@ function redactBodyString(text, policy, path, redactedKeys) {
|
|
|
231
155
|
function tryParseJson(text) {
|
|
232
156
|
const trimmed = text.trim();
|
|
233
157
|
if (trimmed.length === 0 || (trimmed[0] !== "{" && trimmed[0] !== "[")) {
|
|
234
|
-
// A bare
|
|
235
|
-
//
|
|
236
|
-
// plain text instead, so the patterns below still get a chance at it.
|
|
158
|
+
// A bare "true"/"42" is valid JSON but carries no nested keys — treated
|
|
159
|
+
// as plain text so the patterns below still get a chance at it.
|
|
237
160
|
return undefined;
|
|
238
161
|
}
|
|
239
162
|
try {
|
|
@@ -270,34 +193,19 @@ function redactPlainText(text) {
|
|
|
270
193
|
}
|
|
271
194
|
/**
|
|
272
195
|
* Redact credential values out of a command line, keeping the shape.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* - **Detached** -- `["--api-key", "SEKRET"]` or `--api-key SEKRET`, where the
|
|
279
|
-
* secret is the *next* token. This is the form a naive per-token scanner
|
|
280
|
-
* misses entirely: `SEKRET` on its own carries nothing that marks it as a
|
|
281
|
-
* secret, and the only thing that does is the token before it.
|
|
282
|
-
*
|
|
283
|
-
* A detached flag at the very end of a command consumes nothing -- there is
|
|
284
|
-
* no value to redact, and inventing one would corrupt the command.
|
|
285
|
-
*
|
|
286
|
-
* The name and separator are kept on purpose. A command redacted to
|
|
287
|
-
* `[REDACTED]` wholesale is useless to the reader it was persisted for; a
|
|
288
|
-
* command redacted to `node server.mjs --api-key=[REDACTED]` still answers
|
|
289
|
-
* "what was run, and with what shape of argument".
|
|
196
|
+
* Two forms: attached (`--api-key=SEKRET`, name/separator survive) and
|
|
197
|
+
* detached (`--api-key SEKRET`, secret is the *next* token — what a naive
|
|
198
|
+
* per-token scanner misses). A detached flag at the end consumes nothing.
|
|
199
|
+
* Name/separator kept so a redacted command still answers "what was run,
|
|
200
|
+
* with what shape of argument", not just `[REDACTED]` wholesale.
|
|
290
201
|
*/
|
|
291
202
|
export function redactCommand(value) {
|
|
292
203
|
if (Array.isArray(value)) {
|
|
293
204
|
const out = redactTokens(value);
|
|
294
205
|
return { value: out.tokens, redacted: out.redacted };
|
|
295
206
|
}
|
|
296
|
-
// Split on whitespace,
|
|
297
|
-
//
|
|
298
|
-
// metacharacters, so a command reaching here is a plain argv-shaped
|
|
299
|
-
// string, and a quoting parser would be machinery for a case this repo
|
|
300
|
-
// deliberately does not accept.
|
|
207
|
+
// Split/rejoin on whitespace, no shell-quoting parser — `tokenizeCommand`
|
|
208
|
+
// already refuses shell metacharacters upstream.
|
|
301
209
|
const tokens = String(value).split(/\s+/);
|
|
302
210
|
const out = redactTokens(tokens);
|
|
303
211
|
return { value: out.tokens.join(" "), redacted: out.redacted };
|
|
@@ -319,9 +227,8 @@ function redactTokens(tokens) {
|
|
|
319
227
|
redacted = true;
|
|
320
228
|
continue;
|
|
321
229
|
}
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
// swallow whatever follows it.
|
|
230
|
+
// Only a flag arms the next token — a bare word like `keys/dev.json`
|
|
231
|
+
// must not swallow whatever follows.
|
|
325
232
|
if (token.startsWith("-") && isSensitiveArgName(token)) {
|
|
326
233
|
result.push(token);
|
|
327
234
|
consumeNext = true;
|
package/dist/redaction.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
|
package/dist/replay.d.ts
CHANGED
|
@@ -1,55 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Replay (plan §24).
|
|
2
|
+
* Replay (plan §24). Replays evidence, not the application: given an
|
|
3
|
+
* `executionId`, returns the recorded execution, its environment, and its
|
|
4
|
+
* evidence in recorded order — enough to re-derive correlation and
|
|
5
|
+
* findings without booting anything. Never spawns a process, drives a
|
|
6
|
+
* browser, or reissues a request — a replay that re-executed would produce
|
|
7
|
+
* a *new* observation while claiming to reproduce an old one.
|
|
3
8
|
*
|
|
4
|
-
*
|
|
9
|
+
* Required a gap closed first: evidence survived a process restart, the
|
|
10
|
+
* execution that produced it did not (`Execution` lived only in
|
|
11
|
+
* `ExecutionController`'s memory). `recordExecution` is the missing half —
|
|
12
|
+
* a new table, never a new `evidence` column (an added column would need a
|
|
13
|
+
* migration this package has no mechanism for).
|
|
5
14
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* an evidence package without booting anything. It does not spawn a process,
|
|
10
|
-
* drive a browser, or reissue a request, and it never will from here: those
|
|
11
|
-
* are `descry-runtime`'s live path, and a "replay" that quietly re-executed
|
|
12
|
-
* would produce a *new* observation while claiming to reproduce an old one.
|
|
15
|
+
* Redaction applies to the execution record too: `configuration.services[x].command`
|
|
16
|
+
* is a command line, a classic credential carrier. Same `redact()`, same
|
|
17
|
+
* terms; `redactedKeys` (RT-063) reports what it caught.
|
|
13
18
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* ## The gap this had to close first
|
|
20
|
-
*
|
|
21
|
-
* Evidence survived a process restart. **The execution that produced it did
|
|
22
|
-
* not.** `Execution` lived in `ExecutionController`'s memory and nowhere on
|
|
23
|
-
* disk, so after a restart the store held a pile of rows keyed by an
|
|
24
|
-
* `executionId` that no longer resolved to an application, a commit, or a
|
|
25
|
-
* configuration. §24 marked "execution configuration — persisted with the
|
|
26
|
-
* execution" as done, and it was true of the object and false of the disk.
|
|
27
|
-
*
|
|
28
|
-
* `recordExecution` is that missing half. It is a **new table**, never a new
|
|
29
|
-
* column on `evidence` — an added table appears under `CREATE TABLE IF NOT
|
|
30
|
-
* EXISTS` on an existing store file, where an added column would need a
|
|
31
|
-
* migration this package has no mechanism for.
|
|
32
|
-
*
|
|
33
|
-
* ## Redaction applies here too, and that is not obvious
|
|
34
|
-
*
|
|
35
|
-
* §20's rows are all about payloads, and every one of them is about evidence.
|
|
36
|
-
* But a service command is `configuration.services[x].command`, and a command
|
|
37
|
-
* line is one of the classic places a credential is passed —
|
|
38
|
-
* `node server.mjs --api-key=…`, `DATABASE_URL=… node app.js`. The execution
|
|
39
|
-
* record goes through the same `redact()` the evidence write path uses, for
|
|
40
|
-
* the same reason and on the same terms. `redactedKeys` (RT-063) reports what
|
|
41
|
-
* it caught.
|
|
42
|
-
*
|
|
43
|
-
* ## The environment fingerprint is deliberately narrow
|
|
44
|
-
*
|
|
45
|
-
* Four fields: node version, platform, arch, and the declared service
|
|
46
|
-
* commands. **`process.env` is not captured, and its absence is the design.**
|
|
47
|
-
* §20 records that the injected process environment is not captured or
|
|
48
|
-
* filtered — *"so there is nothing to leak yet, which is not the same as
|
|
49
|
-
* filtering."* Capturing it here to improve reproducibility would create
|
|
50
|
-
* exactly the leak that row says does not exist, inside the one module whose
|
|
51
|
-
* output is meant to be safe to hand to a model. A narrower fingerprint that
|
|
52
|
-
* can be trusted beats a fuller one that cannot.
|
|
19
|
+
* The environment fingerprint is deliberately narrow — node version,
|
|
20
|
+
* platform, arch, declared service commands. `process.env` is NOT
|
|
21
|
+
* captured; capturing it for reproducibility would create the exact leak
|
|
22
|
+
* this module exists to avoid, inside the one module whose output is meant
|
|
23
|
+
* to be safe to hand to a model.
|
|
53
24
|
*/
|
|
54
25
|
import type { Evidence } from "@descryy/runtime-contracts";
|
|
55
26
|
/** What was true of the machine when an execution was recorded. Narrow on purpose — see this file's header. */
|
|
@@ -58,10 +29,7 @@ export interface EnvironmentFingerprint {
|
|
|
58
29
|
readonly nodeVersion: string;
|
|
59
30
|
readonly platform: string;
|
|
60
31
|
readonly arch: string;
|
|
61
|
-
/**
|
|
62
|
-
* `serviceName -> command`, from the execution's own configuration.
|
|
63
|
-
* Redacted on the way in: a command line is a classic credential carrier.
|
|
64
|
-
*/
|
|
32
|
+
/** `serviceName -> command`, redacted on the way in. */
|
|
65
33
|
readonly serviceCommands: Readonly<Record<string, string>>;
|
|
66
34
|
}
|
|
67
35
|
/** The recorded half of an execution — what `ExecutionController` held only in memory. */
|
|
@@ -86,33 +54,20 @@ export interface Replay {
|
|
|
86
54
|
readonly evidence: readonly Evidence[];
|
|
87
55
|
/** The environment replay is happening *in*, for comparison against the recorded one. */
|
|
88
56
|
readonly currentEnvironment: EnvironmentFingerprint;
|
|
89
|
-
/**
|
|
90
|
-
* Never empty for a degraded replay, and each one names what differs
|
|
91
|
-
* rather than asserting the replay is invalid — whether a node-version
|
|
92
|
-
* change matters is the reader's call, not this module's.
|
|
93
|
-
*/
|
|
57
|
+
/** Never empty for a degraded replay; names what differs rather than asserting invalidity — the reader's call. */
|
|
94
58
|
readonly warnings: readonly ReplayWarning[];
|
|
95
59
|
}
|
|
96
|
-
/**
|
|
97
|
-
* The current machine's fingerprint.
|
|
98
|
-
*
|
|
99
|
-
* `serviceCommands` comes from the caller's configuration rather than being
|
|
100
|
-
* discovered, because there is nothing on a machine to discover it from — an
|
|
101
|
-
* execution's services are a property of the run, not of the host.
|
|
102
|
-
*/
|
|
60
|
+
/** The current machine's fingerprint. `serviceCommands` comes from the caller's configuration, not discovered — services are a property of the run, not the host. */
|
|
103
61
|
export declare function captureEnvironment(serviceCommands?: Readonly<Record<string, string>>, runtime?: {
|
|
104
62
|
version: string;
|
|
105
63
|
platform: string;
|
|
106
64
|
arch: string;
|
|
107
65
|
}): EnvironmentFingerprint;
|
|
108
66
|
/**
|
|
109
|
-
* Differences between the
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* wrong; it is a fact a reader needs when a re-derived finding disagrees with
|
|
114
|
-
* the original. Refusing to replay would be this module deciding a question
|
|
115
|
-
* it has no information about.
|
|
67
|
+
* Differences between the recorded environment and the replay one.
|
|
68
|
+
* Reported, never enforced — a different node version isn't wrong, just a
|
|
69
|
+
* fact a reader needs if a re-derived finding disagrees. Refusing to
|
|
70
|
+
* replay would be this module deciding a question it has no info about.
|
|
116
71
|
*/
|
|
117
72
|
export declare function environmentDifferences(recorded: EnvironmentFingerprint, current: EnvironmentFingerprint): readonly string[];
|
|
118
73
|
//# sourceMappingURL=replay.d.ts.map
|
package/dist/replay.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAE3D,+GAA+G;AAC/G,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5D;AAED,0FAA0F;AAC1F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC7C,mHAAmH;IACnH,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,qBAAqB,GAAG,aAAa,GAAG,sBAAsB,CAAC;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;IACvC,yFAAyF;IACzF,QAAQ,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;IACpD,kHAAkH;IAClH,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CAC7C;AAED,qKAAqK;AACrK,wBAAgB,kBAAkB,CAChC,eAAe,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EACtD,OAAO,GAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAY,GACrE,sBAAsB,CAOxB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,sBAAsB,GAC9B,SAAS,MAAM,EAAE,CAsBnB"}
|
package/dist/replay.js
CHANGED
|
@@ -1,63 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Replay (plan §24).
|
|
2
|
+
* Replay (plan §24). Replays evidence, not the application: given an
|
|
3
|
+
* `executionId`, returns the recorded execution, its environment, and its
|
|
4
|
+
* evidence in recorded order — enough to re-derive correlation and
|
|
5
|
+
* findings without booting anything. Never spawns a process, drives a
|
|
6
|
+
* browser, or reissues a request — a replay that re-executed would produce
|
|
7
|
+
* a *new* observation while claiming to reproduce an old one.
|
|
3
8
|
*
|
|
4
|
-
*
|
|
9
|
+
* Required a gap closed first: evidence survived a process restart, the
|
|
10
|
+
* execution that produced it did not (`Execution` lived only in
|
|
11
|
+
* `ExecutionController`'s memory). `recordExecution` is the missing half —
|
|
12
|
+
* a new table, never a new `evidence` column (an added column would need a
|
|
13
|
+
* migration this package has no mechanism for).
|
|
5
14
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* an evidence package without booting anything. It does not spawn a process,
|
|
10
|
-
* drive a browser, or reissue a request, and it never will from here: those
|
|
11
|
-
* are `descry-runtime`'s live path, and a "replay" that quietly re-executed
|
|
12
|
-
* would produce a *new* observation while claiming to reproduce an old one.
|
|
15
|
+
* Redaction applies to the execution record too: `configuration.services[x].command`
|
|
16
|
+
* is a command line, a classic credential carrier. Same `redact()`, same
|
|
17
|
+
* terms; `redactedKeys` (RT-063) reports what it caught.
|
|
13
18
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* ## The gap this had to close first
|
|
20
|
-
*
|
|
21
|
-
* Evidence survived a process restart. **The execution that produced it did
|
|
22
|
-
* not.** `Execution` lived in `ExecutionController`'s memory and nowhere on
|
|
23
|
-
* disk, so after a restart the store held a pile of rows keyed by an
|
|
24
|
-
* `executionId` that no longer resolved to an application, a commit, or a
|
|
25
|
-
* configuration. §24 marked "execution configuration — persisted with the
|
|
26
|
-
* execution" as done, and it was true of the object and false of the disk.
|
|
27
|
-
*
|
|
28
|
-
* `recordExecution` is that missing half. It is a **new table**, never a new
|
|
29
|
-
* column on `evidence` — an added table appears under `CREATE TABLE IF NOT
|
|
30
|
-
* EXISTS` on an existing store file, where an added column would need a
|
|
31
|
-
* migration this package has no mechanism for.
|
|
32
|
-
*
|
|
33
|
-
* ## Redaction applies here too, and that is not obvious
|
|
34
|
-
*
|
|
35
|
-
* §20's rows are all about payloads, and every one of them is about evidence.
|
|
36
|
-
* But a service command is `configuration.services[x].command`, and a command
|
|
37
|
-
* line is one of the classic places a credential is passed —
|
|
38
|
-
* `node server.mjs --api-key=…`, `DATABASE_URL=… node app.js`. The execution
|
|
39
|
-
* record goes through the same `redact()` the evidence write path uses, for
|
|
40
|
-
* the same reason and on the same terms. `redactedKeys` (RT-063) reports what
|
|
41
|
-
* it caught.
|
|
42
|
-
*
|
|
43
|
-
* ## The environment fingerprint is deliberately narrow
|
|
44
|
-
*
|
|
45
|
-
* Four fields: node version, platform, arch, and the declared service
|
|
46
|
-
* commands. **`process.env` is not captured, and its absence is the design.**
|
|
47
|
-
* §20 records that the injected process environment is not captured or
|
|
48
|
-
* filtered — *"so there is nothing to leak yet, which is not the same as
|
|
49
|
-
* filtering."* Capturing it here to improve reproducibility would create
|
|
50
|
-
* exactly the leak that row says does not exist, inside the one module whose
|
|
51
|
-
* output is meant to be safe to hand to a model. A narrower fingerprint that
|
|
52
|
-
* can be trusted beats a fuller one that cannot.
|
|
53
|
-
*/
|
|
54
|
-
/**
|
|
55
|
-
* The current machine's fingerprint.
|
|
56
|
-
*
|
|
57
|
-
* `serviceCommands` comes from the caller's configuration rather than being
|
|
58
|
-
* discovered, because there is nothing on a machine to discover it from — an
|
|
59
|
-
* execution's services are a property of the run, not of the host.
|
|
19
|
+
* The environment fingerprint is deliberately narrow — node version,
|
|
20
|
+
* platform, arch, declared service commands. `process.env` is NOT
|
|
21
|
+
* captured; capturing it for reproducibility would create the exact leak
|
|
22
|
+
* this module exists to avoid, inside the one module whose output is meant
|
|
23
|
+
* to be safe to hand to a model.
|
|
60
24
|
*/
|
|
25
|
+
/** The current machine's fingerprint. `serviceCommands` comes from the caller's configuration, not discovered — services are a property of the run, not the host. */
|
|
61
26
|
export function captureEnvironment(serviceCommands = {}, runtime = process) {
|
|
62
27
|
return {
|
|
63
28
|
nodeVersion: runtime.version,
|
|
@@ -67,13 +32,10 @@ export function captureEnvironment(serviceCommands = {}, runtime = process) {
|
|
|
67
32
|
};
|
|
68
33
|
}
|
|
69
34
|
/**
|
|
70
|
-
* Differences between the
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* wrong; it is a fact a reader needs when a re-derived finding disagrees with
|
|
75
|
-
* the original. Refusing to replay would be this module deciding a question
|
|
76
|
-
* it has no information about.
|
|
35
|
+
* Differences between the recorded environment and the replay one.
|
|
36
|
+
* Reported, never enforced — a different node version isn't wrong, just a
|
|
37
|
+
* fact a reader needs if a re-derived finding disagrees. Refusing to
|
|
38
|
+
* replay would be this module deciding a question it has no info about.
|
|
77
39
|
*/
|
|
78
40
|
export function environmentDifferences(recorded, current) {
|
|
79
41
|
const differences = [];
|
package/dist/replay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA0CH,qKAAqK;AACrK,MAAM,UAAU,kBAAkB,CAChC,kBAAoD,EAAE,EACtD,UAA+D,OAAO;IAEtE,OAAO;QACL,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,eAAe;KAChB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgC,EAChC,OAA+B;IAE/B,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,QAAQ,CAAC,WAAW,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;QACjD,WAAW,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,WAAW,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC3C,WAAW,CAAC,IAAI,CAAC,YAAY,QAAQ,CAAC,QAAQ,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC;QACnC,WAAW,CAAC,IAAI,CAAC,QAAQ,QAAQ,CAAC,IAAI,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC3G,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,MAAM,KAAK,KAAK;YAAE,SAAS;QAC/B,IAAI,MAAM,KAAK,SAAS;YAAE,WAAW,CAAC,IAAI,CAAC,YAAY,IAAI,UAAU,CAAC,CAAC;aAClE,IAAI,KAAK,KAAK,SAAS;YAAE,WAAW,CAAC,IAAI,CAAC,YAAY,IAAI,WAAW,CAAC,CAAC;;YACvE,WAAW,CAAC,IAAI,CAAC,YAAY,IAAI,sBAAsB,MAAM,MAAM,KAAK,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@descryy/runtime-evidence-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Evidence persistence keyed by executionId, with redaction enforced on write -- there is no path to read an unredacted payload.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/descryhq-wq/descry-runtime.git",
|
|
10
|
+
"directory": "packages/evidence-store"
|
|
11
|
+
},
|
|
7
12
|
"engines": {
|
|
8
13
|
"node": ">=22.5"
|
|
9
14
|
},
|
|
@@ -24,6 +29,6 @@
|
|
|
24
29
|
"build": "tsc -b"
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@descryy/runtime-contracts": "0.
|
|
32
|
+
"@descryy/runtime-contracts": "0.3.0"
|
|
28
33
|
}
|
|
29
34
|
}
|