@tuent/sentinel 0.1.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.
@@ -0,0 +1,10 @@
1
+ import {
2
+ Sentinel
3
+ } from "./chunk-QFRDEISP.js";
4
+ import "./chunk-6MHWJATS.js";
5
+ import "./chunk-2FFMYSVC.js";
6
+ import "./chunk-NUXSUSYY.js";
7
+ export {
8
+ Sentinel
9
+ };
10
+ //# sourceMappingURL=Sentinel-JLQL3YRD.js.map
@@ -0,0 +1,23 @@
1
+ import {
2
+ generateKeyPair,
3
+ getOrCreateKeyPair,
4
+ loadKeyPair,
5
+ publicKeyFromBase64,
6
+ publicKeyToBase64,
7
+ reconstructSigningPayload,
8
+ saveKeyPair,
9
+ signPayload,
10
+ verifySignature
11
+ } from "./chunk-NUXSUSYY.js";
12
+ export {
13
+ generateKeyPair,
14
+ getOrCreateKeyPair,
15
+ loadKeyPair,
16
+ publicKeyFromBase64,
17
+ publicKeyToBase64,
18
+ reconstructSigningPayload,
19
+ saveKeyPair,
20
+ signPayload,
21
+ verifySignature
22
+ };
23
+ //# sourceMappingURL=auditTrailKeys-GKCW5KUD.js.map
@@ -0,0 +1,428 @@
1
+ // src/policyLoader.ts
2
+ import yaml from "js-yaml";
3
+ var LOCKED_ACTIONABLE_TYPES = /* @__PURE__ */ new Set([
4
+ "role_violation",
5
+ "unauthorized_target",
6
+ "scope_violation"
7
+ ]);
8
+ var VALID_DAYS = /* @__PURE__ */ new Set([
9
+ "monday",
10
+ "tuesday",
11
+ "wednesday",
12
+ "thursday",
13
+ "friday",
14
+ "saturday",
15
+ "sunday"
16
+ ]);
17
+ var VALID_CHANNELS = /* @__PURE__ */ new Set(["console", "webhook", "file"]);
18
+ var VALID_SEVERITIES = /* @__PURE__ */ new Set(["LOW", "MEDIUM", "HIGH", "CRITICAL"]);
19
+ var VALID_KINDS = /* @__PURE__ */ new Set(["informational", "actionable"]);
20
+ var VALID_ACTIONS = /* @__PURE__ */ new Set([
21
+ "file_read",
22
+ "file_write",
23
+ "api_call",
24
+ "database_query",
25
+ "command_exec",
26
+ "tool_invocation",
27
+ "network_request"
28
+ ]);
29
+ function fail(message) {
30
+ throw new Error(`Policy validation error: ${message}`);
31
+ }
32
+ function validatePolicy(data) {
33
+ if (typeof data !== "object" || data === null) {
34
+ fail("policy must be a YAML object");
35
+ }
36
+ const doc = data;
37
+ if (doc.version === void 0) fail("version is required");
38
+ if (String(doc.version) !== "1.0") fail('version must be "1.0"');
39
+ if (typeof doc.agent !== "object" || doc.agent === null) fail("agent section is required");
40
+ const agent = doc.agent;
41
+ if (typeof agent.id !== "string" || agent.id.length === 0) fail("agent.id is required");
42
+ if (typeof agent.name !== "string" || agent.name.length === 0) fail("agent.name is required");
43
+ if (agent.description !== void 0 && typeof agent.description !== "string") {
44
+ fail("agent.description must be a string");
45
+ }
46
+ if (typeof doc.policy !== "object" || doc.policy === null) fail("policy section is required");
47
+ const policy = doc.policy;
48
+ if (typeof policy.allow !== "object" || policy.allow === null) {
49
+ fail("policy.allow section is required");
50
+ }
51
+ const allow = policy.allow;
52
+ if (!Array.isArray(allow.actions) || allow.actions.length === 0) {
53
+ fail("policy.allow.actions must be a non-empty array of strings");
54
+ }
55
+ if (!allow.actions.every((a) => typeof a === "string")) {
56
+ fail("policy.allow.actions must be a non-empty array of strings");
57
+ }
58
+ if (!Array.isArray(allow.targets) || allow.targets.length === 0) {
59
+ fail("policy.allow.targets must be a non-empty array of strings");
60
+ }
61
+ if (!allow.targets.every((t) => typeof t === "string")) {
62
+ fail("policy.allow.targets must be a non-empty array of strings");
63
+ }
64
+ if (allow.networkHosts !== void 0) {
65
+ if (!Array.isArray(allow.networkHosts)) {
66
+ fail("policy.allow.networkHosts must be an array of strings");
67
+ }
68
+ for (let i = 0; i < allow.networkHosts.length; i++) {
69
+ const entry = allow.networkHosts[i];
70
+ if (typeof entry !== "string" || entry.length === 0) {
71
+ fail(`policy.allow.networkHosts[${i}] must be a non-empty string`);
72
+ }
73
+ if (/[\s/:]/.test(entry)) {
74
+ fail(
75
+ `policy.allow.networkHosts[${i}] ("${entry}") contains whitespace, slash, or colon \u2014 these are not valid hostnames. Use bare hostnames only (e.g. "docs.anthropic.com").`
76
+ );
77
+ }
78
+ }
79
+ for (let i = 0; i < allow.networkHosts.length; i++) {
80
+ allow.networkHosts[i] = allow.networkHosts[i].trim().toLowerCase().replace(/\.+$/, "");
81
+ }
82
+ }
83
+ if (typeof policy.forbid !== "object" || policy.forbid === null) {
84
+ fail("policy.forbid section is required");
85
+ }
86
+ const forbid = policy.forbid;
87
+ if (!Array.isArray(forbid.targets) || forbid.targets.length === 0) {
88
+ fail("policy.forbid.targets must be a non-empty array of strings");
89
+ }
90
+ if (!forbid.targets.every((t) => typeof t === "string")) {
91
+ fail("policy.forbid.targets must be a non-empty array of strings");
92
+ }
93
+ if (policy.exceptions !== void 0) {
94
+ if (!Array.isArray(policy.exceptions)) {
95
+ fail("policy.exceptions must be an array");
96
+ }
97
+ for (let i = 0; i < policy.exceptions.length; i++) {
98
+ const prefix = `exception #${i + 1}`;
99
+ const ex = policy.exceptions[i];
100
+ if (typeof ex !== "object" || ex === null) {
101
+ fail(`${prefix}: must be an object`);
102
+ }
103
+ if (typeof ex.target !== "string" || ex.target.length === 0) {
104
+ fail(`${prefix}: target is required and must be a non-empty string`);
105
+ }
106
+ if (!Array.isArray(ex.allowedActions) || ex.allowedActions.length === 0) {
107
+ fail(
108
+ `${prefix}: allowedActions must be a non-empty array of valid AgentAction values; got ${JSON.stringify(ex.allowedActions ?? "undefined")}`
109
+ );
110
+ }
111
+ for (const action of ex.allowedActions) {
112
+ if (typeof action !== "string" || !VALID_ACTIONS.has(action)) {
113
+ fail(
114
+ `${prefix}: allowedActions contains invalid action "${action}". Valid: ${[...VALID_ACTIONS].join(", ")}`
115
+ );
116
+ }
117
+ }
118
+ if (ex.requiresTask !== void 0) {
119
+ if (typeof ex.requiresTask !== "string" || ex.requiresTask.length === 0) {
120
+ fail(`${prefix}: requiresTask must be a non-empty string`);
121
+ }
122
+ }
123
+ if (ex.requiresApproval !== void 0) {
124
+ if (typeof ex.requiresApproval !== "boolean") {
125
+ fail(`${prefix}: requiresApproval must be a boolean`);
126
+ }
127
+ }
128
+ if (ex.expiresAfter !== void 0) {
129
+ if (typeof ex.expiresAfter !== "number" || !Number.isFinite(ex.expiresAfter) || ex.expiresAfter <= 0) {
130
+ fail(
131
+ `${prefix}: expiresAfter must be a positive finite number (milliseconds); got ${ex.expiresAfter}`
132
+ );
133
+ }
134
+ }
135
+ if (ex.downgradeKindTo !== void 0) {
136
+ if (typeof ex.downgradeKindTo !== "string" || !VALID_KINDS.has(ex.downgradeKindTo)) {
137
+ fail(`${prefix}: downgradeKindTo must be one of: informational, actionable`);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ if (policy.schedule !== void 0) {
143
+ if (typeof policy.schedule !== "object" || policy.schedule === null) {
144
+ fail("policy.schedule must be an object");
145
+ }
146
+ const schedule = policy.schedule;
147
+ if (!Array.isArray(schedule.hours) || schedule.hours.length !== 2) {
148
+ fail("schedule.hours must be [start, end] with values 0-23");
149
+ }
150
+ const [start, end] = schedule.hours;
151
+ if (typeof start !== "number" || typeof end !== "number" || !Number.isInteger(start) || !Number.isInteger(end) || start < 0 || start > 23 || end < 0 || end > 23) {
152
+ fail("schedule.hours must be [start, end] with values 0-23");
153
+ }
154
+ if (schedule.days !== void 0) {
155
+ if (!Array.isArray(schedule.days)) {
156
+ fail("schedule.days must be an array of day strings");
157
+ }
158
+ for (const day of schedule.days) {
159
+ if (typeof day !== "string" || !VALID_DAYS.has(day.toLowerCase())) {
160
+ fail(
161
+ `schedule.days contains invalid day "${day}". Valid days: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday`
162
+ );
163
+ }
164
+ }
165
+ }
166
+ }
167
+ if (policy.limits !== void 0) {
168
+ if (typeof policy.limits !== "object" || policy.limits === null) {
169
+ fail("policy.limits must be an object");
170
+ }
171
+ const limits = policy.limits;
172
+ if (limits.maxEventsPerHour !== void 0 && typeof limits.maxEventsPerHour !== "number") {
173
+ fail("policy.limits.maxEventsPerHour must be a number");
174
+ }
175
+ if (limits.maxSessionDuration !== void 0 && typeof limits.maxSessionDuration !== "number") {
176
+ fail("policy.limits.maxSessionDuration must be a number");
177
+ }
178
+ }
179
+ if (doc.enforcement !== void 0) {
180
+ if (typeof doc.enforcement !== "object" || doc.enforcement === null) {
181
+ fail("enforcement must be an object");
182
+ }
183
+ const enforcement = doc.enforcement;
184
+ if (enforcement.restrictAfter !== void 0 && typeof enforcement.restrictAfter !== "number") {
185
+ fail("enforcement.restrictAfter must be a number");
186
+ }
187
+ if (enforcement.quarantineAfter !== void 0 && typeof enforcement.quarantineAfter !== "number") {
188
+ fail("enforcement.quarantineAfter must be a number");
189
+ }
190
+ if (enforcement.approvalRequired !== void 0 && typeof enforcement.approvalRequired !== "boolean") {
191
+ fail("enforcement.approvalRequired must be a boolean");
192
+ }
193
+ if (enforcement.minKind !== void 0) {
194
+ if (typeof enforcement.minKind !== "string" || !VALID_KINDS.has(enforcement.minKind)) {
195
+ fail("enforcement.minKind must be one of: informational, actionable");
196
+ }
197
+ }
198
+ if (enforcement.promote !== void 0) {
199
+ if (!Array.isArray(enforcement.promote)) {
200
+ fail("enforcement.promote must be an array of finding type strings");
201
+ }
202
+ for (const pt of enforcement.promote) {
203
+ if (typeof pt !== "string") {
204
+ fail("enforcement.promote must be an array of finding type strings");
205
+ }
206
+ if (LOCKED_ACTIONABLE_TYPES.has(pt)) {
207
+ fail(
208
+ `enforcement.promote cannot include "${pt}" \u2014 it is a locked-actionable type that is always actionable`
209
+ );
210
+ }
211
+ }
212
+ }
213
+ if (enforcement.baselineMaturity !== void 0) {
214
+ if (typeof enforcement.baselineMaturity !== "object" || enforcement.baselineMaturity === null) {
215
+ fail("enforcement.baselineMaturity must be an object");
216
+ }
217
+ const bm = enforcement.baselineMaturity;
218
+ for (const key of ["minSessions", "minDaysObserved", "minCategoryDiversity"]) {
219
+ if (bm[key] !== void 0) {
220
+ if (typeof bm[key] !== "number" || !Number.isFinite(bm[key])) {
221
+ fail(`enforcement.baselineMaturity.${key} must be a finite number`);
222
+ }
223
+ if (bm[key] < 0) {
224
+ fail(`enforcement.baselineMaturity.${key} must not be negative`);
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ if (doc.alerts !== void 0) {
231
+ if (typeof doc.alerts !== "object" || doc.alerts === null) {
232
+ fail("alerts must be an object");
233
+ }
234
+ const alerts = doc.alerts;
235
+ if (!Array.isArray(alerts.channels) || alerts.channels.length === 0) {
236
+ fail("alerts.channels must be a non-empty array");
237
+ }
238
+ for (const ch of alerts.channels) {
239
+ if (typeof ch === "string") {
240
+ if (!VALID_CHANNELS.has(ch)) {
241
+ fail(`alerts.channels contains invalid channel "${ch}". Valid: console, webhook, file`);
242
+ }
243
+ } else if (typeof ch === "object" && ch !== null) {
244
+ const chObj = ch;
245
+ if (typeof chObj.type !== "string" || !VALID_CHANNELS.has(chObj.type)) {
246
+ fail(
247
+ `alerts.channels contains invalid channel type "${chObj.type}". Valid: console, webhook, file`
248
+ );
249
+ }
250
+ if (chObj.minKind !== void 0) {
251
+ if (typeof chObj.minKind !== "string" || !VALID_KINDS.has(chObj.minKind)) {
252
+ fail("alerts.channels[].minKind must be one of: informational, actionable");
253
+ }
254
+ }
255
+ } else {
256
+ fail("alerts.channels entries must be strings or objects with a type field");
257
+ }
258
+ }
259
+ if (alerts.webhookUrl !== void 0 && typeof alerts.webhookUrl !== "string") {
260
+ fail("alerts.webhookUrl must be a string");
261
+ }
262
+ if (alerts.filePath !== void 0 && typeof alerts.filePath !== "string") {
263
+ fail("alerts.filePath must be a string");
264
+ }
265
+ if (alerts.minSeverity !== void 0) {
266
+ if (typeof alerts.minSeverity !== "string" || !VALID_SEVERITIES.has(alerts.minSeverity)) {
267
+ fail("alerts.minSeverity must be one of: LOW, MEDIUM, HIGH, CRITICAL");
268
+ }
269
+ }
270
+ if (alerts.minKind !== void 0) {
271
+ if (typeof alerts.minKind !== "string" || !VALID_KINDS.has(alerts.minKind)) {
272
+ fail("alerts.minKind must be one of: informational, actionable");
273
+ }
274
+ }
275
+ }
276
+ if (doc.repo !== void 0) {
277
+ if (typeof doc.repo !== "object" || doc.repo === null) {
278
+ fail("repo must be an object");
279
+ }
280
+ const repo = doc.repo;
281
+ if (repo.root !== void 0) {
282
+ if (typeof repo.root !== "string" || repo.root.length === 0) {
283
+ fail("repo.root must be a non-empty string");
284
+ }
285
+ }
286
+ if (repo.scanOnStartup !== void 0) {
287
+ if (typeof repo.scanOnStartup !== "boolean") {
288
+ fail("repo.scanOnStartup must be a boolean");
289
+ }
290
+ }
291
+ if (repo.mapPath !== void 0) {
292
+ if (typeof repo.mapPath !== "string" || repo.mapPath.length === 0) {
293
+ fail("repo.mapPath must be a non-empty string");
294
+ }
295
+ }
296
+ if (repo.overlayPath !== void 0) {
297
+ if (typeof repo.overlayPath !== "string" || repo.overlayPath.length === 0) {
298
+ fail("repo.overlayPath must be a non-empty string");
299
+ }
300
+ }
301
+ }
302
+ return data;
303
+ }
304
+ async function loadPolicy(yamlPath) {
305
+ const { readFile } = await import("fs/promises");
306
+ let raw;
307
+ try {
308
+ raw = await readFile(yamlPath, "utf-8");
309
+ } catch (err) {
310
+ if (err.code === "ENOENT") {
311
+ const e = new Error(`Policy file not found: ${yamlPath}`);
312
+ e.cause = err;
313
+ throw e;
314
+ }
315
+ throw err;
316
+ }
317
+ return loadPolicyFromString(raw);
318
+ }
319
+ function loadPolicyFromString(yamlString) {
320
+ let parsed;
321
+ try {
322
+ parsed = yaml.load(yamlString);
323
+ } catch (err) {
324
+ const e = new Error(`Invalid YAML syntax: ${err.message}`);
325
+ e.cause = err;
326
+ throw e;
327
+ }
328
+ return validatePolicy(parsed);
329
+ }
330
+ function policyToRole(policy) {
331
+ const role = {
332
+ agentId: policy.agent.id,
333
+ name: policy.agent.name,
334
+ description: policy.agent.description ?? `Monitored agent: ${policy.agent.name}`,
335
+ allowedActions: policy.policy.allow.actions,
336
+ allowedTargetPatterns: policy.policy.allow.targets,
337
+ forbiddenTargetPatterns: policy.policy.forbid.targets
338
+ };
339
+ if (policy.policy.allow.networkHosts && policy.policy.allow.networkHosts.length > 0) {
340
+ role.networkHosts = policy.policy.allow.networkHosts;
341
+ }
342
+ if (policy.policy.exceptions && policy.policy.exceptions.length > 0) {
343
+ role.exceptions = policy.policy.exceptions.map((ex) => {
344
+ const exception = {
345
+ target: ex.target,
346
+ allowedActions: ex.allowedActions
347
+ };
348
+ if (ex.requiresTask !== void 0) exception.requiresTask = ex.requiresTask;
349
+ if (ex.requiresApproval !== void 0) exception.requiresApproval = ex.requiresApproval;
350
+ if (ex.expiresAfter !== void 0) exception.expiresAfter = ex.expiresAfter;
351
+ if (ex.downgradeKindTo !== void 0) exception.downgradeKindTo = ex.downgradeKindTo;
352
+ return exception;
353
+ });
354
+ }
355
+ if (policy.policy.schedule) {
356
+ role.expectedSchedule = {
357
+ activeHours: policy.policy.schedule.hours,
358
+ activeDays: policy.policy.schedule.days
359
+ };
360
+ }
361
+ if (policy.policy.limits) {
362
+ if (policy.policy.limits.maxEventsPerHour !== void 0) {
363
+ role.maxEventsPerHour = policy.policy.limits.maxEventsPerHour;
364
+ }
365
+ if (policy.policy.limits.maxSessionDuration !== void 0) {
366
+ role.maxSessionDuration = policy.policy.limits.maxSessionDuration;
367
+ }
368
+ }
369
+ return role;
370
+ }
371
+ function policyToConfig(policy) {
372
+ const config = {};
373
+ if (policy.enforcement) {
374
+ if (policy.enforcement.restrictAfter !== void 0 || policy.enforcement.quarantineAfter !== void 0 || policy.enforcement.promote !== void 0 || policy.enforcement.baselineMaturity !== void 0) {
375
+ config.enforcement = {};
376
+ if (policy.enforcement.restrictAfter !== void 0) {
377
+ config.enforcement.restrictAfter = policy.enforcement.restrictAfter;
378
+ }
379
+ if (policy.enforcement.quarantineAfter !== void 0) {
380
+ config.enforcement.quarantineAfter = policy.enforcement.quarantineAfter;
381
+ }
382
+ if (policy.enforcement.promote !== void 0) {
383
+ config.enforcement.promote = policy.enforcement.promote;
384
+ }
385
+ if (policy.enforcement.baselineMaturity !== void 0) {
386
+ config.enforcement.baselineMaturity = policy.enforcement.baselineMaturity;
387
+ }
388
+ }
389
+ }
390
+ if (policy.alerts) {
391
+ config.alerts = {
392
+ channels: policy.alerts.channels.map((ch) => {
393
+ const isObj = typeof ch === "object" && ch !== null;
394
+ const type = isObj ? ch.type : ch;
395
+ const perChannelMinKind = isObj ? ch.minKind : void 0;
396
+ return {
397
+ type,
398
+ name: type,
399
+ config: {
400
+ ...type === "webhook" && policy.alerts?.webhookUrl ? { url: policy.alerts.webhookUrl } : {},
401
+ ...type === "file" && policy.alerts?.filePath ? { filePath: policy.alerts.filePath } : {}
402
+ },
403
+ ...perChannelMinKind ? { minKind: perChannelMinKind } : {}
404
+ };
405
+ }),
406
+ minSeverity: policy.alerts.minSeverity,
407
+ ...policy.alerts.minKind ? { minKind: policy.alerts.minKind } : {}
408
+ };
409
+ }
410
+ if (policy.repo) {
411
+ config.repo = {};
412
+ if (policy.repo.root !== void 0) config.repo.root = policy.repo.root;
413
+ if (policy.repo.scanOnStartup !== void 0)
414
+ config.repo.scanOnStartup = policy.repo.scanOnStartup;
415
+ if (policy.repo.mapPath !== void 0) config.repo.mapPath = policy.repo.mapPath;
416
+ if (policy.repo.overlayPath !== void 0) config.repo.overlayPath = policy.repo.overlayPath;
417
+ }
418
+ return config;
419
+ }
420
+
421
+ export {
422
+ LOCKED_ACTIONABLE_TYPES,
423
+ loadPolicy,
424
+ loadPolicyFromString,
425
+ policyToRole,
426
+ policyToConfig
427
+ };
428
+ //# sourceMappingURL=chunk-2FFMYSVC.js.map