@armoriq/policy-ir 0.1.3 → 0.1.5
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/{document-entry-CDOQVeis.d.cts → document-entry-CsMP9dea.d.cts} +110 -3
- package/dist/{document-entry-CDOQVeis.d.ts → document-entry-CsMP9dea.d.ts} +110 -3
- package/dist/document.cjs +86 -15
- package/dist/document.cjs.map +1 -1
- package/dist/document.d.cts +1 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.js +85 -15
- package/dist/document.js.map +1 -1
- package/dist/index.cjs +104 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -44
- package/dist/index.d.ts +14 -44
- package/dist/index.js +103 -52
- package/dist/index.js.map +1 -1
- package/dist/vectors.cjs +27 -2
- package/dist/vectors.cjs.map +1 -1
- package/dist/vectors.js +27 -2
- package/dist/vectors.js.map +1 -1
- package/package.json +5 -6
- package/vectors/compile.json +9 -1
- package/vectors/evaluate.json +18 -1
|
@@ -40,6 +40,8 @@ interface Defaults {
|
|
|
40
40
|
network: Verdict;
|
|
41
41
|
files: Verdict;
|
|
42
42
|
skills: Verdict;
|
|
43
|
+
/** Who approves a hold from `tools.default`: the source's `spec.approvalDefaults`. */
|
|
44
|
+
approval?: Approval;
|
|
43
45
|
}
|
|
44
46
|
interface Capture {
|
|
45
47
|
arguments: boolean;
|
|
@@ -211,12 +213,35 @@ declare function parseYaml(text: string): ParseResult;
|
|
|
211
213
|
/** Parse an already-loaded YAML/JSON value strictly. */
|
|
212
214
|
declare function parseDocument(value: unknown): ParseResult;
|
|
213
215
|
|
|
216
|
+
/** One tool the target can reach, as the inventory knows it. */
|
|
217
|
+
interface InventoryTool {
|
|
218
|
+
/** The wire name the model sees; for an MCP tool the host may also supply the qualified `mcp__<server>__<tool>` form as `qualifiedName`. */
|
|
219
|
+
name: string;
|
|
220
|
+
qualifiedName?: string;
|
|
221
|
+
owner: {
|
|
222
|
+
type: 'mcp_server' | 'agent' | 'local';
|
|
223
|
+
id: string;
|
|
224
|
+
name?: string;
|
|
225
|
+
};
|
|
226
|
+
schemaHash: string | null;
|
|
227
|
+
inputSchema?: unknown;
|
|
228
|
+
capabilities?: string[];
|
|
229
|
+
/** False for a row the observed feed created and no owner has declared: the compiler warns instead of binding. */
|
|
230
|
+
declared: boolean;
|
|
231
|
+
}
|
|
232
|
+
/** What a host implements: conmap on `tool_snapshots`, the Armor CLI on the local tool list. */
|
|
233
|
+
interface InventoryPort {
|
|
234
|
+
toolsForTarget(target: SourceTarget): Promise<InventoryTool[]>;
|
|
235
|
+
}
|
|
236
|
+
declare const CAPABILITY_KEYS: ReadonlySet<string>;
|
|
237
|
+
|
|
214
238
|
/**
|
|
215
239
|
* Everything that can be checked without an inventory. Inventory-dependent
|
|
216
240
|
* checks (roster clamp, glob collisions on real tools, schema binding) live in
|
|
217
|
-
* the compiler.
|
|
241
|
+
* the compiler. An inventory, when given, lets the loosening check match a
|
|
242
|
+
* tool's bare and qualified names across the base and an overlay.
|
|
218
243
|
*/
|
|
219
|
-
declare function validate(source: PolicySource): Diagnostics;
|
|
244
|
+
declare function validate(source: PolicySource, inventory?: InventoryTool[]): Diagnostics;
|
|
220
245
|
|
|
221
246
|
/**
|
|
222
247
|
* PolicySource back to YAML such that `parse(serialize(x))` equals `x`
|
|
@@ -226,4 +251,86 @@ declare function validate(source: PolicySource): Diagnostics;
|
|
|
226
251
|
*/
|
|
227
252
|
declare function serialize(source: PolicySource): string;
|
|
228
253
|
|
|
229
|
-
|
|
254
|
+
type Enforceability = 'enforced' | 'argument-only' | 'inert';
|
|
255
|
+
type Section = 'tools' | 'files' | 'network' | 'skills' | 'secrets';
|
|
256
|
+
/**
|
|
257
|
+
* Per-section honesty (FR-9, FR-19): a local device sees real paths and
|
|
258
|
+
* hosts; the enterprise SDK path sees only path-shaped and URL-shaped
|
|
259
|
+
* arguments; skills and secrets have no enterprise surface at all.
|
|
260
|
+
*/
|
|
261
|
+
interface EnforceabilityReport {
|
|
262
|
+
sections: Partial<Record<Section, Enforceability>>;
|
|
263
|
+
/** Tools in the inventory that no capability rule reaches, when the document uses capability keys (M-12). */
|
|
264
|
+
notCoveredByCapabilityRules: string[];
|
|
265
|
+
}
|
|
266
|
+
declare function enforceabilityReport(source: PolicySource, inventory: InventoryTool[]): EnforceabilityReport;
|
|
267
|
+
|
|
268
|
+
declare const COMPOSE_INTENT_MAX = 4000;
|
|
269
|
+
interface ComposeTarget {
|
|
270
|
+
agent: string;
|
|
271
|
+
}
|
|
272
|
+
interface ComposeAnswer {
|
|
273
|
+
questionId: string;
|
|
274
|
+
/** The question as it was asked, since compose keeps no state between rounds. */
|
|
275
|
+
question: string;
|
|
276
|
+
text: string;
|
|
277
|
+
}
|
|
278
|
+
interface ComposeRequest {
|
|
279
|
+
target: ComposeTarget;
|
|
280
|
+
intent: string;
|
|
281
|
+
draft?: string;
|
|
282
|
+
answers?: ComposeAnswer[];
|
|
283
|
+
}
|
|
284
|
+
interface ComposeUnsupported {
|
|
285
|
+
sentence: string;
|
|
286
|
+
reason: string;
|
|
287
|
+
}
|
|
288
|
+
interface ComposeQuestion {
|
|
289
|
+
id: string;
|
|
290
|
+
text: string;
|
|
291
|
+
}
|
|
292
|
+
interface ComposeChangedCall {
|
|
293
|
+
auditId: string;
|
|
294
|
+
tool: string;
|
|
295
|
+
arguments: Record<string, unknown>;
|
|
296
|
+
executedAt: string;
|
|
297
|
+
from: Verdict;
|
|
298
|
+
to: Verdict;
|
|
299
|
+
}
|
|
300
|
+
interface ComposeReplay {
|
|
301
|
+
calls: number;
|
|
302
|
+
verdicts: Record<Verdict, number>;
|
|
303
|
+
changed: ComposeChangedCall[];
|
|
304
|
+
}
|
|
305
|
+
interface ComposeExample {
|
|
306
|
+
/** Same format as `Diagnostic.path`: `/spec/tools/rules/<key>/<variant>`. */
|
|
307
|
+
path: string;
|
|
308
|
+
call: {
|
|
309
|
+
tool: string;
|
|
310
|
+
qualifiedName?: string;
|
|
311
|
+
arguments: Record<string, unknown>;
|
|
312
|
+
};
|
|
313
|
+
verdict: Verdict;
|
|
314
|
+
}
|
|
315
|
+
interface ComposePreview {
|
|
316
|
+
replay: ComposeReplay | null;
|
|
317
|
+
examples: ComposeExample[];
|
|
318
|
+
}
|
|
319
|
+
interface ComposeResponse {
|
|
320
|
+
composeId: string;
|
|
321
|
+
document: PolicyDocument | null;
|
|
322
|
+
yaml: string;
|
|
323
|
+
diagnostics: Diagnostic[];
|
|
324
|
+
report: EnforceabilityReport;
|
|
325
|
+
unsupported: ComposeUnsupported[];
|
|
326
|
+
questions: ComposeQuestion[];
|
|
327
|
+
/** Source sentence per rule, keyed by `Diagnostic.path`: `/spec/tools/rules/<key>/<variant>`. */
|
|
328
|
+
sources: Record<string, string>;
|
|
329
|
+
preview: ComposePreview;
|
|
330
|
+
model: string;
|
|
331
|
+
rounds: number;
|
|
332
|
+
/** The model's one-paragraph explanation of the draft. */
|
|
333
|
+
reasoning?: string;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export { parseYaml as $, type Approval as A, SCHEMA_VERSION as B, type Condition as C, Diagnostics as D, type EnforceabilityReport as E, SOURCE_OPERATORS as F, type SchemaBinding as G, type Section as H, type InventoryTool as I, type Severity as J, type KeyTier as K, type Layer as L, type SourceOperator as M, NEVER as N, type Overlay as O, type PolicyDocument as P, type SourceTarget as Q, type RuleValue as R, type Statement as S, type StatementMatch as T, type Subject as U, type Verdict as V, type ToolsSection as W, VERDICTS as X, enforceabilityReport as Y, layerOfTarget as Z, parseDocument as _, type PolicySource as a, serialize as a0, validate as a1, type InventoryPort as b, CAPABILITY_KEYS as c, COMPOSE_INTENT_MAX as d, type Capture as e, type ComposeAnswer as f, type ComposeChangedCall as g, type ComposeExample as h, type ComposePreview as i, type ComposeQuestion as j, type ComposeReplay as k, type ComposeRequest as l, type ComposeResponse as m, type ComposeTarget as n, type ComposeUnsupported as o, type ConditionOp as p, type ConditionSpec as q, type Defaults as r, type Diagnostic as s, type Enforceability as t, type NetworkEntry as u, type ParseResult as v, type PolicySpec as w, type PolicyTarget as x, type Provenance as y, type RuleVariant as z };
|
|
@@ -40,6 +40,8 @@ interface Defaults {
|
|
|
40
40
|
network: Verdict;
|
|
41
41
|
files: Verdict;
|
|
42
42
|
skills: Verdict;
|
|
43
|
+
/** Who approves a hold from `tools.default`: the source's `spec.approvalDefaults`. */
|
|
44
|
+
approval?: Approval;
|
|
43
45
|
}
|
|
44
46
|
interface Capture {
|
|
45
47
|
arguments: boolean;
|
|
@@ -211,12 +213,35 @@ declare function parseYaml(text: string): ParseResult;
|
|
|
211
213
|
/** Parse an already-loaded YAML/JSON value strictly. */
|
|
212
214
|
declare function parseDocument(value: unknown): ParseResult;
|
|
213
215
|
|
|
216
|
+
/** One tool the target can reach, as the inventory knows it. */
|
|
217
|
+
interface InventoryTool {
|
|
218
|
+
/** The wire name the model sees; for an MCP tool the host may also supply the qualified `mcp__<server>__<tool>` form as `qualifiedName`. */
|
|
219
|
+
name: string;
|
|
220
|
+
qualifiedName?: string;
|
|
221
|
+
owner: {
|
|
222
|
+
type: 'mcp_server' | 'agent' | 'local';
|
|
223
|
+
id: string;
|
|
224
|
+
name?: string;
|
|
225
|
+
};
|
|
226
|
+
schemaHash: string | null;
|
|
227
|
+
inputSchema?: unknown;
|
|
228
|
+
capabilities?: string[];
|
|
229
|
+
/** False for a row the observed feed created and no owner has declared: the compiler warns instead of binding. */
|
|
230
|
+
declared: boolean;
|
|
231
|
+
}
|
|
232
|
+
/** What a host implements: conmap on `tool_snapshots`, the Armor CLI on the local tool list. */
|
|
233
|
+
interface InventoryPort {
|
|
234
|
+
toolsForTarget(target: SourceTarget): Promise<InventoryTool[]>;
|
|
235
|
+
}
|
|
236
|
+
declare const CAPABILITY_KEYS: ReadonlySet<string>;
|
|
237
|
+
|
|
214
238
|
/**
|
|
215
239
|
* Everything that can be checked without an inventory. Inventory-dependent
|
|
216
240
|
* checks (roster clamp, glob collisions on real tools, schema binding) live in
|
|
217
|
-
* the compiler.
|
|
241
|
+
* the compiler. An inventory, when given, lets the loosening check match a
|
|
242
|
+
* tool's bare and qualified names across the base and an overlay.
|
|
218
243
|
*/
|
|
219
|
-
declare function validate(source: PolicySource): Diagnostics;
|
|
244
|
+
declare function validate(source: PolicySource, inventory?: InventoryTool[]): Diagnostics;
|
|
220
245
|
|
|
221
246
|
/**
|
|
222
247
|
* PolicySource back to YAML such that `parse(serialize(x))` equals `x`
|
|
@@ -226,4 +251,86 @@ declare function validate(source: PolicySource): Diagnostics;
|
|
|
226
251
|
*/
|
|
227
252
|
declare function serialize(source: PolicySource): string;
|
|
228
253
|
|
|
229
|
-
|
|
254
|
+
type Enforceability = 'enforced' | 'argument-only' | 'inert';
|
|
255
|
+
type Section = 'tools' | 'files' | 'network' | 'skills' | 'secrets';
|
|
256
|
+
/**
|
|
257
|
+
* Per-section honesty (FR-9, FR-19): a local device sees real paths and
|
|
258
|
+
* hosts; the enterprise SDK path sees only path-shaped and URL-shaped
|
|
259
|
+
* arguments; skills and secrets have no enterprise surface at all.
|
|
260
|
+
*/
|
|
261
|
+
interface EnforceabilityReport {
|
|
262
|
+
sections: Partial<Record<Section, Enforceability>>;
|
|
263
|
+
/** Tools in the inventory that no capability rule reaches, when the document uses capability keys (M-12). */
|
|
264
|
+
notCoveredByCapabilityRules: string[];
|
|
265
|
+
}
|
|
266
|
+
declare function enforceabilityReport(source: PolicySource, inventory: InventoryTool[]): EnforceabilityReport;
|
|
267
|
+
|
|
268
|
+
declare const COMPOSE_INTENT_MAX = 4000;
|
|
269
|
+
interface ComposeTarget {
|
|
270
|
+
agent: string;
|
|
271
|
+
}
|
|
272
|
+
interface ComposeAnswer {
|
|
273
|
+
questionId: string;
|
|
274
|
+
/** The question as it was asked, since compose keeps no state between rounds. */
|
|
275
|
+
question: string;
|
|
276
|
+
text: string;
|
|
277
|
+
}
|
|
278
|
+
interface ComposeRequest {
|
|
279
|
+
target: ComposeTarget;
|
|
280
|
+
intent: string;
|
|
281
|
+
draft?: string;
|
|
282
|
+
answers?: ComposeAnswer[];
|
|
283
|
+
}
|
|
284
|
+
interface ComposeUnsupported {
|
|
285
|
+
sentence: string;
|
|
286
|
+
reason: string;
|
|
287
|
+
}
|
|
288
|
+
interface ComposeQuestion {
|
|
289
|
+
id: string;
|
|
290
|
+
text: string;
|
|
291
|
+
}
|
|
292
|
+
interface ComposeChangedCall {
|
|
293
|
+
auditId: string;
|
|
294
|
+
tool: string;
|
|
295
|
+
arguments: Record<string, unknown>;
|
|
296
|
+
executedAt: string;
|
|
297
|
+
from: Verdict;
|
|
298
|
+
to: Verdict;
|
|
299
|
+
}
|
|
300
|
+
interface ComposeReplay {
|
|
301
|
+
calls: number;
|
|
302
|
+
verdicts: Record<Verdict, number>;
|
|
303
|
+
changed: ComposeChangedCall[];
|
|
304
|
+
}
|
|
305
|
+
interface ComposeExample {
|
|
306
|
+
/** Same format as `Diagnostic.path`: `/spec/tools/rules/<key>/<variant>`. */
|
|
307
|
+
path: string;
|
|
308
|
+
call: {
|
|
309
|
+
tool: string;
|
|
310
|
+
qualifiedName?: string;
|
|
311
|
+
arguments: Record<string, unknown>;
|
|
312
|
+
};
|
|
313
|
+
verdict: Verdict;
|
|
314
|
+
}
|
|
315
|
+
interface ComposePreview {
|
|
316
|
+
replay: ComposeReplay | null;
|
|
317
|
+
examples: ComposeExample[];
|
|
318
|
+
}
|
|
319
|
+
interface ComposeResponse {
|
|
320
|
+
composeId: string;
|
|
321
|
+
document: PolicyDocument | null;
|
|
322
|
+
yaml: string;
|
|
323
|
+
diagnostics: Diagnostic[];
|
|
324
|
+
report: EnforceabilityReport;
|
|
325
|
+
unsupported: ComposeUnsupported[];
|
|
326
|
+
questions: ComposeQuestion[];
|
|
327
|
+
/** Source sentence per rule, keyed by `Diagnostic.path`: `/spec/tools/rules/<key>/<variant>`. */
|
|
328
|
+
sources: Record<string, string>;
|
|
329
|
+
preview: ComposePreview;
|
|
330
|
+
model: string;
|
|
331
|
+
rounds: number;
|
|
332
|
+
/** The model's one-paragraph explanation of the draft. */
|
|
333
|
+
reasoning?: string;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export { parseYaml as $, type Approval as A, SCHEMA_VERSION as B, type Condition as C, Diagnostics as D, type EnforceabilityReport as E, SOURCE_OPERATORS as F, type SchemaBinding as G, type Section as H, type InventoryTool as I, type Severity as J, type KeyTier as K, type Layer as L, type SourceOperator as M, NEVER as N, type Overlay as O, type PolicyDocument as P, type SourceTarget as Q, type RuleValue as R, type Statement as S, type StatementMatch as T, type Subject as U, type Verdict as V, type ToolsSection as W, VERDICTS as X, enforceabilityReport as Y, layerOfTarget as Z, parseDocument as _, type PolicySource as a, serialize as a0, validate as a1, type InventoryPort as b, CAPABILITY_KEYS as c, COMPOSE_INTENT_MAX as d, type Capture as e, type ComposeAnswer as f, type ComposeChangedCall as g, type ComposeExample as h, type ComposePreview as i, type ComposeQuestion as j, type ComposeReplay as k, type ComposeRequest as l, type ComposeResponse as m, type ComposeTarget as n, type ComposeUnsupported as o, type ConditionOp as p, type ConditionSpec as q, type Defaults as r, type Diagnostic as s, type Enforceability as t, type NetworkEntry as u, type ParseResult as v, type PolicySpec as w, type PolicyTarget as x, type Provenance as y, type RuleVariant as z };
|
package/dist/document.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/document-entry.ts
|
|
21
21
|
var document_entry_exports = {};
|
|
22
22
|
__export(document_entry_exports, {
|
|
23
|
+
COMPOSE_INTENT_MAX: () => COMPOSE_INTENT_MAX,
|
|
23
24
|
Diagnostics: () => Diagnostics,
|
|
24
25
|
NEVER: () => NEVER,
|
|
25
26
|
SOURCE_OPERATORS: () => SOURCE_OPERATORS,
|
|
@@ -437,6 +438,81 @@ function parseCapture(r, value) {
|
|
|
437
438
|
// src/evaluate/input.ts
|
|
438
439
|
var VERDICT_RANK = { allow: 0, hold: 1, block: 2 };
|
|
439
440
|
|
|
441
|
+
// src/evaluate/glob.ts
|
|
442
|
+
function globToRegExp(pattern) {
|
|
443
|
+
let source = "^";
|
|
444
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
445
|
+
const ch = pattern[i];
|
|
446
|
+
if (ch === "*") {
|
|
447
|
+
const double = pattern[i + 1] === "*";
|
|
448
|
+
source += double ? ".*" : "[^/]*";
|
|
449
|
+
if (double) i++;
|
|
450
|
+
} else if (ch === "?") {
|
|
451
|
+
source += "[^/]";
|
|
452
|
+
} else {
|
|
453
|
+
source += escapeRegExp(ch);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return new RegExp(source + "$");
|
|
457
|
+
}
|
|
458
|
+
function matchGlob(pattern, value) {
|
|
459
|
+
return globToRegExp(pattern).test(value);
|
|
460
|
+
}
|
|
461
|
+
function isGlob(key) {
|
|
462
|
+
return key.includes("*") || key.includes("?");
|
|
463
|
+
}
|
|
464
|
+
function escapeRegExp(ch) {
|
|
465
|
+
return /[\\^$.|?*+()[\]{}]/.test(ch) ? "\\" + ch : ch;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/evaluate/select.ts
|
|
469
|
+
function selectKey(keys, names) {
|
|
470
|
+
let best = null;
|
|
471
|
+
for (const key of keys) {
|
|
472
|
+
if (!names.some((name) => keyMatches(key, name))) continue;
|
|
473
|
+
if (best === null || keyBeats(key, best)) best = key;
|
|
474
|
+
}
|
|
475
|
+
return best;
|
|
476
|
+
}
|
|
477
|
+
function keyMatches(key, tool) {
|
|
478
|
+
return isGlob(key) ? matchGlob(key, tool) : key === tool;
|
|
479
|
+
}
|
|
480
|
+
function keyBeats(candidate, incumbent) {
|
|
481
|
+
const candidateGlob = isGlob(candidate);
|
|
482
|
+
const incumbentGlob = isGlob(incumbent);
|
|
483
|
+
if (candidateGlob !== incumbentGlob) return !candidateGlob;
|
|
484
|
+
return candidate.length > incumbent.length;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/document/loosening.ts
|
|
488
|
+
function looseningsOf(base, overlay, inventory) {
|
|
489
|
+
const baseKeys = Object.keys(base.rules ?? {});
|
|
490
|
+
const overlayKeys = Object.keys(overlay);
|
|
491
|
+
const out = [];
|
|
492
|
+
for (const key of overlayKeys) {
|
|
493
|
+
const verdict = loosest(overlay[key]);
|
|
494
|
+
const bases = [baseVerdict(base, baseKeys.includes(key) ? key : null)];
|
|
495
|
+
for (const tool of inventory) {
|
|
496
|
+
const names = tool.qualifiedName === void 0 ? [tool.name] : [tool.name, tool.qualifiedName];
|
|
497
|
+
if (selectKey(overlayKeys, names) === key) bases.push(baseVerdict(base, selectKey(baseKeys, names)));
|
|
498
|
+
}
|
|
499
|
+
const strictest = bases.reduce((a, b) => looser(a, b) ? b : a);
|
|
500
|
+
if (looser(verdict, strictest)) out.push({ key, verdict, base: strictest });
|
|
501
|
+
}
|
|
502
|
+
return out;
|
|
503
|
+
}
|
|
504
|
+
function baseVerdict(base, key) {
|
|
505
|
+
const variants = key === null ? void 0 : base.rules?.[key];
|
|
506
|
+
if (!variants) return base.default;
|
|
507
|
+
return variants.reduce((v, x) => looser(v, x.action) ? x.action : v, "allow");
|
|
508
|
+
}
|
|
509
|
+
function loosest(variants) {
|
|
510
|
+
return variants.reduce((v, x) => looser(x.action, v) ? x.action : v, "block");
|
|
511
|
+
}
|
|
512
|
+
function looser(a, b) {
|
|
513
|
+
return VERDICT_RANK[a] < VERDICT_RANK[b];
|
|
514
|
+
}
|
|
515
|
+
|
|
440
516
|
// src/document/portable-regex.ts
|
|
441
517
|
function isPortablePattern(pattern) {
|
|
442
518
|
if (typeof pattern !== "string") return false;
|
|
@@ -551,9 +627,9 @@ function isSurrogate(c) {
|
|
|
551
627
|
}
|
|
552
628
|
|
|
553
629
|
// src/document/validate.ts
|
|
554
|
-
function validate(source) {
|
|
630
|
+
function validate(source, inventory = []) {
|
|
555
631
|
const diag = new Diagnostics();
|
|
556
|
-
const ctx = { diag, approvalDefaults: source.spec.approvalDefaults, base: source.spec.tools };
|
|
632
|
+
const ctx = { diag, approvalDefaults: source.spec.approvalDefaults, base: source.spec.tools, inventory };
|
|
557
633
|
validateTargetSections(ctx, source);
|
|
558
634
|
if (source.spec.approvalDefaults) validateApproval(ctx, source.spec.approvalDefaults, "/spec/approvalDefaults", true);
|
|
559
635
|
validateRules(ctx, source.spec.tools.rules, "/spec/tools/rules");
|
|
@@ -646,23 +722,14 @@ function validateOverlays(ctx, overlays2, path) {
|
|
|
646
722
|
}
|
|
647
723
|
function flagLoosening(ctx, overlay, path) {
|
|
648
724
|
const baseDefault = ctx.base.default;
|
|
649
|
-
if (overlay.tools?.default &&
|
|
725
|
+
if (overlay.tools?.default && looser2(overlay.tools.default, baseDefault)) {
|
|
650
726
|
ctx.diag.warning("loosening_grant", `${path}/tools/default`, `default ${overlay.tools.default} is looser than the base default ${baseDefault}`);
|
|
651
727
|
}
|
|
652
|
-
for (const
|
|
653
|
-
|
|
654
|
-
const loosest = variants.reduce((v, x) => looser(x.action, v) ? x.action : v, "block");
|
|
655
|
-
if (looser(loosest, base)) {
|
|
656
|
-
ctx.diag.warning("loosening_grant", `${path}/tools/rules/${key}`, `${key}: ${loosest} is looser than the base ${base}`);
|
|
657
|
-
}
|
|
728
|
+
for (const { key, verdict, base } of looseningsOf(ctx.base, overlay.tools?.rules ?? {}, ctx.inventory)) {
|
|
729
|
+
ctx.diag.warning("loosening_grant", `${path}/tools/rules/${key}`, `${key}: ${verdict} is looser than the base ${base}`);
|
|
658
730
|
}
|
|
659
731
|
}
|
|
660
|
-
function
|
|
661
|
-
const variants = base.rules?.[key];
|
|
662
|
-
if (!variants) return base.default;
|
|
663
|
-
return variants.reduce((v, x) => looser(v, x.action) ? x.action : v, "allow");
|
|
664
|
-
}
|
|
665
|
-
function looser(a, b) {
|
|
732
|
+
function looser2(a, b) {
|
|
666
733
|
return VERDICT_RANK[a] < VERDICT_RANK[b];
|
|
667
734
|
}
|
|
668
735
|
function validateCapture(ctx, source) {
|
|
@@ -708,8 +775,12 @@ function overlays(map) {
|
|
|
708
775
|
}
|
|
709
776
|
return out;
|
|
710
777
|
}
|
|
778
|
+
|
|
779
|
+
// src/compose.ts
|
|
780
|
+
var COMPOSE_INTENT_MAX = 4e3;
|
|
711
781
|
// Annotate the CommonJS export names for ESM import in node:
|
|
712
782
|
0 && (module.exports = {
|
|
783
|
+
COMPOSE_INTENT_MAX,
|
|
713
784
|
Diagnostics,
|
|
714
785
|
NEVER,
|
|
715
786
|
SOURCE_OPERATORS,
|