@de-otio/repo-aegis-core 0.2.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/dist/age.d.ts +32 -0
- package/dist/age.d.ts.map +1 -0
- package/dist/age.js +98 -0
- package/dist/age.js.map +1 -0
- package/dist/audit-log.d.ts +50 -0
- package/dist/audit-log.d.ts.map +1 -0
- package/dist/audit-log.js +183 -0
- package/dist/audit-log.js.map +1 -0
- package/dist/audit-log.test.d.ts +2 -0
- package/dist/audit-log.test.d.ts.map +1 -0
- package/dist/audit-log.test.js +181 -0
- package/dist/audit-log.test.js.map +1 -0
- package/dist/deny-set.d.ts +43 -0
- package/dist/deny-set.d.ts.map +1 -0
- package/dist/deny-set.js +165 -0
- package/dist/deny-set.js.map +1 -0
- package/dist/deny-set.test.d.ts +2 -0
- package/dist/deny-set.test.d.ts.map +1 -0
- package/dist/deny-set.test.js +155 -0
- package/dist/deny-set.test.js.map +1 -0
- package/dist/exceptions.d.ts +96 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +143 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/exit-codes.d.ts +4 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +6 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/first-touch.d.ts +57 -0
- package/dist/first-touch.d.ts.map +1 -0
- package/dist/first-touch.js +112 -0
- package/dist/first-touch.js.map +1 -0
- package/dist/import-graph.test.d.ts +2 -0
- package/dist/import-graph.test.d.ts.map +1 -0
- package/dist/import-graph.test.js +210 -0
- package/dist/import-graph.test.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/lock.d.ts +22 -0
- package/dist/lock.d.ts.map +1 -0
- package/dist/lock.js +86 -0
- package/dist/lock.js.map +1 -0
- package/dist/lock.test.d.ts +2 -0
- package/dist/lock.test.d.ts.map +1 -0
- package/dist/lock.test.js +125 -0
- package/dist/lock.test.js.map +1 -0
- package/dist/paths.d.ts +22 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +46 -0
- package/dist/paths.js.map +1 -0
- package/dist/paths.test.d.ts +2 -0
- package/dist/paths.test.d.ts.map +1 -0
- package/dist/paths.test.js +78 -0
- package/dist/paths.test.js.map +1 -0
- package/dist/redaction.d.ts +29 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +48 -0
- package/dist/redaction.js.map +1 -0
- package/dist/redaction.test.d.ts +2 -0
- package/dist/redaction.test.d.ts.map +1 -0
- package/dist/redaction.test.js +67 -0
- package/dist/redaction.test.js.map +1 -0
- package/dist/regex-safety.d.ts +87 -0
- package/dist/regex-safety.d.ts.map +1 -0
- package/dist/regex-safety.js +322 -0
- package/dist/regex-safety.js.map +1 -0
- package/dist/regex-safety.test.d.ts +2 -0
- package/dist/regex-safety.test.d.ts.map +1 -0
- package/dist/regex-safety.test.js +149 -0
- package/dist/regex-safety.test.js.map +1 -0
- package/dist/registry-mutate.d.ts +35 -0
- package/dist/registry-mutate.d.ts.map +1 -0
- package/dist/registry-mutate.js +149 -0
- package/dist/registry-mutate.js.map +1 -0
- package/dist/registry-mutate.test.d.ts +2 -0
- package/dist/registry-mutate.test.d.ts.map +1 -0
- package/dist/registry-mutate.test.js +96 -0
- package/dist/registry-mutate.test.js.map +1 -0
- package/dist/registry.d.ts +64 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +120 -0
- package/dist/registry.js.map +1 -0
- package/dist/registry.test.d.ts +2 -0
- package/dist/registry.test.d.ts.map +1 -0
- package/dist/registry.test.js +316 -0
- package/dist/registry.test.js.map +1 -0
- package/dist/remote-url.d.ts +18 -0
- package/dist/remote-url.d.ts.map +1 -0
- package/dist/remote-url.js +66 -0
- package/dist/remote-url.js.map +1 -0
- package/dist/remote-url.test.d.ts +2 -0
- package/dist/remote-url.test.d.ts.map +1 -0
- package/dist/remote-url.test.js +116 -0
- package/dist/remote-url.test.js.map +1 -0
- package/dist/render.d.ts +54 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +182 -0
- package/dist/render.js.map +1 -0
- package/dist/render.test.d.ts +2 -0
- package/dist/render.test.d.ts.map +1 -0
- package/dist/render.test.js +152 -0
- package/dist/render.test.js.map +1 -0
- package/dist/repo.d.ts +40 -0
- package/dist/repo.d.ts.map +1 -0
- package/dist/repo.js +214 -0
- package/dist/repo.js.map +1 -0
- package/dist/repo.test.d.ts +2 -0
- package/dist/repo.test.d.ts.map +1 -0
- package/dist/repo.test.js +234 -0
- package/dist/repo.test.js.map +1 -0
- package/dist/scan.d.ts +103 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +436 -0
- package/dist/scan.js.map +1 -0
- package/dist/scan.test.d.ts +2 -0
- package/dist/scan.test.d.ts.map +1 -0
- package/dist/scan.test.js +437 -0
- package/dist/scan.test.js.map +1 -0
- package/dist/schemas.d.ts +50 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +190 -0
- package/dist/schemas.js.map +1 -0
- package/dist/secret-markers.d.ts +34 -0
- package/dist/secret-markers.d.ts.map +1 -0
- package/dist/secret-markers.js +118 -0
- package/dist/secret-markers.js.map +1 -0
- package/dist/secret-markers.test.d.ts +2 -0
- package/dist/secret-markers.test.d.ts.map +1 -0
- package/dist/secret-markers.test.js +154 -0
- package/dist/secret-markers.test.js.map +1 -0
- package/dist/trust-boundary.d.ts +33 -0
- package/dist/trust-boundary.d.ts.map +1 -0
- package/dist/trust-boundary.js +77 -0
- package/dist/trust-boundary.js.map +1 -0
- package/dist/trust-boundary.test.d.ts +2 -0
- package/dist/trust-boundary.test.d.ts.map +1 -0
- package/dist/trust-boundary.test.js +170 -0
- package/dist/trust-boundary.test.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/working-tree.d.ts +38 -0
- package/dist/working-tree.d.ts.map +1 -0
- package/dist/working-tree.js +133 -0
- package/dist/working-tree.js.map +1 -0
- package/dist/working-tree.test.d.ts +2 -0
- package/dist/working-tree.test.d.ts.map +1 -0
- package/dist/working-tree.test.js +162 -0
- package/dist/working-tree.test.js.map +1 -0
- package/package.json +40 -0
- package/src/age.ts +113 -0
- package/src/audit-log.test.ts +222 -0
- package/src/audit-log.ts +215 -0
- package/src/deny-set.test.ts +208 -0
- package/src/deny-set.ts +231 -0
- package/src/exceptions.ts +134 -0
- package/src/exit-codes.ts +5 -0
- package/src/first-touch.ts +172 -0
- package/src/import-graph.test.ts +239 -0
- package/src/index.ts +191 -0
- package/src/lock.test.ts +151 -0
- package/src/lock.ts +88 -0
- package/src/paths.test.ts +94 -0
- package/src/paths.ts +55 -0
- package/src/redaction.test.ts +81 -0
- package/src/redaction.ts +49 -0
- package/src/regex-safety.test.ts +194 -0
- package/src/regex-safety.ts +349 -0
- package/src/registry-mutate.test.ts +134 -0
- package/src/registry-mutate.ts +185 -0
- package/src/registry.test.ts +460 -0
- package/src/registry.ts +178 -0
- package/src/remote-url.test.ts +121 -0
- package/src/remote-url.ts +78 -0
- package/src/render.test.ts +206 -0
- package/src/render.ts +215 -0
- package/src/repo.test.ts +275 -0
- package/src/repo.ts +245 -0
- package/src/scan.test.ts +580 -0
- package/src/scan.ts +531 -0
- package/src/schemas.ts +207 -0
- package/src/secret-markers.test.ts +183 -0
- package/src/secret-markers.ts +145 -0
- package/src/trust-boundary.test.ts +198 -0
- package/src/trust-boundary.ts +98 -0
- package/src/types.ts +55 -0
- package/src/working-tree.test.ts +193 -0
- package/src/working-tree.ts +130 -0
package/dist/lock.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.test.d.ts","sourceRoot":"","sources":["../src/lock.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { describe, it, before, after } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { mkdtempSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import lockfile from "proper-lockfile";
|
|
9
|
+
import { withLock, withLockSync } from "./lock.js";
|
|
10
|
+
import { LockTimeoutError } from "./exceptions.js";
|
|
11
|
+
let tmp;
|
|
12
|
+
before(() => {
|
|
13
|
+
tmp = mkdtempSync(join(tmpdir(), "repo-aegis-lock-test-"));
|
|
14
|
+
});
|
|
15
|
+
after(() => {
|
|
16
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
function lockPathFor(name) {
|
|
19
|
+
const p = join(tmp, name);
|
|
20
|
+
writeFileSync(p, "");
|
|
21
|
+
return p;
|
|
22
|
+
}
|
|
23
|
+
describe("withLockSync", () => {
|
|
24
|
+
it("runs fn while holding the lock and returns its result", () => {
|
|
25
|
+
const lp = lockPathFor("happy.lock");
|
|
26
|
+
const result = withLockSync(() => 42, { lockPath: lp });
|
|
27
|
+
assert.equal(result, 42);
|
|
28
|
+
});
|
|
29
|
+
it("releases the lock even when fn throws", () => {
|
|
30
|
+
const lp = lockPathFor("throw.lock");
|
|
31
|
+
assert.throws(() => withLockSync(() => { throw new Error("boom"); }, { lockPath: lp }), /boom/);
|
|
32
|
+
// Should be able to acquire it again immediately.
|
|
33
|
+
const r = withLockSync(() => "ok", { lockPath: lp });
|
|
34
|
+
assert.equal(r, "ok");
|
|
35
|
+
});
|
|
36
|
+
it("creates the lock file if missing", () => {
|
|
37
|
+
const lp = join(tmp, "auto-create.lock");
|
|
38
|
+
assert.equal(existsSync(lp), false);
|
|
39
|
+
withLockSync(() => 1, { lockPath: lp });
|
|
40
|
+
assert.ok(existsSync(lp));
|
|
41
|
+
});
|
|
42
|
+
it("throws LockTimeoutError when another process holds the lock", () => {
|
|
43
|
+
const lp = lockPathFor("contended.lock");
|
|
44
|
+
const release = lockfile.lockSync(lp, { stale: 30_000 });
|
|
45
|
+
try {
|
|
46
|
+
assert.throws(() => withLockSync(() => 1, { lockPath: lp }), LockTimeoutError);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
release();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe("withLock (async)", () => {
|
|
54
|
+
it("runs fn and returns its result", async () => {
|
|
55
|
+
const lp = lockPathFor("async-happy.lock");
|
|
56
|
+
const result = await withLock(() => 99, { lockPath: lp });
|
|
57
|
+
assert.equal(result, 99);
|
|
58
|
+
});
|
|
59
|
+
it("supports async fn", async () => {
|
|
60
|
+
const lp = lockPathFor("async-fn.lock");
|
|
61
|
+
const result = await withLock(async () => {
|
|
62
|
+
await new Promise(r => setTimeout(r, 5));
|
|
63
|
+
return "done";
|
|
64
|
+
}, { lockPath: lp });
|
|
65
|
+
assert.equal(result, "done");
|
|
66
|
+
});
|
|
67
|
+
it("releases the lock when fn rejects", async () => {
|
|
68
|
+
const lp = lockPathFor("async-reject.lock");
|
|
69
|
+
await assert.rejects(withLock(async () => { throw new Error("x"); }, { lockPath: lp }), /x/);
|
|
70
|
+
const r = await withLock(() => "ok", { lockPath: lp });
|
|
71
|
+
assert.equal(r, "ok");
|
|
72
|
+
});
|
|
73
|
+
it("serializes two parallel withLock calls against the same lockPath", async () => {
|
|
74
|
+
// Two concurrent withLock calls on the same path must NOT overlap.
|
|
75
|
+
// We record entry/exit timestamps for each and assert one's interval
|
|
76
|
+
// fully precedes the other.
|
|
77
|
+
const lp = lockPathFor("async-serialize.lock");
|
|
78
|
+
const HOLD_MS = 60;
|
|
79
|
+
const spans = [];
|
|
80
|
+
async function critical(label) {
|
|
81
|
+
return withLock(async () => {
|
|
82
|
+
const start = Date.now();
|
|
83
|
+
await new Promise(r => setTimeout(r, HOLD_MS));
|
|
84
|
+
const end = Date.now();
|
|
85
|
+
const span = { start, end, label };
|
|
86
|
+
spans.push(span);
|
|
87
|
+
return span;
|
|
88
|
+
}, { lockPath: lp, timeoutMs: 5000 });
|
|
89
|
+
}
|
|
90
|
+
const [a, b] = await Promise.all([critical("a"), critical("b")]);
|
|
91
|
+
// Sort by start time and assert non-overlap.
|
|
92
|
+
const sorted = [a, b].sort((x, y) => x.start - y.start);
|
|
93
|
+
assert.ok(sorted[0].end <= sorted[1].start, `expected serialized intervals; got a=[${a.start},${a.end}] b=[${b.start},${b.end}]`);
|
|
94
|
+
assert.equal(spans.length, 2);
|
|
95
|
+
});
|
|
96
|
+
it("throws LockTimeoutError when the path is held by lockfile.lockSync", async () => {
|
|
97
|
+
const lp = lockPathFor("async-contended.lock");
|
|
98
|
+
const release = lockfile.lockSync(lp, { stale: 30_000 });
|
|
99
|
+
try {
|
|
100
|
+
await assert.rejects(withLock(() => 1, { lockPath: lp, timeoutMs: 100 }), LockTimeoutError);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
release();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it("honours timeoutMs (rejects within the timeout window)", async () => {
|
|
107
|
+
const lp = lockPathFor("async-timeout.lock");
|
|
108
|
+
const release = lockfile.lockSync(lp, { stale: 30_000 });
|
|
109
|
+
try {
|
|
110
|
+
const t0 = Date.now();
|
|
111
|
+
await assert.rejects(withLock(() => 1, { lockPath: lp, timeoutMs: 100 }), LockTimeoutError);
|
|
112
|
+
const elapsed = Date.now() - t0;
|
|
113
|
+
// proper-lockfile retry math + jitter: a 100ms timeout typically
|
|
114
|
+
// settles in well under 500ms. Generous upper bound to avoid CI
|
|
115
|
+
// flake on busy runners; the lower bound asserts we waited at all
|
|
116
|
+
// (i.e. that timeoutMs wasn't ignored entirely and rejected
|
|
117
|
+
// immediately at the very first retry).
|
|
118
|
+
assert.ok(elapsed < 1500, `withLock should reject promptly under timeoutMs=100; elapsed=${elapsed}ms`);
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
release();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=lock.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.test.js","sourceRoot":"","sources":["../src/lock.test.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function repoAegisHome(): string;
|
|
2
|
+
export declare function registryPath(home?: string): string;
|
|
3
|
+
export declare function markersDir(home?: string): string;
|
|
4
|
+
export declare function flatMarkersPath(home?: string): string;
|
|
5
|
+
export declare function statePath(home?: string): string;
|
|
6
|
+
export declare function leakContextFlagPath(home?: string): string;
|
|
7
|
+
export declare function lockFilePath(home?: string): string;
|
|
8
|
+
export declare function denySetCachePath(home?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Path of the operator audit log. JSON Lines, append-only, chmod 600.
|
|
11
|
+
* Off by default; opted into via `~/.config/repo-aegis/state/audit-log.json`
|
|
12
|
+
* (`{ "enabled": true }`). See `audit-log.ts` for the writer and
|
|
13
|
+
* `cli/commands/audit-log.ts` for the on/off/show/path subcommands.
|
|
14
|
+
*/
|
|
15
|
+
export declare function auditLogPath(home?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* True if `REPO_AEGIS_HOME` is set in the environment, indicating the user
|
|
18
|
+
* (or a parent process) has overridden the default home. CLI uses this to
|
|
19
|
+
* print a stderr warning that the override is in effect.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isHomeOverridden(): boolean;
|
|
22
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEnE;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEjE;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEtE;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAE1E;AAED,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEnE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
export function repoAegisHome() {
|
|
6
|
+
return process.env["REPO_AEGIS_HOME"] ?? join(homedir(), ".config", "repo-aegis");
|
|
7
|
+
}
|
|
8
|
+
export function registryPath(home = repoAegisHome()) {
|
|
9
|
+
return process.env["REPO_AEGIS_REGISTRY"] ?? join(home, "engagements.yaml");
|
|
10
|
+
}
|
|
11
|
+
export function markersDir(home = repoAegisHome()) {
|
|
12
|
+
return process.env["REPO_AEGIS_MARKERS_DIR"] ?? join(home, "markers");
|
|
13
|
+
}
|
|
14
|
+
export function flatMarkersPath(home = repoAegisHome()) {
|
|
15
|
+
return join(home, "markers.txt");
|
|
16
|
+
}
|
|
17
|
+
export function statePath(home = repoAegisHome()) {
|
|
18
|
+
return join(home, "state");
|
|
19
|
+
}
|
|
20
|
+
export function leakContextFlagPath(home = repoAegisHome()) {
|
|
21
|
+
return join(statePath(home), "leak-context-mode");
|
|
22
|
+
}
|
|
23
|
+
export function lockFilePath(home = repoAegisHome()) {
|
|
24
|
+
return join(statePath(home), ".lock");
|
|
25
|
+
}
|
|
26
|
+
export function denySetCachePath(home = repoAegisHome()) {
|
|
27
|
+
return join(statePath(home), "deny-set.cache.json");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Path of the operator audit log. JSON Lines, append-only, chmod 600.
|
|
31
|
+
* Off by default; opted into via `~/.config/repo-aegis/state/audit-log.json`
|
|
32
|
+
* (`{ "enabled": true }`). See `audit-log.ts` for the writer and
|
|
33
|
+
* `cli/commands/audit-log.ts` for the on/off/show/path subcommands.
|
|
34
|
+
*/
|
|
35
|
+
export function auditLogPath(home = repoAegisHome()) {
|
|
36
|
+
return join(statePath(home), "audit.log");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* True if `REPO_AEGIS_HOME` is set in the environment, indicating the user
|
|
40
|
+
* (or a parent process) has overridden the default home. CLI uses this to
|
|
41
|
+
* print a stderr warning that the override is in effect.
|
|
42
|
+
*/
|
|
43
|
+
export function isHomeOverridden() {
|
|
44
|
+
return process.env["REPO_AEGIS_HOME"] !== undefined;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,qDAAqD;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,aAAa,EAAE;IACzD,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe,aAAa,EAAE;IACvD,OAAO,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,aAAa,EAAE;IAC5D,OAAO,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,aAAa,EAAE;IACtD,OAAO,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe,aAAa,EAAE;IAChE,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,aAAa,EAAE;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe,aAAa,EAAE;IAC7D,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,qBAAqB,CAAC,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe,aAAa,EAAE;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,SAAS,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.test.d.ts","sourceRoot":"","sources":["../src/paths.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { repoAegisHome, registryPath, markersDir, flatMarkersPath, statePath, leakContextFlagPath, lockFilePath, isHomeOverridden, } from "./paths.js";
|
|
6
|
+
let originalHome;
|
|
7
|
+
let originalRegistry;
|
|
8
|
+
let originalMarkersDir;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
originalHome = process.env["REPO_AEGIS_HOME"];
|
|
11
|
+
originalRegistry = process.env["REPO_AEGIS_REGISTRY"];
|
|
12
|
+
originalMarkersDir = process.env["REPO_AEGIS_MARKERS_DIR"];
|
|
13
|
+
delete process.env["REPO_AEGIS_HOME"];
|
|
14
|
+
delete process.env["REPO_AEGIS_REGISTRY"];
|
|
15
|
+
delete process.env["REPO_AEGIS_MARKERS_DIR"];
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
if (originalHome !== undefined)
|
|
19
|
+
process.env["REPO_AEGIS_HOME"] = originalHome;
|
|
20
|
+
else
|
|
21
|
+
delete process.env["REPO_AEGIS_HOME"];
|
|
22
|
+
if (originalRegistry !== undefined)
|
|
23
|
+
process.env["REPO_AEGIS_REGISTRY"] = originalRegistry;
|
|
24
|
+
else
|
|
25
|
+
delete process.env["REPO_AEGIS_REGISTRY"];
|
|
26
|
+
if (originalMarkersDir !== undefined)
|
|
27
|
+
process.env["REPO_AEGIS_MARKERS_DIR"] = originalMarkersDir;
|
|
28
|
+
else
|
|
29
|
+
delete process.env["REPO_AEGIS_MARKERS_DIR"];
|
|
30
|
+
});
|
|
31
|
+
describe("paths", () => {
|
|
32
|
+
it("default home is ~/.config/repo-aegis", () => {
|
|
33
|
+
const h = repoAegisHome();
|
|
34
|
+
assert.match(h, /\.config\/repo-aegis$/);
|
|
35
|
+
});
|
|
36
|
+
it("REPO_AEGIS_HOME env overrides home", () => {
|
|
37
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/custom";
|
|
38
|
+
assert.equal(repoAegisHome(), "/tmp/custom");
|
|
39
|
+
});
|
|
40
|
+
it("REPO_AEGIS_REGISTRY overrides registry path", () => {
|
|
41
|
+
process.env["REPO_AEGIS_REGISTRY"] = "/tmp/custom-registry.yaml";
|
|
42
|
+
assert.equal(registryPath(), "/tmp/custom-registry.yaml");
|
|
43
|
+
});
|
|
44
|
+
it("registryPath defaults to home/engagements.yaml", () => {
|
|
45
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
46
|
+
assert.equal(registryPath(), "/tmp/x/engagements.yaml");
|
|
47
|
+
});
|
|
48
|
+
it("REPO_AEGIS_MARKERS_DIR overrides markers path", () => {
|
|
49
|
+
process.env["REPO_AEGIS_MARKERS_DIR"] = "/tmp/custom-markers";
|
|
50
|
+
assert.equal(markersDir(), "/tmp/custom-markers");
|
|
51
|
+
});
|
|
52
|
+
it("markersDir defaults to home/markers", () => {
|
|
53
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
54
|
+
assert.equal(markersDir(), "/tmp/x/markers");
|
|
55
|
+
});
|
|
56
|
+
it("flatMarkersPath is home/markers.txt", () => {
|
|
57
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
58
|
+
assert.equal(flatMarkersPath(), "/tmp/x/markers.txt");
|
|
59
|
+
});
|
|
60
|
+
it("statePath is home/state", () => {
|
|
61
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
62
|
+
assert.equal(statePath(), "/tmp/x/state");
|
|
63
|
+
});
|
|
64
|
+
it("leakContextFlagPath is home/state/leak-context-mode", () => {
|
|
65
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
66
|
+
assert.equal(leakContextFlagPath(), "/tmp/x/state/leak-context-mode");
|
|
67
|
+
});
|
|
68
|
+
it("lockFilePath is home/state/.lock", () => {
|
|
69
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
70
|
+
assert.equal(lockFilePath(), "/tmp/x/state/.lock");
|
|
71
|
+
});
|
|
72
|
+
it("isHomeOverridden reflects env var presence", () => {
|
|
73
|
+
assert.equal(isHomeOverridden(), false);
|
|
74
|
+
process.env["REPO_AEGIS_HOME"] = "/tmp/x";
|
|
75
|
+
assert.equal(isHomeOverridden(), true);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=paths.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../src/paths.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type RedactionMode = "preview" | "hash" | "position-only";
|
|
2
|
+
/**
|
|
3
|
+
* Redact a matched marker string for safe display.
|
|
4
|
+
*
|
|
5
|
+
* Default mode `preview` returns `<first-3-chars>***<length-N>` for matches
|
|
6
|
+
* of length >= 4, else `[redacted]`. The literal match value never appears
|
|
7
|
+
* in output that flows to AI agent context (PostToolUse hook output, error
|
|
8
|
+
* messages, JSON payloads), because the literal match in recent context is
|
|
9
|
+
* exactly the recency-pressure failure mode the tool exists to prevent.
|
|
10
|
+
*
|
|
11
|
+
* `hash` mode returns the first 8 hex chars of SHA-256 + length, useful
|
|
12
|
+
* when distinct hits should be distinguishable from each other but the
|
|
13
|
+
* value itself remains opaque.
|
|
14
|
+
*
|
|
15
|
+
* `position-only` returns the constant `[redacted]`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function redactMatch(match: string, mode?: RedactionMode): string;
|
|
18
|
+
/**
|
|
19
|
+
* Pass-through; explicit "this is the literal".
|
|
20
|
+
*
|
|
21
|
+
* Should ONLY be called when the user has explicitly opted in via the
|
|
22
|
+
* `--verbose` CLI flag. Hooks must never pass through this function.
|
|
23
|
+
*
|
|
24
|
+
* The previous `REPO_AEGIS_REVEAL_MATCHES` env-var was removed: env
|
|
25
|
+
* vars propagate to subprocess hooks unintentionally and could cause
|
|
26
|
+
* literal markers to flow into AI tool-result context.
|
|
27
|
+
*/
|
|
28
|
+
export declare function revealMatch(match: string): string;
|
|
29
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,eAAe,CAAC;AAEjE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAyB,GAAG,MAAM,CAalF;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
/**
|
|
5
|
+
* Redact a matched marker string for safe display.
|
|
6
|
+
*
|
|
7
|
+
* Default mode `preview` returns `<first-3-chars>***<length-N>` for matches
|
|
8
|
+
* of length >= 4, else `[redacted]`. The literal match value never appears
|
|
9
|
+
* in output that flows to AI agent context (PostToolUse hook output, error
|
|
10
|
+
* messages, JSON payloads), because the literal match in recent context is
|
|
11
|
+
* exactly the recency-pressure failure mode the tool exists to prevent.
|
|
12
|
+
*
|
|
13
|
+
* `hash` mode returns the first 8 hex chars of SHA-256 + length, useful
|
|
14
|
+
* when distinct hits should be distinguishable from each other but the
|
|
15
|
+
* value itself remains opaque.
|
|
16
|
+
*
|
|
17
|
+
* `position-only` returns the constant `[redacted]`.
|
|
18
|
+
*/
|
|
19
|
+
export function redactMatch(match, mode = "preview") {
|
|
20
|
+
// Use code-point iteration to avoid splitting surrogate pairs in the
|
|
21
|
+
// first 3 characters of multi-byte unicode markers.
|
|
22
|
+
const codePoints = Array.from(match);
|
|
23
|
+
if (mode === "position-only")
|
|
24
|
+
return "[redacted]";
|
|
25
|
+
if (mode === "hash") {
|
|
26
|
+
const h = createHash("sha256").update(match).digest("hex").slice(0, 8);
|
|
27
|
+
return `[hash:${h}:${codePoints.length}]`;
|
|
28
|
+
}
|
|
29
|
+
// preview
|
|
30
|
+
if (codePoints.length < 4)
|
|
31
|
+
return "[redacted]";
|
|
32
|
+
const head = codePoints.slice(0, 3).join("");
|
|
33
|
+
return `${head}***${codePoints.length}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Pass-through; explicit "this is the literal".
|
|
37
|
+
*
|
|
38
|
+
* Should ONLY be called when the user has explicitly opted in via the
|
|
39
|
+
* `--verbose` CLI flag. Hooks must never pass through this function.
|
|
40
|
+
*
|
|
41
|
+
* The previous `REPO_AEGIS_REVEAL_MATCHES` env-var was removed: env
|
|
42
|
+
* vars propagate to subprocess hooks unintentionally and could cause
|
|
43
|
+
* literal markers to flow into AI tool-result context.
|
|
44
|
+
*/
|
|
45
|
+
export function revealMatch(match) {
|
|
46
|
+
return match;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,qDAAqD;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,OAAsB,SAAS;IACxE,qEAAqE;IACrE,oDAAoD;IACpD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,eAAe;QAAE,OAAO,YAAY,CAAC;IAClD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACvE,OAAO,SAAS,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;IAC5C,CAAC;IACD,UAAU;IACV,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,YAAY,CAAC;IAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7C,OAAO,GAAG,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.test.d.ts","sourceRoot":"","sources":["../src/redaction.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
// Copyright (C) 2026 Richard Myers and contributors.
|
|
3
|
+
import { describe, it } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import { redactMatch, revealMatch } from "./redaction.js";
|
|
6
|
+
describe("redactMatch", () => {
|
|
7
|
+
it("preview mode: shows first 3 chars + length for long strings", () => {
|
|
8
|
+
assert.equal(redactMatch("acme-corp", "preview"), "acm***9");
|
|
9
|
+
assert.equal(redactMatch("betaco", "preview"), "bet***6");
|
|
10
|
+
});
|
|
11
|
+
it("preview mode: redacts short strings entirely", () => {
|
|
12
|
+
assert.equal(redactMatch("abc", "preview"), "[redacted]");
|
|
13
|
+
assert.equal(redactMatch("ab", "preview"), "[redacted]");
|
|
14
|
+
assert.equal(redactMatch("", "preview"), "[redacted]");
|
|
15
|
+
});
|
|
16
|
+
it("preview mode is the default", () => {
|
|
17
|
+
assert.equal(redactMatch("acme-corp"), "acm***9");
|
|
18
|
+
});
|
|
19
|
+
it("hash mode: returns hex hash + length, deterministic", () => {
|
|
20
|
+
const a = redactMatch("acme-corp", "hash");
|
|
21
|
+
const b = redactMatch("acme-corp", "hash");
|
|
22
|
+
assert.equal(a, b);
|
|
23
|
+
assert.match(a, /^\[hash:[a-f0-9]{8}:9\]$/);
|
|
24
|
+
});
|
|
25
|
+
it("hash mode: distinct inputs produce distinct outputs", () => {
|
|
26
|
+
assert.notEqual(redactMatch("acme-corp", "hash"), redactMatch("betaco", "hash"));
|
|
27
|
+
});
|
|
28
|
+
it("position-only mode: always [redacted]", () => {
|
|
29
|
+
assert.equal(redactMatch("acme-corp", "position-only"), "[redacted]");
|
|
30
|
+
assert.equal(redactMatch("anything", "position-only"), "[redacted]");
|
|
31
|
+
});
|
|
32
|
+
it("never includes the literal in any redaction mode", () => {
|
|
33
|
+
const literal = "veryspecificcustomername";
|
|
34
|
+
const modes = ["preview", "hash", "position-only"];
|
|
35
|
+
for (const m of modes) {
|
|
36
|
+
const out = redactMatch(literal, m);
|
|
37
|
+
assert.ok(!out.includes(literal), `mode ${m} leaked literal`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it("handles unicode without crashing", () => {
|
|
41
|
+
assert.ok(redactMatch("éâô-corp", "preview").length > 0);
|
|
42
|
+
assert.ok(redactMatch("中文corp", "hash").length > 0);
|
|
43
|
+
});
|
|
44
|
+
it("preview mode does not split surrogate pairs", () => {
|
|
45
|
+
// 🔒 is U+1F512, a surrogate pair in UTF-16. .slice(0,3) on the raw
|
|
46
|
+
// string would split the surrogate; code-point iteration must not.
|
|
47
|
+
const s = "🔒🔑🔓abcd";
|
|
48
|
+
const out = redactMatch(s, "preview");
|
|
49
|
+
// Length is 7 code points; preview returns the first 3 code points + ***N
|
|
50
|
+
assert.match(out, /^🔒🔑🔓\*\*\*7$/);
|
|
51
|
+
});
|
|
52
|
+
it("preview mode counts length in code points, not UTF-16 units", () => {
|
|
53
|
+
const s = "🔒🔑🔓"; // 3 code points, 6 UTF-16 units
|
|
54
|
+
assert.equal(redactMatch(s, "preview"), "[redacted]"); // length 3 < 4
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("revealMatch", () => {
|
|
58
|
+
it("returns the literal", () => {
|
|
59
|
+
assert.equal(revealMatch("acme-corp"), "acme-corp");
|
|
60
|
+
});
|
|
61
|
+
it("is the only function that returns the literal", () => {
|
|
62
|
+
// Documentary test: enforces by convention that revealMatch is the
|
|
63
|
+
// single point of literal exposure in the redaction module.
|
|
64
|
+
assert.equal(typeof revealMatch, "function");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=redaction.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.test.js","sourceRoot":"","sources":["../src/redaction.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface PatternValidationResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
reason?: string;
|
|
4
|
+
}
|
|
5
|
+
export type RegexBackend = "re2" | "in-process" | "subprocess";
|
|
6
|
+
/**
|
|
7
|
+
* Test-only override. Forces {@link getRegexBackend} and
|
|
8
|
+
* {@link validatePattern} to use a specific backend regardless of which
|
|
9
|
+
* backends are installed. Pass `null` to clear. Production callers must
|
|
10
|
+
* not use this.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare function setRegexBackendForTesting(backend: RegexBackend | null): void;
|
|
15
|
+
/**
|
|
16
|
+
* Report which regex backend repo-aegis is using for *additional*
|
|
17
|
+
* pattern-safety validation:
|
|
18
|
+
*
|
|
19
|
+
* - `"re2"`: the optional `re2` dependency is installed. Patterns that
|
|
20
|
+
* compile cleanly in re2 are provably safe from catastrophic
|
|
21
|
+
* backtracking (re2's hybrid NFA/DFA evaluator is linear-time by
|
|
22
|
+
* construction). For patterns that re2 can't parse (lookahead /
|
|
23
|
+
* look-behind / backreferences are unsupported in re2), validation
|
|
24
|
+
* falls back to the `"in-process"` time-budget heuristic.
|
|
25
|
+
* - `"in-process"`: re2 is unavailable; the in-process timer fires
|
|
26
|
+
* after the test completes (best-effort, may exceed budget).
|
|
27
|
+
* {@link validatePatterns} `{ strict: true }` upgrades to
|
|
28
|
+
* `"subprocess"` for the duration of that call.
|
|
29
|
+
* - `"subprocess"`: only returned by {@link getRegexBackend} when set
|
|
30
|
+
* via {@link setRegexBackendForTesting}; otherwise an internal
|
|
31
|
+
* detail of {@link validatePatterns}.
|
|
32
|
+
*
|
|
33
|
+
* Note: re2 affects *validation*, not the scanner's regex engine. The
|
|
34
|
+
* scanner still uses Node's native RegExp because the marker patterns
|
|
35
|
+
* may legitimately use lookahead constructs that re2 doesn't support.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getRegexBackend(): RegexBackend;
|
|
38
|
+
/**
|
|
39
|
+
* Validate a single regex pattern for use as a marker.
|
|
40
|
+
*
|
|
41
|
+
* Checks:
|
|
42
|
+
* 1. Compiles as a JavaScript RegExp without throwing.
|
|
43
|
+
* 2. Length <= 2048 chars.
|
|
44
|
+
* 3. Backtracking-bound test against `'a'.repeat(1000)` completes within 100ms.
|
|
45
|
+
* Catastrophic-backtracking patterns (e.g., `(a+)+$`) hang here and are
|
|
46
|
+
* rejected as ReDoS-suspected.
|
|
47
|
+
*
|
|
48
|
+
* Run at `render` time; bad patterns must not reach the hot path of `check`.
|
|
49
|
+
*
|
|
50
|
+
* @internal Prefer {@link validatePatterns} (which can run strict,
|
|
51
|
+
* subprocess-backed validation that is preemptable on catastrophic
|
|
52
|
+
* backtracking). This single-pattern, in-process helper is exposed for
|
|
53
|
+
* intra-repo callers that already pre-validate adversary-trust boundaries
|
|
54
|
+
* but is not part of the supported public API.
|
|
55
|
+
*/
|
|
56
|
+
export declare function validatePattern(pattern: string): PatternValidationResult;
|
|
57
|
+
export interface ValidatePatternsOptions {
|
|
58
|
+
/**
|
|
59
|
+
* If true, run the backtracking-bound test in a subprocess that can be
|
|
60
|
+
* preemptively killed on timeout. Catches catastrophic-backtracking
|
|
61
|
+
* patterns that the in-process timer can only detect after-the-fact.
|
|
62
|
+
* Adds ~50-200ms of process-spawn overhead for the whole batch.
|
|
63
|
+
* Default: false (use the in-process timer).
|
|
64
|
+
*/
|
|
65
|
+
strict?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate a list of patterns. Returns split valid/invalid.
|
|
69
|
+
*
|
|
70
|
+
* With `strict: true`, runs the backtracking-bound check in a subprocess
|
|
71
|
+
* that can be preemptively killed if any pattern hangs the regex engine.
|
|
72
|
+
* Recommended for `render` and other one-time-cost paths; not for the
|
|
73
|
+
* per-scan hot path.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validatePatterns(patterns: string[], opts?: ValidatePatternsOptions): {
|
|
76
|
+
valid: string[];
|
|
77
|
+
invalid: {
|
|
78
|
+
pattern: string;
|
|
79
|
+
reason: string;
|
|
80
|
+
}[];
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Validate that a combined alternation regex is within the size cap.
|
|
84
|
+
* Used by render and the deny-set computation as a safety net.
|
|
85
|
+
*/
|
|
86
|
+
export declare function validateCombinedSize(combined: string): PatternValidationResult;
|
|
87
|
+
//# sourceMappingURL=regex-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex-safety.d.ts","sourceRoot":"","sources":["../src/regex-safety.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;AA+B/D;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAE5E;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAG9C;AAQD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,CA2CxE;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,GAAE,uBAA4B,GACjC;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAYrE;AA+GD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,uBAAuB,CAQ9E"}
|