@tuent/sentinel 0.1.2 → 0.1.4
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/SECURITY_MODEL.md +9 -0
- package/dist/Sentinel-4QKPFHTI.js +10 -0
- package/dist/{Sentinel-xFCyXH45.d.ts → Sentinel-DT0IyGQi.d.ts} +127 -9
- package/dist/{chunk-PDWWRZXF.js → chunk-2IPSTUNH.js} +18 -7
- package/dist/chunk-B6S2PBS4.js +47 -0
- package/dist/{chunk-QIYQWOLO.js → chunk-FIEIGBYL.js} +387 -242
- package/dist/{chunk-L4R3LPJS.js → chunk-HRI2Y326.js} +119 -35
- package/dist/{chunk-GRN5P3H2.js → chunk-I2FVDDSG.js} +242 -94
- package/dist/{chunk-WLIDSTS4.js → chunk-KWZ7JKKO.js} +221 -27
- package/dist/{chunk-FWIISAZZ.js → chunk-LTBVWF5H.js} +201 -80
- package/dist/chunk-TKAKHSZ3.js +1 -0
- package/dist/cli.js +33 -35
- package/dist/gateway/index.d.ts +20 -1
- package/dist/gateway/index.js +4 -4
- package/dist/gatewayDaemon.js +38 -16
- package/dist/index.d.ts +31 -19
- package/dist/index.js +9 -8
- package/dist/logAdapter-WM43W3S7.js +7 -0
- package/dist/{mcpAdapter-R47GX2P3.js → mcpAdapter-WYAXUE7T.js} +2 -2
- package/dist/{policyLoader-KZL2U4M2.js → policyLoader-XX6BQXNB.js} +8 -4
- package/package.json +1 -1
- package/dist/Sentinel-XMSJE4DZ.js +0 -10
- package/dist/chunk-FMZWHT4M.js +0 -20
- package/dist/logAdapter-IB6ZDEV2.js +0 -7
|
@@ -1,15 +1,62 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_DANGEROUS_SCHEMES,
|
|
3
|
+
DEFAULT_FORBIDDEN_PATTERNS,
|
|
4
|
+
DEFAULT_NETWORK_DENYLIST_CIDRS,
|
|
5
|
+
checkSaneNumber,
|
|
6
|
+
suggestKey
|
|
7
|
+
} from "./chunk-KWZ7JKKO.js";
|
|
8
|
+
|
|
9
|
+
// src/repoSensitivityOverlay.ts
|
|
2
10
|
import * as fs from "fs/promises";
|
|
3
11
|
import * as path from "path";
|
|
4
12
|
import * as os from "os";
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
var VALID_DECISION_TYPES = /* @__PURE__ */ new Set(["reject", "accept", "override"]);
|
|
14
|
+
var OVERLAY_TOP_KEYS = ["version", "repoRoot", "decisions", "updatedAt"];
|
|
15
|
+
var DECISION_KEYS = [
|
|
16
|
+
"path",
|
|
17
|
+
"decision",
|
|
18
|
+
"reason",
|
|
19
|
+
"sensitivity",
|
|
20
|
+
"category",
|
|
21
|
+
"decidedAt"
|
|
22
|
+
];
|
|
23
|
+
function sanitizeSensitivityScore(fieldLabel, value, throwError) {
|
|
24
|
+
const violation = checkSaneNumber(value, {});
|
|
25
|
+
if (violation) {
|
|
26
|
+
throwError(`${fieldLabel} ${violation} \u2014 sensitivity must be a finite number in [0, 1]`);
|
|
27
|
+
}
|
|
28
|
+
const n = value;
|
|
29
|
+
if (n < 0 || n > 1) {
|
|
30
|
+
const clamped = Math.min(1, Math.max(0, n));
|
|
31
|
+
console.warn(
|
|
32
|
+
`[Sentinel] ${fieldLabel} is ${n}, outside [0, 1] \u2014 clamped to ${clamped}. Fix the file to silence this warning.`
|
|
33
|
+
);
|
|
34
|
+
return clamped;
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
function rejectUnknownJsonKeys(section, obj, allowed, throwError) {
|
|
39
|
+
for (const key of Object.keys(obj)) {
|
|
40
|
+
if (!allowed.includes(key)) {
|
|
41
|
+
const suggestion = suggestKey(key, allowed);
|
|
42
|
+
throwError(
|
|
43
|
+
`unknown key "${key}" in ${section}.` + (suggestion ? ` Did you mean "${suggestion}"?` : "") + ` Valid keys: ${allowed.join(", ")}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
var DEFAULT_OVERLAY_PATH = path.join(
|
|
49
|
+
os.homedir(),
|
|
50
|
+
".dahlia",
|
|
51
|
+
"repo-sensitivity.review.json"
|
|
52
|
+
);
|
|
53
|
+
async function saveOverlay(overlay, customPath) {
|
|
54
|
+
const target = customPath ?? DEFAULT_OVERLAY_PATH;
|
|
8
55
|
const parent = path.dirname(target);
|
|
9
56
|
const tempPath = target + ".tmp";
|
|
10
57
|
try {
|
|
11
58
|
await fs.mkdir(parent, { recursive: true });
|
|
12
|
-
await fs.writeFile(tempPath, JSON.stringify(
|
|
59
|
+
await fs.writeFile(tempPath, JSON.stringify(overlay, null, 2), "utf-8");
|
|
13
60
|
await fs.rename(tempPath, target);
|
|
14
61
|
} catch (err) {
|
|
15
62
|
try {
|
|
@@ -17,13 +64,16 @@ async function saveMap(map, customPath) {
|
|
|
17
64
|
} catch {
|
|
18
65
|
}
|
|
19
66
|
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
-
throw new Error(`
|
|
67
|
+
throw new Error(`SensitivityOverlay.saveOverlay: failed to save to ${target}: ${message}`, {
|
|
21
68
|
cause: err
|
|
22
69
|
});
|
|
23
70
|
}
|
|
24
71
|
}
|
|
25
|
-
async function
|
|
26
|
-
const target = customPath ??
|
|
72
|
+
async function loadOverlay(customPath) {
|
|
73
|
+
const target = customPath ?? DEFAULT_OVERLAY_PATH;
|
|
74
|
+
const failLoad = (message) => {
|
|
75
|
+
throw new Error(`SensitivityOverlay.loadOverlay: file at ${target} is invalid: ${message}`);
|
|
76
|
+
};
|
|
27
77
|
let raw;
|
|
28
78
|
try {
|
|
29
79
|
raw = await fs.readFile(target, "utf-8");
|
|
@@ -38,21 +88,56 @@ async function loadMap(customPath) {
|
|
|
38
88
|
parsed = JSON.parse(raw);
|
|
39
89
|
} catch (err) {
|
|
40
90
|
const message = err instanceof Error ? err.message : String(err);
|
|
41
|
-
throw new Error(`
|
|
91
|
+
throw new Error(`SensitivityOverlay.loadOverlay: file at ${target} is malformed: ${message}`, {
|
|
42
92
|
cause: err
|
|
43
93
|
});
|
|
44
94
|
}
|
|
45
|
-
|
|
95
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
96
|
+
failLoad("top level must be a JSON object");
|
|
97
|
+
}
|
|
98
|
+
rejectUnknownJsonKeys("overlay", parsed, OVERLAY_TOP_KEYS, failLoad);
|
|
99
|
+
const version = typeof parsed.version === "string" ? parsed.version : "1.0";
|
|
46
100
|
const repoRoot = typeof parsed.repoRoot === "string" ? parsed.repoRoot : "";
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
101
|
+
const rawDecisions = Array.isArray(parsed.decisions) ? parsed.decisions : [];
|
|
102
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : "unknown";
|
|
103
|
+
const decisions = rawDecisions.map((d, i) => {
|
|
104
|
+
const prefix = `decisions[${i}]`;
|
|
105
|
+
if (typeof d !== "object" || d === null || Array.isArray(d)) {
|
|
106
|
+
failLoad(`${prefix} must be an object`);
|
|
107
|
+
}
|
|
108
|
+
const entry = d;
|
|
109
|
+
rejectUnknownJsonKeys(prefix, entry, DECISION_KEYS, failLoad);
|
|
110
|
+
if (typeof entry.path !== "string" || entry.path.length === 0) {
|
|
111
|
+
failLoad(`${prefix}.path must be a non-empty string`);
|
|
112
|
+
}
|
|
113
|
+
if (typeof entry.decision !== "string" || !VALID_DECISION_TYPES.has(entry.decision)) {
|
|
114
|
+
failLoad(
|
|
115
|
+
`${prefix}.decision is "${String(entry.decision)}" \u2014 must be one of: reject, accept, override`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const result = {
|
|
119
|
+
path: entry.path,
|
|
120
|
+
decision: entry.decision,
|
|
121
|
+
decidedAt: typeof entry.decidedAt === "string" ? entry.decidedAt : "unknown"
|
|
122
|
+
};
|
|
123
|
+
if (entry.reason !== void 0) result.reason = String(entry.reason);
|
|
124
|
+
if (entry.category !== void 0) result.category = String(entry.category);
|
|
125
|
+
if (entry.sensitivity !== void 0) {
|
|
126
|
+
result.sensitivity = sanitizeSensitivityScore(
|
|
127
|
+
`${prefix}.sensitivity`,
|
|
128
|
+
entry.sensitivity,
|
|
129
|
+
failLoad
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
});
|
|
134
|
+
return { version, repoRoot, decisions, updatedAt };
|
|
50
135
|
}
|
|
51
|
-
function
|
|
136
|
+
function lookupOverlayDecision(overlay, target, repoRoot) {
|
|
52
137
|
if (!target || typeof target !== "string" || target.trim() === "") {
|
|
53
138
|
return null;
|
|
54
139
|
}
|
|
55
|
-
const lookupRoot = repoRoot ??
|
|
140
|
+
const lookupRoot = repoRoot ?? overlay.repoRoot;
|
|
56
141
|
let canonical;
|
|
57
142
|
if (path.isAbsolute(target)) {
|
|
58
143
|
if (!lookupRoot) return null;
|
|
@@ -66,48 +151,76 @@ function lookupEntry(map, target, repoRoot) {
|
|
|
66
151
|
canonical = canonical.slice(2);
|
|
67
152
|
}
|
|
68
153
|
canonical = canonical.replace(/\\/g, "/");
|
|
69
|
-
for (const
|
|
70
|
-
if (
|
|
154
|
+
for (const decision of overlay.decisions) {
|
|
155
|
+
if (decision.path === canonical) return decision;
|
|
71
156
|
}
|
|
72
157
|
return null;
|
|
73
158
|
}
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
159
|
+
function applyOverlay(overlay, decisionPath, decisionType, options) {
|
|
160
|
+
let canonical = decisionPath;
|
|
161
|
+
if (canonical.startsWith("./")) {
|
|
162
|
+
canonical = canonical.slice(2);
|
|
163
|
+
}
|
|
164
|
+
canonical = canonical.replace(/\\/g, "/");
|
|
165
|
+
const newDecision = {
|
|
166
|
+
path: canonical,
|
|
167
|
+
decision: decisionType,
|
|
168
|
+
decidedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
80
169
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
170
|
+
if (options?.reason !== void 0) newDecision.reason = options.reason;
|
|
171
|
+
if (options?.sensitivity !== void 0) newDecision.sensitivity = options.sensitivity;
|
|
172
|
+
if (options?.category !== void 0) newDecision.category = options.category;
|
|
173
|
+
const existing = overlay.decisions.findIndex((d) => d.path === canonical);
|
|
174
|
+
const decisions = existing >= 0 ? [
|
|
175
|
+
...overlay.decisions.slice(0, existing),
|
|
176
|
+
newDecision,
|
|
177
|
+
...overlay.decisions.slice(existing + 1)
|
|
178
|
+
] : [...overlay.decisions, newDecision];
|
|
179
|
+
return {
|
|
180
|
+
...overlay,
|
|
181
|
+
decisions,
|
|
182
|
+
updatedAt: newDecision.decidedAt
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function removeOverlayDecision(overlay, decisionPath) {
|
|
186
|
+
let canonical = decisionPath;
|
|
187
|
+
if (canonical.startsWith("./")) {
|
|
188
|
+
canonical = canonical.slice(2);
|
|
189
|
+
}
|
|
190
|
+
canonical = canonical.replace(/\\/g, "/");
|
|
191
|
+
const filtered = overlay.decisions.filter((d) => d.path !== canonical);
|
|
192
|
+
if (filtered.length === overlay.decisions.length) {
|
|
193
|
+
return overlay;
|
|
86
194
|
}
|
|
87
195
|
return {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
196
|
+
...overlay,
|
|
197
|
+
decisions: filtered,
|
|
198
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function createEmptyOverlay(repoRoot) {
|
|
202
|
+
return {
|
|
203
|
+
version: "1.0",
|
|
204
|
+
repoRoot,
|
|
205
|
+
decisions: [],
|
|
206
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
92
207
|
};
|
|
93
208
|
}
|
|
94
209
|
|
|
95
|
-
// src/
|
|
210
|
+
// src/repoSensitivityMap.ts
|
|
96
211
|
import * as fs2 from "fs/promises";
|
|
97
212
|
import * as path2 from "path";
|
|
98
213
|
import * as os2 from "os";
|
|
99
|
-
var
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
async function saveOverlay(overlay, customPath) {
|
|
105
|
-
const target = customPath ?? DEFAULT_OVERLAY_PATH;
|
|
214
|
+
var MAP_TOP_KEYS = ["scannedAt", "repoRoot", "entries", "summary"];
|
|
215
|
+
var ENTRY_KEYS = ["path", "sensitivity", "category", "confidence", "matchedRules"];
|
|
216
|
+
var DEFAULT_MAP_PATH = path2.join(os2.homedir(), ".dahlia", "repo-sensitivity.json");
|
|
217
|
+
async function saveMap(map, customPath) {
|
|
218
|
+
const target = customPath ?? DEFAULT_MAP_PATH;
|
|
106
219
|
const parent = path2.dirname(target);
|
|
107
220
|
const tempPath = target + ".tmp";
|
|
108
221
|
try {
|
|
109
222
|
await fs2.mkdir(parent, { recursive: true });
|
|
110
|
-
await fs2.writeFile(tempPath, JSON.stringify(
|
|
223
|
+
await fs2.writeFile(tempPath, JSON.stringify(map, null, 2), "utf-8");
|
|
111
224
|
await fs2.rename(tempPath, target);
|
|
112
225
|
} catch (err) {
|
|
113
226
|
try {
|
|
@@ -115,13 +228,16 @@ async function saveOverlay(overlay, customPath) {
|
|
|
115
228
|
} catch {
|
|
116
229
|
}
|
|
117
230
|
const message = err instanceof Error ? err.message : String(err);
|
|
118
|
-
throw new Error(`
|
|
231
|
+
throw new Error(`RepoSensitivityMap.saveMap: failed to save to ${target}: ${message}`, {
|
|
119
232
|
cause: err
|
|
120
233
|
});
|
|
121
234
|
}
|
|
122
235
|
}
|
|
123
|
-
async function
|
|
124
|
-
const target = customPath ??
|
|
236
|
+
async function loadMap(customPath) {
|
|
237
|
+
const target = customPath ?? DEFAULT_MAP_PATH;
|
|
238
|
+
const failLoad = (message) => {
|
|
239
|
+
throw new Error(`RepoSensitivityMap.loadMap: file at ${target} is invalid: ${message}`);
|
|
240
|
+
};
|
|
125
241
|
let raw;
|
|
126
242
|
try {
|
|
127
243
|
raw = await fs2.readFile(target, "utf-8");
|
|
@@ -136,21 +252,48 @@ async function loadOverlay(customPath) {
|
|
|
136
252
|
parsed = JSON.parse(raw);
|
|
137
253
|
} catch (err) {
|
|
138
254
|
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
-
throw new Error(`
|
|
255
|
+
throw new Error(`RepoSensitivityMap.loadMap: file at ${target} is malformed: ${message}`, {
|
|
140
256
|
cause: err
|
|
141
257
|
});
|
|
142
258
|
}
|
|
143
|
-
|
|
259
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
260
|
+
failLoad("top level must be a JSON object");
|
|
261
|
+
}
|
|
262
|
+
rejectUnknownJsonKeys("map", parsed, MAP_TOP_KEYS, failLoad);
|
|
263
|
+
const scannedAt = typeof parsed.scannedAt === "string" ? parsed.scannedAt : "unknown";
|
|
144
264
|
const repoRoot = typeof parsed.repoRoot === "string" ? parsed.repoRoot : "";
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
|
|
265
|
+
const rawEntries = Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
266
|
+
const entries = rawEntries.map((e, i) => {
|
|
267
|
+
const prefix = `entries[${i}]`;
|
|
268
|
+
if (typeof e !== "object" || e === null || Array.isArray(e)) {
|
|
269
|
+
failLoad(`${prefix} must be an object`);
|
|
270
|
+
}
|
|
271
|
+
const entry = e;
|
|
272
|
+
rejectUnknownJsonKeys(prefix, entry, ENTRY_KEYS, failLoad);
|
|
273
|
+
if (typeof entry.path !== "string" || entry.path.length === 0) {
|
|
274
|
+
failLoad(`${prefix}.path must be a non-empty string`);
|
|
275
|
+
}
|
|
276
|
+
const sensitivity = sanitizeSensitivityScore(
|
|
277
|
+
`${prefix}.sensitivity`,
|
|
278
|
+
entry.sensitivity,
|
|
279
|
+
failLoad
|
|
280
|
+
);
|
|
281
|
+
return {
|
|
282
|
+
path: entry.path,
|
|
283
|
+
sensitivity,
|
|
284
|
+
category: typeof entry.category === "string" ? entry.category : "general",
|
|
285
|
+
confidence: entry.confidence === "low" || entry.confidence === "medium" || entry.confidence === "high" ? entry.confidence : "low",
|
|
286
|
+
matchedRules: Array.isArray(entry.matchedRules) ? entry.matchedRules.map(String) : []
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
const summary = parsed.summary && typeof parsed.summary === "object" && !Array.isArray(parsed.summary) ? parsed.summary : computeSummaryFromEntries(entries);
|
|
290
|
+
return { scannedAt, repoRoot, entries, summary };
|
|
148
291
|
}
|
|
149
|
-
function
|
|
292
|
+
function lookupEntry(map, target, repoRoot) {
|
|
150
293
|
if (!target || typeof target !== "string" || target.trim() === "") {
|
|
151
294
|
return null;
|
|
152
295
|
}
|
|
153
|
-
const lookupRoot = repoRoot ??
|
|
296
|
+
const lookupRoot = repoRoot ?? map.repoRoot;
|
|
154
297
|
let canonical;
|
|
155
298
|
if (path2.isAbsolute(target)) {
|
|
156
299
|
if (!lookupRoot) return null;
|
|
@@ -164,134 +307,32 @@ function lookupOverlayDecision(overlay, target, repoRoot) {
|
|
|
164
307
|
canonical = canonical.slice(2);
|
|
165
308
|
}
|
|
166
309
|
canonical = canonical.replace(/\\/g, "/");
|
|
167
|
-
for (const
|
|
168
|
-
if (
|
|
310
|
+
for (const entry of map.entries) {
|
|
311
|
+
if (entry.path === canonical) return entry;
|
|
169
312
|
}
|
|
170
313
|
return null;
|
|
171
314
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const newDecision = {
|
|
179
|
-
path: canonical,
|
|
180
|
-
decision: decisionType,
|
|
181
|
-
decidedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
182
|
-
};
|
|
183
|
-
if (options?.reason !== void 0) newDecision.reason = options.reason;
|
|
184
|
-
if (options?.sensitivity !== void 0) newDecision.sensitivity = options.sensitivity;
|
|
185
|
-
if (options?.category !== void 0) newDecision.category = options.category;
|
|
186
|
-
const existing = overlay.decisions.findIndex((d) => d.path === canonical);
|
|
187
|
-
const decisions = existing >= 0 ? [
|
|
188
|
-
...overlay.decisions.slice(0, existing),
|
|
189
|
-
newDecision,
|
|
190
|
-
...overlay.decisions.slice(existing + 1)
|
|
191
|
-
] : [...overlay.decisions, newDecision];
|
|
192
|
-
return {
|
|
193
|
-
...overlay,
|
|
194
|
-
decisions,
|
|
195
|
-
updatedAt: newDecision.decidedAt
|
|
315
|
+
function computeSummaryFromEntries(entries) {
|
|
316
|
+
const byCategory = {};
|
|
317
|
+
const byConfidence = {
|
|
318
|
+
low: 0,
|
|
319
|
+
medium: 0,
|
|
320
|
+
high: 0
|
|
196
321
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
canonical = canonical.replace(/\\/g, "/");
|
|
204
|
-
const filtered = overlay.decisions.filter((d) => d.path !== canonical);
|
|
205
|
-
if (filtered.length === overlay.decisions.length) {
|
|
206
|
-
return overlay;
|
|
322
|
+
for (const entry of entries) {
|
|
323
|
+
byCategory[entry.category] = (byCategory[entry.category] ?? 0) + 1;
|
|
324
|
+
if (entry.confidence === "low" || entry.confidence === "medium" || entry.confidence === "high") {
|
|
325
|
+
byConfidence[entry.confidence]++;
|
|
326
|
+
}
|
|
207
327
|
}
|
|
208
328
|
return {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
function createEmptyOverlay(repoRoot) {
|
|
215
|
-
return {
|
|
216
|
-
version: "1.0",
|
|
217
|
-
repoRoot,
|
|
218
|
-
decisions: [],
|
|
219
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
329
|
+
totalFiles: entries.length,
|
|
330
|
+
sensitiveFiles: entries.length,
|
|
331
|
+
byCategory,
|
|
332
|
+
byConfidence
|
|
220
333
|
};
|
|
221
334
|
}
|
|
222
335
|
|
|
223
|
-
// src/defaults.ts
|
|
224
|
-
var DEFAULT_FORBIDDEN_PATTERNS = [
|
|
225
|
-
"**/.env",
|
|
226
|
-
"**/.env.*",
|
|
227
|
-
"**/.ssh/**",
|
|
228
|
-
"**/.aws/**",
|
|
229
|
-
"**/secrets/**",
|
|
230
|
-
"**/credentials/**",
|
|
231
|
-
"**/id_rsa*",
|
|
232
|
-
"**/id_dsa*",
|
|
233
|
-
"**/id_ecdsa*",
|
|
234
|
-
"**/id_ed25519*",
|
|
235
|
-
"**/*.pem",
|
|
236
|
-
"**/*.key",
|
|
237
|
-
"/etc/**",
|
|
238
|
-
// Sprint 26 FIX 1 (A) — common credential stores. DRIFT: keep in sync with
|
|
239
|
-
// STARTER_POLICY.forbid.targets in setup/initClaudeCode.ts (template↔defaults
|
|
240
|
-
// unification tracked in a separate ticket — do not refactor here).
|
|
241
|
-
"**/.netrc",
|
|
242
|
-
"**/.npmrc",
|
|
243
|
-
"**/.git-credentials",
|
|
244
|
-
"**/.pgpass",
|
|
245
|
-
"**/.zsh_history",
|
|
246
|
-
"**/.config/gh/**",
|
|
247
|
-
"**/.docker/config.json",
|
|
248
|
-
"**/.gnupg/**",
|
|
249
|
-
"**/.config/gcloud/**",
|
|
250
|
-
"**/.kube/**",
|
|
251
|
-
"**/Library/Keychains/**",
|
|
252
|
-
// Sprint 26 FIX 1 (B) — Sentinel's own state dir (current path only; the
|
|
253
|
-
// ~/.dahlia → ~/.sentinel rename is a separate re-arch).
|
|
254
|
-
"**/.dahlia/**",
|
|
255
|
-
// Sprint 26 FIX 3 — the live policy file and cc's hook-wiring settings files.
|
|
256
|
-
// Denies the agent's own tool-writes (policy rewrite / unhook vectors); reads
|
|
257
|
-
// stay allowed via DEFAULT_POLICY_READ_EXCEPTIONS below. The settings glob
|
|
258
|
-
// covers project AND user-level (~/.claude/settings*.json) in one pattern.
|
|
259
|
-
// DRIFT: keep in sync with STARTER_POLICY.forbid.targets in setup/initClaudeCode.ts.
|
|
260
|
-
"**/.sentinel.yaml",
|
|
261
|
-
"**/.claude/settings*.json"
|
|
262
|
-
];
|
|
263
|
-
var DEFAULT_POLICY_READ_EXCEPTIONS = [
|
|
264
|
-
{ target: "**/.sentinel.yaml", allowedActions: ["file_read"] },
|
|
265
|
-
{ target: "**/.claude/settings*.json", allowedActions: ["file_read"] }
|
|
266
|
-
];
|
|
267
|
-
function withPolicyReadExceptions(existing) {
|
|
268
|
-
const merged = existing ? [...existing] : [];
|
|
269
|
-
for (const exc of DEFAULT_POLICY_READ_EXCEPTIONS) {
|
|
270
|
-
if (!merged.some((e) => e.target === exc.target)) merged.push(exc);
|
|
271
|
-
}
|
|
272
|
-
return merged;
|
|
273
|
-
}
|
|
274
|
-
var DEFAULT_MEDIUM_DISPOSITION = {
|
|
275
|
-
network_request: "deny"
|
|
276
|
-
};
|
|
277
|
-
var DEFAULT_NETWORK_DENYLIST_CIDRS = [
|
|
278
|
-
"10.0.0.0/8",
|
|
279
|
-
// RFC1918 private (Class A)
|
|
280
|
-
"172.16.0.0/12",
|
|
281
|
-
// RFC1918 private (Class B)
|
|
282
|
-
"192.168.0.0/16",
|
|
283
|
-
// RFC1918 private (Class C)
|
|
284
|
-
"127.0.0.0/8",
|
|
285
|
-
// loopback v4
|
|
286
|
-
"169.254.0.0/16",
|
|
287
|
-
// link-local v4 (cloud metadata endpoints)
|
|
288
|
-
"fe80::/10",
|
|
289
|
-
// link-local v6
|
|
290
|
-
"::1/128"
|
|
291
|
-
// loopback v6
|
|
292
|
-
];
|
|
293
|
-
var DEFAULT_DANGEROUS_SCHEMES = ["file:", "data:", "javascript:", "vbscript:"];
|
|
294
|
-
|
|
295
336
|
// src/roleValidator.ts
|
|
296
337
|
import { normalize as normalize2, basename as basename2, dirname as dirname4, join as join4 } from "path";
|
|
297
338
|
import { lstatSync, readdirSync, realpathSync as realpathSync2 } from "fs";
|
|
@@ -550,7 +591,7 @@ function tokenizePaths(command) {
|
|
|
550
591
|
if (dispatch.unparseable) {
|
|
551
592
|
result.unparseable = true;
|
|
552
593
|
}
|
|
553
|
-
} else if (isPathShaped(token)) {
|
|
594
|
+
} else if (isPathShaped(token) && !isEnvIdentifierChain(token)) {
|
|
554
595
|
const resolved = resolvePathToken(token);
|
|
555
596
|
result.paths.push(resolved);
|
|
556
597
|
}
|
|
@@ -558,6 +599,21 @@ function tokenizePaths(command) {
|
|
|
558
599
|
}
|
|
559
600
|
return result;
|
|
560
601
|
}
|
|
602
|
+
function isEnvIdentifierChain(token) {
|
|
603
|
+
if (token.includes("/") || token.startsWith(".")) return false;
|
|
604
|
+
if (!allEnvOccurrencesIdentifierEmbedded(token)) return false;
|
|
605
|
+
return !SENSITIVE_BASENAME_RE.test(token.replace(/\.env/gi, " "));
|
|
606
|
+
}
|
|
607
|
+
function allEnvOccurrencesIdentifierEmbedded(s) {
|
|
608
|
+
const envHits = /\.env/gi;
|
|
609
|
+
let m;
|
|
610
|
+
let any = false;
|
|
611
|
+
while ((m = envHits.exec(s)) !== null) {
|
|
612
|
+
any = true;
|
|
613
|
+
if (!/[A-Za-z0-9_$]/.test(s[m.index - 1] ?? "")) return false;
|
|
614
|
+
}
|
|
615
|
+
return any;
|
|
616
|
+
}
|
|
561
617
|
function isPathShaped(token) {
|
|
562
618
|
if (token.includes("/")) return true;
|
|
563
619
|
if (token.startsWith(".")) return true;
|
|
@@ -633,8 +689,8 @@ function scanBashCommand(command, forbiddenBasenames) {
|
|
|
633
689
|
}
|
|
634
690
|
function buildPattern(basename3) {
|
|
635
691
|
const escaped = escapeRegex(basename3);
|
|
636
|
-
if (
|
|
637
|
-
return new RegExp(
|
|
692
|
+
if (EXTENSION_BASENAMES.has(basename3.toLowerCase())) {
|
|
693
|
+
return new RegExp(`(?:^|\\w)${escaped}(?=$|[\\s;&|<>()'"=\\/])`, "i");
|
|
638
694
|
}
|
|
639
695
|
if (basename3.startsWith(".")) {
|
|
640
696
|
return new RegExp(`(?:^|[\\s;&|<>()\\/'"=])${escaped}(?=$|[\\s;&|<>()\\/'"=.])`, "i");
|
|
@@ -653,17 +709,15 @@ function scanContentForForbiddenBasenames(content, forbiddenBasenames) {
|
|
|
653
709
|
}
|
|
654
710
|
function buildContentPattern(basename3) {
|
|
655
711
|
const escaped = escapeRegex(basename3);
|
|
656
|
-
if (
|
|
657
|
-
return new RegExp(
|
|
712
|
+
if (EXTENSION_BASENAMES.has(basename3.toLowerCase())) {
|
|
713
|
+
return new RegExp(`(?:^|\\w)${escaped}(?=$|[\\s;&|<>()'"=\\/])`, "i");
|
|
658
714
|
}
|
|
659
715
|
if (basename3.startsWith(".")) {
|
|
660
716
|
return new RegExp(`(?:^|[\\s;&|<>()\\/'"=])${escaped}(?=$|[\\s;&|<>()\\/'"=.])`, "i");
|
|
661
717
|
}
|
|
662
718
|
return new RegExp(`(?<=[/\\\\]\\.?)${escaped}\\b`, "i");
|
|
663
719
|
}
|
|
664
|
-
|
|
665
|
-
return /^\.[a-zA-Z]+$/.test(s);
|
|
666
|
-
}
|
|
720
|
+
var EXTENSION_BASENAMES = /* @__PURE__ */ new Set([".pem", ".key"]);
|
|
667
721
|
function escapeRegex(s) {
|
|
668
722
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
669
723
|
}
|
|
@@ -681,8 +735,8 @@ function scanGlobPattern(pattern, forbiddenBasenames) {
|
|
|
681
735
|
function buildGlobContextPattern(basename3) {
|
|
682
736
|
const escaped = escapeRegex(basename3);
|
|
683
737
|
const GLOB_DELIM = String.raw`\s;&|<>()\\/'"=.*?{}\[\]`;
|
|
684
|
-
if (
|
|
685
|
-
return new RegExp(`[\\w*]${escaped}(?=$|[${GLOB_DELIM}])`, "i");
|
|
738
|
+
if (EXTENSION_BASENAMES.has(basename3.toLowerCase())) {
|
|
739
|
+
return new RegExp(`(?:^|[\\w*])${escaped}(?=$|[${GLOB_DELIM}])`, "i");
|
|
686
740
|
}
|
|
687
741
|
if (basename3.startsWith(".")) {
|
|
688
742
|
return new RegExp(`(?:^|[${GLOB_DELIM}])${escaped}(?=$|[${GLOB_DELIM}])`, "i");
|
|
@@ -816,6 +870,7 @@ function classifyDeny(command, signals) {
|
|
|
816
870
|
}
|
|
817
871
|
|
|
818
872
|
// src/roleValidator.ts
|
|
873
|
+
import { parse as shellParse2 } from "shell-quote";
|
|
819
874
|
var SUSPICIOUS_BASENAME_RE = /^\.|(\.env|secret|credential|key|config|token)/i;
|
|
820
875
|
function resolveSymlinks(normalizedPath) {
|
|
821
876
|
if (!normalizedPath || normalizedPath.includes("://")) return normalizedPath;
|
|
@@ -1148,10 +1203,26 @@ var RoleValidator = class {
|
|
|
1148
1203
|
}
|
|
1149
1204
|
}
|
|
1150
1205
|
}
|
|
1151
|
-
|
|
1206
|
+
if (event.action === "command_exec") {
|
|
1207
|
+
const cmd = this.screenCommandTarget(event);
|
|
1208
|
+
if (cmd) {
|
|
1209
|
+
const finding = this.buildForbiddenFinding(
|
|
1210
|
+
event,
|
|
1211
|
+
cmd.matchedTarget,
|
|
1212
|
+
cmd.matchedPattern,
|
|
1213
|
+
activeTask ?? null
|
|
1214
|
+
);
|
|
1215
|
+
if (finding) {
|
|
1216
|
+
finding.targetKey = cmd.targetKey;
|
|
1217
|
+
finding.dedupKey = event.primaryTarget;
|
|
1218
|
+
finding.mentionOnly = false;
|
|
1219
|
+
return finding;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1152
1223
|
for (const pattern of this.role.forbiddenTargetPatterns) {
|
|
1153
1224
|
let matchedTargetValue = null;
|
|
1154
|
-
if (
|
|
1225
|
+
if (event.action !== "command_exec" && primaryTargetPaths.some((tp) => matchGlobInsensitive(pattern, tp))) {
|
|
1155
1226
|
matchedTargetValue = normalizedPrimaryTarget;
|
|
1156
1227
|
}
|
|
1157
1228
|
if (!matchedTargetValue) {
|
|
@@ -1169,57 +1240,13 @@ var RoleValidator = class {
|
|
|
1169
1240
|
}
|
|
1170
1241
|
}
|
|
1171
1242
|
if (matchedTargetValue) {
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
"unauthorized_target",
|
|
1180
|
-
matchedTargetValue,
|
|
1181
|
-
event.action
|
|
1182
|
-
),
|
|
1183
|
-
matchedTarget: matchedTargetValue
|
|
1184
|
-
});
|
|
1185
|
-
if (!isCritical) {
|
|
1186
|
-
const exception = findMatchingException(this.role.exceptions, event, activeTask ?? null);
|
|
1187
|
-
if (exception) {
|
|
1188
|
-
this.onAuditEntry?.({
|
|
1189
|
-
type: "exception_applied",
|
|
1190
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1191
|
-
agentId: event.agentId,
|
|
1192
|
-
target: matchedTargetValue,
|
|
1193
|
-
action: event.action,
|
|
1194
|
-
exceptionTarget: exception.target,
|
|
1195
|
-
taskId: activeTask?.taskId ?? null
|
|
1196
|
-
});
|
|
1197
|
-
if (exception.requiresApproval) {
|
|
1198
|
-
const ctx = {
|
|
1199
|
-
finding,
|
|
1200
|
-
exception,
|
|
1201
|
-
activeTask: activeTask ?? null,
|
|
1202
|
-
expiresAt: exception.expiresAfter != null && activeTask ? new Date(new Date(activeTask.startedAt).getTime() + exception.expiresAfter) : null
|
|
1203
|
-
};
|
|
1204
|
-
const approved = this.approvalFn?.(ctx) === true;
|
|
1205
|
-
if (approved) {
|
|
1206
|
-
this.onAuditEntry?.({
|
|
1207
|
-
type: "exception_approved",
|
|
1208
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1209
|
-
agentId: event.agentId,
|
|
1210
|
-
target: matchedTargetValue,
|
|
1211
|
-
action: event.action,
|
|
1212
|
-
exceptionTarget: exception.target,
|
|
1213
|
-
taskId: activeTask?.taskId ?? null
|
|
1214
|
-
});
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
} else {
|
|
1218
|
-
continue;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
return this.enhanceWithSensitivity(finding, event);
|
|
1243
|
+
const finding = this.buildForbiddenFinding(
|
|
1244
|
+
event,
|
|
1245
|
+
matchedTargetValue,
|
|
1246
|
+
pattern,
|
|
1247
|
+
activeTask ?? null
|
|
1248
|
+
);
|
|
1249
|
+
if (finding) return finding;
|
|
1223
1250
|
}
|
|
1224
1251
|
}
|
|
1225
1252
|
if (this.sensitivityScorer && event.metadata?._mcpTool === "true") {
|
|
@@ -1238,7 +1265,7 @@ var RoleValidator = class {
|
|
|
1238
1265
|
});
|
|
1239
1266
|
}
|
|
1240
1267
|
}
|
|
1241
|
-
if (this.role.allowedTargetPatterns.length > 0) {
|
|
1268
|
+
if (this.role.allowedTargetPatterns.length > 0 && event.action !== "command_exec") {
|
|
1242
1269
|
const anchor = this.workspaceRoot !== "" && PATH_TARGET_ACTIONS.has(event.action);
|
|
1243
1270
|
const matched = this.role.allowedTargetPatterns.some((pattern) => {
|
|
1244
1271
|
const candidates = anchor ? [pattern, anchorAllowedPattern(pattern, this.workspaceRoot)] : [pattern];
|
|
@@ -1276,7 +1303,7 @@ var RoleValidator = class {
|
|
|
1276
1303
|
}
|
|
1277
1304
|
}
|
|
1278
1305
|
const scopePatterns = activeTask?.scopePatterns;
|
|
1279
|
-
if (scopePatterns && scopePatterns.length > 0) {
|
|
1306
|
+
if (scopePatterns && scopePatterns.length > 0 && event.action !== "command_exec") {
|
|
1280
1307
|
const anchor = this.workspaceRoot !== "" && PATH_TARGET_ACTIONS.has(event.action);
|
|
1281
1308
|
const inScope = scopePatterns.some((pattern) => {
|
|
1282
1309
|
const candidates = anchor ? [pattern, anchorAllowedPattern(pattern, this.workspaceRoot)] : [pattern];
|
|
@@ -1340,6 +1367,127 @@ var RoleValidator = class {
|
|
|
1340
1367
|
}
|
|
1341
1368
|
return finding;
|
|
1342
1369
|
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Sprint 26B B′ — scanner-authoritative forbidden screen for a command_exec
|
|
1372
|
+
* PRIMARY target (the command string). Three layers, first match wins:
|
|
1373
|
+
*
|
|
1374
|
+
* L1 — tokenizePaths(command): resolved path-shaped tokens (symlink-resolved,
|
|
1375
|
+
* dir-globs, F-5b's isEnvIdentifierChain guard inside) matched against the
|
|
1376
|
+
* FULL role patterns via matchGlob.
|
|
1377
|
+
* Boundary — every argv token's basename vs each pattern's basename component
|
|
1378
|
+
* via the SAME matchGlob (its `^…$` anchoring → true boundary, so
|
|
1379
|
+
* `^\.env$` rejects `process.env` and `^payroll\.csv$` rejects
|
|
1380
|
+
* `mypayroll.csv`, while a `payroll.csv` token matches a `payroll.csv`
|
|
1381
|
+
* pattern basename). Patterns whose basename is pure-wildcard (a bare
|
|
1382
|
+
* star or globstar) are dir-globs and are skipped here (L1 handles them
|
|
1383
|
+
* on resolved paths).
|
|
1384
|
+
* L2 — scanBashCommand substring net (fixed FORBIDDEN_BASENAMES) gated by
|
|
1385
|
+
* isPositionallySafeMention, for heredoc/substitution shapes tokenization
|
|
1386
|
+
* can't cleanly split.
|
|
1387
|
+
*
|
|
1388
|
+
* Returns the matched target + the F-5a targetKey (resolved path / token /
|
|
1389
|
+
* `l2:<basenames>`, mirroring the gateway emit) + a pattern label for the
|
|
1390
|
+
* finding description, or null when nothing matches.
|
|
1391
|
+
*/
|
|
1392
|
+
screenCommandTarget(event) {
|
|
1393
|
+
const command = event.primaryTarget;
|
|
1394
|
+
if (isPositionallySafeMention(command)) return null;
|
|
1395
|
+
const tr = tokenizePaths(command);
|
|
1396
|
+
for (const p of tr.paths) {
|
|
1397
|
+
for (const pattern of this.role.forbiddenTargetPatterns) {
|
|
1398
|
+
if (matchGlobInsensitive(pattern, p)) {
|
|
1399
|
+
return { matchedTarget: p, targetKey: p, matchedPattern: pattern };
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
let tokens;
|
|
1404
|
+
try {
|
|
1405
|
+
tokens = shellParse2(command);
|
|
1406
|
+
} catch {
|
|
1407
|
+
tokens = [];
|
|
1408
|
+
}
|
|
1409
|
+
for (const tok of tokens) {
|
|
1410
|
+
if (typeof tok !== "string") continue;
|
|
1411
|
+
const tokBase = basename2(tok);
|
|
1412
|
+
if (tokBase.length === 0) continue;
|
|
1413
|
+
for (const pattern of this.role.forbiddenTargetPatterns) {
|
|
1414
|
+
const patBase = basename2(pattern);
|
|
1415
|
+
if (patBase === "*" || patBase === "**") continue;
|
|
1416
|
+
if (matchGlobInsensitive(patBase, tokBase)) {
|
|
1417
|
+
return { matchedTarget: tok, targetKey: tok, matchedPattern: pattern };
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
const scan = scanBashCommand(command, FORBIDDEN_BASENAMES);
|
|
1422
|
+
if (scan.matched) {
|
|
1423
|
+
const hits = [...new Set(scan.hits)].sort();
|
|
1424
|
+
return {
|
|
1425
|
+
matchedTarget: scan.hits[0],
|
|
1426
|
+
targetKey: `l2:${hits.join(",")}`,
|
|
1427
|
+
matchedPattern: hits.join(", ")
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Build the unauthorized_target finding for a matched forbidden target, applying
|
|
1434
|
+
* the same exception/approval/sensitivity flow shared by Check 2's per-pattern
|
|
1435
|
+
* loop and the B′ command screen. Returns the finding to emit, or null when an
|
|
1436
|
+
* exception (auto, or approved) suppresses it (caller continues / falls through).
|
|
1437
|
+
*/
|
|
1438
|
+
buildForbiddenFinding(event, matchedTargetValue, pattern, activeTask) {
|
|
1439
|
+
const sensitivity = this.sensitivityScorer?.scoreTarget(matchedTargetValue, event.action);
|
|
1440
|
+
const isCritical = sensitivity ? sensitivity.effectiveScore >= 0.9 : false;
|
|
1441
|
+
const finding = this.makeFinding(event, {
|
|
1442
|
+
severity: "HIGH",
|
|
1443
|
+
type: "unauthorized_target",
|
|
1444
|
+
description: `Agent accessed '${matchedTargetValue}' which matches forbidden pattern '${pattern}'`,
|
|
1445
|
+
recommendation: getTargetRecommendation(
|
|
1446
|
+
"unauthorized_target",
|
|
1447
|
+
matchedTargetValue,
|
|
1448
|
+
event.action
|
|
1449
|
+
),
|
|
1450
|
+
matchedTarget: matchedTargetValue
|
|
1451
|
+
});
|
|
1452
|
+
if (!isCritical) {
|
|
1453
|
+
const exception = findMatchingException(this.role.exceptions, event, activeTask);
|
|
1454
|
+
if (exception) {
|
|
1455
|
+
this.onAuditEntry?.({
|
|
1456
|
+
type: "exception_applied",
|
|
1457
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1458
|
+
agentId: event.agentId,
|
|
1459
|
+
target: matchedTargetValue,
|
|
1460
|
+
action: event.action,
|
|
1461
|
+
exceptionTarget: exception.target,
|
|
1462
|
+
taskId: activeTask?.taskId ?? null
|
|
1463
|
+
});
|
|
1464
|
+
if (exception.requiresApproval) {
|
|
1465
|
+
const ctx = {
|
|
1466
|
+
finding,
|
|
1467
|
+
exception,
|
|
1468
|
+
activeTask,
|
|
1469
|
+
expiresAt: exception.expiresAfter != null && activeTask ? new Date(new Date(activeTask.startedAt).getTime() + exception.expiresAfter) : null
|
|
1470
|
+
};
|
|
1471
|
+
const approved = this.approvalFn?.(ctx) === true;
|
|
1472
|
+
if (approved) {
|
|
1473
|
+
this.onAuditEntry?.({
|
|
1474
|
+
type: "exception_approved",
|
|
1475
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1476
|
+
agentId: event.agentId,
|
|
1477
|
+
target: matchedTargetValue,
|
|
1478
|
+
action: event.action,
|
|
1479
|
+
exceptionTarget: exception.target,
|
|
1480
|
+
taskId: activeTask?.taskId ?? null
|
|
1481
|
+
});
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
} else {
|
|
1485
|
+
return null;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return this.enhanceWithSensitivity(finding, event);
|
|
1490
|
+
}
|
|
1343
1491
|
makeFinding(event, details) {
|
|
1344
1492
|
return {
|
|
1345
1493
|
severity: details.severity,
|
|
@@ -1759,18 +1907,15 @@ var TargetSensitivityScorer = class {
|
|
|
1759
1907
|
};
|
|
1760
1908
|
|
|
1761
1909
|
export {
|
|
1762
|
-
DEFAULT_MAP_PATH,
|
|
1763
|
-
saveMap,
|
|
1764
|
-
loadMap,
|
|
1765
1910
|
DEFAULT_OVERLAY_PATH,
|
|
1766
1911
|
saveOverlay,
|
|
1767
1912
|
loadOverlay,
|
|
1768
1913
|
applyOverlay,
|
|
1769
1914
|
removeOverlayDecision,
|
|
1770
1915
|
createEmptyOverlay,
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1916
|
+
DEFAULT_MAP_PATH,
|
|
1917
|
+
saveMap,
|
|
1918
|
+
loadMap,
|
|
1774
1919
|
TargetSensitivityScorer,
|
|
1775
1920
|
tokenizePaths,
|
|
1776
1921
|
FORBIDDEN_BASENAMES,
|
|
@@ -1788,4 +1933,4 @@ export {
|
|
|
1788
1933
|
findMatchingException,
|
|
1789
1934
|
RoleValidator
|
|
1790
1935
|
};
|
|
1791
|
-
//# sourceMappingURL=chunk-
|
|
1936
|
+
//# sourceMappingURL=chunk-FIEIGBYL.js.map
|