@intutic/gate 2.1.0 → 2.3.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/README.md +29 -12
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +20 -6
- package/dist/actions.js.map +1 -1
- package/dist/client.d.ts +19 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +48 -4
- package/dist/client.js.map +1 -1
- package/dist/dsh.d.ts +1 -1
- package/dist/dsh.js +1 -1
- package/dist/errors.d.ts +25 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -1
- package/dist/eve.d.ts +11 -11
- package/dist/eve.d.ts.map +1 -1
- package/dist/eve.js +10 -10
- package/dist/eve.js.map +1 -1
- package/dist/gate.d.ts +34 -1
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +135 -9
- package/dist/gate.js.map +1 -1
- package/dist/harness.d.ts +28 -20
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +165 -26
- package/dist/harness.js.map +1 -1
- package/dist/hold.d.ts +69 -0
- package/dist/hold.d.ts.map +1 -0
- package/dist/hold.js +127 -0
- package/dist/hold.js.map +1 -0
- package/dist/imagecheck.d.ts +14 -7
- package/dist/imagecheck.d.ts.map +1 -1
- package/dist/imagecheck.js +3 -3
- package/dist/imagecheck.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +26 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +45 -0
- package/dist/limits.js.map +1 -0
- package/dist/managedAgents.d.ts +3 -3
- package/dist/managedAgents.d.ts.map +1 -1
- package/dist/managedAgents.js +2 -2
- package/dist/mcpRegistryRecord.d.ts +143 -0
- package/dist/mcpRegistryRecord.d.ts.map +1 -0
- package/dist/mcpRegistryRecord.js +217 -0
- package/dist/mcpRegistryRecord.js.map +1 -0
- package/dist/openai.d.ts +3 -3
- package/dist/openai.d.ts.map +1 -1
- package/dist/openai.js +9 -8
- package/dist/openai.js.map +1 -1
- package/dist/phrases.d.ts +87 -0
- package/dist/phrases.d.ts.map +1 -0
- package/dist/phrases.js +236 -0
- package/dist/phrases.js.map +1 -0
- package/dist/sequence.d.ts +52 -0
- package/dist/sequence.d.ts.map +1 -0
- package/dist/sequence.js +121 -0
- package/dist/sequence.js.map +1 -0
- package/dist/snapshot.d.ts +58 -36
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +106 -48
- package/dist/snapshot.js.map +1 -1
- package/dist/ssoGroups.d.ts +50 -0
- package/dist/ssoGroups.d.ts.map +1 -0
- package/dist/ssoGroups.js +101 -0
- package/dist/ssoGroups.js.map +1 -0
- package/dist/workflow.d.ts +5 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +8 -4
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
package/dist/harness.js
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
* existing bypass-permissions behavior unless users opt in." Under
|
|
48
48
|
* `'allow-all'` nothing pauses. Under the stricter modes the mode decides
|
|
49
49
|
* WHICH built-ins pause, and each paused call is then a genuine per-call
|
|
50
|
-
* approval this gate answers
|
|
50
|
+
* approval this gate answers: the
|
|
51
51
|
* `@ai-sdk/harness-claude-code` bridge (`dist/bridge/index.mjs`,
|
|
52
52
|
* `createPermissionSettings`/`nativeToolRequiresApproval`) adds `ask` rules
|
|
53
53
|
* for bash-kind tools (`Bash`, `Monitor`) under `'allow-edits'`, and for
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
import { createHash } from 'node:crypto';
|
|
85
85
|
import { active as activeGate } from './gate.js';
|
|
86
86
|
import { IntuticGateRefusal } from './errors.js';
|
|
87
|
+
import { MCP_ALLOWLIST_JS_SOURCE, MCP_REGISTRY_JS_SOURCE, MCP_SNAPSHOT_UNVERIFIED_JS_SOURCE } from './mcpRegistryRecord.js';
|
|
88
|
+
import { PHRASES_JS_SOURCE } from './phrases.js';
|
|
89
|
+
import { SEQUENCE_JS_SOURCE } from './sequence.js';
|
|
90
|
+
import { ARGUMENTS_SIZE_LIMIT, COMMAND_SIZE_LIMIT, GATE_DEADLINE_MS, HOOK_TIMEOUT_SECONDS } from './limits.js';
|
|
87
91
|
/**
|
|
88
92
|
* Render a pending approval's `input` as the `tool_input` object
|
|
89
93
|
* {@link Gate.guard} evaluates.
|
|
@@ -328,11 +332,19 @@ function sandboxJoin(workDir, relative) {
|
|
|
328
332
|
return `${base}/${relative}`;
|
|
329
333
|
}
|
|
330
334
|
/**
|
|
331
|
-
* Self-contained (
|
|
332
|
-
* `normalise`/`evaluate`
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
335
|
+
* Self-contained (built-ins only, no npm install) hand-port of `snapshot.ts`'s
|
|
336
|
+
* `loadSnapshot`/`normalise`/`evaluate` and of `gate.ts`'s Tier M — the
|
|
337
|
+
* policy-snapshot rules and the MCP server registry and allowlist. See the
|
|
338
|
+
* module-level comment above for what this deliberately does and does not
|
|
339
|
+
* cover, and `__tests__/harness.test.ts` for the behavioural-parity check
|
|
340
|
+
* against the real `snapshot.evaluate()` this port is pinned to and the
|
|
341
|
+
* shared MCP registry vectors.
|
|
342
|
+
*
|
|
343
|
+
* The rules text is checked as every gate checks it: a missing or broken
|
|
344
|
+
* `#digest`, or a `#workspace` other than `workspaceId`, leaves only its
|
|
345
|
+
* SSO-group refusals, and refuses every MCP call (`POLICY_SNAPSHOT_UNVERIFIED`)
|
|
346
|
+
* because neither MCP record can be vouched for. An empty rules text is no
|
|
347
|
+
* snapshot at all, as an absent file is.
|
|
336
348
|
*
|
|
337
349
|
* Contract matches the laptop's `claude-code-check.js`
|
|
338
350
|
* (`services/sync-daemon/src/harness/claudeCodeHooks.ts`) deliberately:
|
|
@@ -342,91 +354,218 @@ function sandboxJoin(workDir, relative) {
|
|
|
342
354
|
* any parse/read error fails CLOSED (exit `2`), the same posture
|
|
343
355
|
* `emitJsFailClosedPrelude` documents for the laptop's version.
|
|
344
356
|
*/
|
|
345
|
-
function renderSandboxGateScript(rulesFileName) {
|
|
357
|
+
function renderSandboxGateScript(rulesFileName, workspaceId = '') {
|
|
346
358
|
return `#!/usr/bin/env node
|
|
347
359
|
'use strict';
|
|
348
360
|
/**
|
|
349
|
-
* Intutic sandbox PreToolUse gate (
|
|
361
|
+
* Intutic sandbox PreToolUse gate (policy-snapshot rules, and the MCP server
|
|
362
|
+
* registry and allowlist the snapshot carries).
|
|
350
363
|
* Auto-generated by @intutic/gate's intuticSandboxBootstrap(). DO NOT EDIT.
|
|
351
364
|
*
|
|
352
365
|
* Unlike the laptop's claude-code-check.js, this script has no live
|
|
353
366
|
* control-plane connection at call time (the recommended sandbox network
|
|
354
367
|
* policy is deny-all) and therefore implements NEITHER the SOP tier (A3)
|
|
355
|
-
* NOR review-hold parking NOR governance-event draining.
|
|
368
|
+
* NOR review-hold parking NOR governance-event draining.
|
|
356
369
|
*/
|
|
357
370
|
const fs = require('fs');
|
|
358
371
|
const path = require('path');
|
|
372
|
+
const crypto = require('crypto');
|
|
359
373
|
|
|
360
374
|
const RULES_PATH = path.join(__dirname, ${JSON.stringify(rulesFileName)});
|
|
375
|
+
const WORKSPACE_ID = ${JSON.stringify(workspaceId)};
|
|
361
376
|
|
|
362
377
|
function normalise(v) {
|
|
363
378
|
const s = v === null || v === undefined ? '' : String(v);
|
|
364
379
|
return ' ' + s.replace(/\\s+/g, ' ').trim() + ' ';
|
|
365
380
|
}
|
|
366
381
|
|
|
367
|
-
function
|
|
382
|
+
function stringList(v) {
|
|
383
|
+
return Array.isArray(v) ? v.filter(function (x) { return typeof x === 'string'; }) : [];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** The snapshot: its rules, MCP records and whether it verified. */
|
|
387
|
+
function loadSnapshot() {
|
|
388
|
+
const out = { rules: [], state: 'absent', mcpRegistry: null, mcpAllowlist: null };
|
|
368
389
|
let text;
|
|
369
390
|
try {
|
|
370
391
|
text = fs.readFileSync(RULES_PATH, 'utf-8');
|
|
371
392
|
} catch (e) {
|
|
372
|
-
return
|
|
393
|
+
return out;
|
|
373
394
|
}
|
|
374
|
-
|
|
395
|
+
// Written empty when the bootstrap was given no rules: no snapshot, not a
|
|
396
|
+
// damaged one.
|
|
397
|
+
if (!text.trim()) return out;
|
|
398
|
+
out.state = 'ok';
|
|
399
|
+
let digest = '', workspace = '';
|
|
400
|
+
const body = [];
|
|
375
401
|
for (const line of text.split('\\n')) {
|
|
402
|
+
if (line.indexOf('#digest ') === 0) { digest = line.slice(8).trim(); continue; }
|
|
403
|
+
if (line.indexOf('#workspace ') === 0) { workspace = line.slice(11).trim(); continue; }
|
|
376
404
|
if (!line || line.charAt(0) === '#') continue;
|
|
405
|
+
body.push(line);
|
|
377
406
|
const f = line.split('\\t');
|
|
407
|
+
if (f[0] === '@mcp_registry' && f.length === 2) {
|
|
408
|
+
try {
|
|
409
|
+
const r = JSON.parse(Buffer.from(f[1], 'base64').toString('utf8'));
|
|
410
|
+
const disabled = {};
|
|
411
|
+
if (r.disabledTools && typeof r.disabledTools === 'object') {
|
|
412
|
+
for (const k of Object.keys(r.disabledTools)) disabled[k] = stringList(r.disabledTools[k]);
|
|
413
|
+
}
|
|
414
|
+
out.mcpRegistry = { defaultPolicy: r.defaultPolicy === 'deny' ? 'deny' : 'allow',
|
|
415
|
+
approvedServers: stringList(r.approvedServers), blockedServers: stringList(r.blockedServers),
|
|
416
|
+
heldServers: stringList(r.heldServers), disabledTools: disabled };
|
|
417
|
+
} catch (e) { /* a damaged record is no registry */ }
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (f[0] === '@mcp_allowlist' && f.length === 3) {
|
|
421
|
+
out.mcpAllowlist = { severity: f[1] === 'shadow' ? 'shadow' : 'block', servers: f[2].split(',').filter(Boolean) };
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
// Column order: id, severity, flags, subject, reason, source(regex).
|
|
378
425
|
if (f.length < 6 || !f[5]) continue;
|
|
426
|
+
// Flags: i = case-insensitive, s = a sequence rule (sequence.ts).
|
|
427
|
+
const ic = f[2].indexOf('i') !== -1;
|
|
379
428
|
try {
|
|
380
|
-
rules.push({
|
|
429
|
+
out.rules.push({
|
|
381
430
|
id: f[0],
|
|
382
431
|
severity: f[1],
|
|
383
432
|
subject: f[3] || 'any',
|
|
384
433
|
reason: f[4],
|
|
385
|
-
pattern: new RegExp(f[5],
|
|
434
|
+
pattern: new RegExp(f[5], ic ? 'i' : ''),
|
|
435
|
+
steps: f[2].indexOf('s') !== -1 ? compileSequence(f[5], ic) : null,
|
|
386
436
|
});
|
|
387
437
|
} catch (e) {
|
|
388
438
|
// Regex would not compile — dropped, not fatal. Matches snapshot.ts.
|
|
389
439
|
}
|
|
390
440
|
}
|
|
391
|
-
|
|
441
|
+
// Integrity, as snapshot.ts checks it: the digest over the data lines (a
|
|
442
|
+
// missing one fails too), and the workspace the rules were issued to.
|
|
443
|
+
const actual = crypto.createHash('sha256').update(body.join('\\n'), 'utf-8').digest('hex').slice(0, 32);
|
|
444
|
+
if (actual !== digest || (workspace && WORKSPACE_ID && workspace !== WORKSPACE_ID)) {
|
|
445
|
+
out.state = 'invalid';
|
|
446
|
+
// Only the SSO-group refusals survive, and neither MCP record does.
|
|
447
|
+
out.rules = out.rules.filter(function (r) { return r.id.indexOf('sso_group.') === 0 && r.severity === 'block'; });
|
|
448
|
+
out.mcpRegistry = null;
|
|
449
|
+
out.mcpAllowlist = null;
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
392
452
|
}
|
|
393
453
|
|
|
454
|
+
// The phrase matcher snapshot.ts uses, emitted from phrases.ts: a \`phrase\`
|
|
455
|
+
// rule's source is |-separated phrases matched as words, not a regex.
|
|
456
|
+
${PHRASES_JS_SOURCE}
|
|
457
|
+
|
|
458
|
+
// The sequence-rule matcher snapshot.ts uses, emitted from sequence.ts: a rule
|
|
459
|
+
// flagged \`s\` has its steps searched for in order, in linear time.
|
|
460
|
+
${SEQUENCE_JS_SOURCE}
|
|
461
|
+
|
|
462
|
+
// The MCP server registry and allowlist decisions, and the refusal on an
|
|
463
|
+
// unverified snapshot, emitted from mcpRegistryRecord.ts.
|
|
464
|
+
${MCP_REGISTRY_JS_SOURCE}
|
|
465
|
+
|
|
466
|
+
${MCP_ALLOWLIST_JS_SOURCE}
|
|
467
|
+
|
|
468
|
+
${MCP_SNAPSHOT_UNVERIFIED_JS_SOURCE}
|
|
469
|
+
|
|
394
470
|
function evaluate(toolName, target, command, rules) {
|
|
395
471
|
const nTool = normalise(toolName);
|
|
396
472
|
const nCommand = normalise(command);
|
|
397
473
|
const nTarget = normalise(target);
|
|
474
|
+
let words = null;
|
|
398
475
|
for (const rule of rules) {
|
|
476
|
+
if (rule.subject === 'phrase') {
|
|
477
|
+
if (words === null) words = phraseText(command);
|
|
478
|
+
const w = words;
|
|
479
|
+
if (!rule.pattern.source.split('|').some(function (p) { return hasPhrase(w, p, true); })) continue;
|
|
480
|
+
return { severity: rule.severity, reason: rule.reason + ' [' + rule.id + ']' };
|
|
481
|
+
}
|
|
399
482
|
const subjects =
|
|
400
483
|
rule.subject === 'tool' ? [nTool] :
|
|
401
484
|
rule.subject === 'command' ? [nCommand] :
|
|
402
485
|
rule.subject === 'target' ? [nTarget] :
|
|
403
486
|
[nCommand, nTarget];
|
|
404
487
|
for (const subject of subjects) {
|
|
405
|
-
if (!rule.pattern.test(subject)) continue;
|
|
488
|
+
if (!(rule.steps ? sequenceMatch(rule.steps, subject) : rule.pattern.test(subject))) continue;
|
|
406
489
|
return { severity: rule.severity, reason: rule.reason + ' [' + rule.id + ']' };
|
|
407
490
|
}
|
|
408
491
|
}
|
|
409
492
|
return null;
|
|
410
493
|
}
|
|
411
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Tier M, for an mcp__<server>__<tool> call: on an unverified snapshot every
|
|
497
|
+
* call is refused; otherwise the registry, then the allowlist, whose refusal
|
|
498
|
+
* under shadow (an observe-only workspace) is reported and allowed.
|
|
499
|
+
*/
|
|
500
|
+
function evaluateMcp(toolName, snap) {
|
|
501
|
+
if (toolName.indexOf('mcp__') !== 0) return null;
|
|
502
|
+
const rest = toolName.slice('mcp__'.length);
|
|
503
|
+
const sep = rest.indexOf('__');
|
|
504
|
+
if (sep <= 0) return null;
|
|
505
|
+
const server = rest.slice(0, sep);
|
|
506
|
+
if (snap.state === 'invalid') {
|
|
507
|
+
const u = mcpSnapshotUnverifiedRefusal(server);
|
|
508
|
+
return { severity: 'block', reason: u.reason + ' [' + u.ruleId + ']', code: u.code };
|
|
509
|
+
}
|
|
510
|
+
const r = snap.mcpRegistry ? evaluateMcpRegistry(snap.mcpRegistry, server, rest.slice(sep + 2)) : null;
|
|
511
|
+
if (r) return { severity: 'block', reason: r.reason + ' [' + r.ruleId + ']', code: r.code };
|
|
512
|
+
const a = snap.mcpAllowlist ? evaluateMcpAllowlist(snap.mcpAllowlist, server) : null;
|
|
513
|
+
if (a) return { severity: snap.mcpAllowlist.severity === 'shadow' ? 'shadow' : 'block', reason: a.reason + ' [' + a.ruleId + ']', code: a.code };
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
|
|
412
517
|
let inputData = '';
|
|
413
518
|
process.stdin.on('data', (chunk) => { inputData += chunk; });
|
|
414
519
|
process.stdin.on('end', () => {
|
|
415
520
|
try {
|
|
416
521
|
const ctx = JSON.parse(inputData || '{}');
|
|
417
|
-
const toolName = ctx.tool_name || ctx.toolName || '';
|
|
522
|
+
const toolName = String(ctx.tool_name || ctx.toolName || '');
|
|
418
523
|
const toolInput = ctx.tool_input || ctx.toolInput || {};
|
|
419
524
|
const target = toolInput.path || toolInput.file_path || toolInput.filePath ||
|
|
420
525
|
toolInput.new_path || toolInput.target || toolInput.notebook_path || '';
|
|
421
526
|
const command = String(toolInput.command || toolInput.cmd || toolInput.script || '');
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
527
|
+
// Too large to evaluate inside the hook timeout: refused before any rule
|
|
528
|
+
// runs (limits.ts). Claude Code reads a hook that times out as an allow.
|
|
529
|
+
const commandBytes = Buffer.byteLength(command, 'utf8');
|
|
530
|
+
const argumentBytes = Buffer.byteLength(JSON.stringify(toolInput) || '', 'utf8');
|
|
531
|
+
if (commandBytes > ${COMMAND_SIZE_LIMIT} || argumentBytes > ${ARGUMENTS_SIZE_LIMIT}) {
|
|
532
|
+
console.error('[Intutic Guardrail] BLOCKED: COMMAND_TOO_LARGE: the ' +
|
|
533
|
+
(commandBytes > ${COMMAND_SIZE_LIMIT} ? 'command is ' + commandBytes : 'tool arguments are ' + argumentBytes) +
|
|
534
|
+
' bytes, over the ' + (commandBytes > ${COMMAND_SIZE_LIMIT} ? ${COMMAND_SIZE_LIMIT} : ${ARGUMENTS_SIZE_LIMIT}) +
|
|
535
|
+
'-byte limit a gate evaluates; split the work into smaller calls');
|
|
536
|
+
process.exit(2);
|
|
537
|
+
}
|
|
538
|
+
const snap = loadSnapshot();
|
|
539
|
+
// The rules run under a deadline that interrupts even a regex mid-match,
|
|
540
|
+
// measured from process start, and a call still undecided then is refused:
|
|
541
|
+
// a snapshot rule written in a workspace need not be linear.
|
|
542
|
+
let decision;
|
|
543
|
+
globalThis.__intuticEvaluate = function () {
|
|
544
|
+
return evaluate(toolName, target, command, snap.rules) || evaluateMcp(toolName, snap);
|
|
545
|
+
};
|
|
546
|
+
try {
|
|
547
|
+
decision = require('vm').runInThisContext('__intuticEvaluate()', {
|
|
548
|
+
timeout: Math.max(1, ${GATE_DEADLINE_MS} - Math.round(process.uptime() * 1000)),
|
|
549
|
+
});
|
|
550
|
+
} catch (err) {
|
|
551
|
+
if (!err || err.code !== 'ERR_SCRIPT_EXECUTION_TIMEOUT') throw err;
|
|
552
|
+
console.error('[Intutic Guardrail] BLOCKED: GATE_DEADLINE: the policy rules did not finish within ' +
|
|
553
|
+
${GATE_DEADLINE_MS} + ' ms, so the call is refused rather than left to the hook timeout');
|
|
426
554
|
process.exit(2);
|
|
427
555
|
}
|
|
428
556
|
if (decision && (decision.severity === 'warn' || decision.severity === 'shadow')) {
|
|
429
557
|
console.error('[Intutic Guardrail] FLAGGED (' + decision.severity + '): ' + decision.reason);
|
|
558
|
+
} else if (decision && decision.severity === 'hold') {
|
|
559
|
+
// A hold asks a person first, and this script cannot reach the control
|
|
560
|
+
// plane to ask: it refuses, as every gate does with a hold it cannot record.
|
|
561
|
+
console.error('[Intutic Guardrail] HELD for approval: ' + decision.reason +
|
|
562
|
+
' — approval cannot be requested from inside this sandbox, so the call is refused.');
|
|
563
|
+
process.exit(2);
|
|
564
|
+
} else if (decision) {
|
|
565
|
+
// block, or a severity this script does not know: never an allow. An
|
|
566
|
+
// MCP refusal names its code, as the other gates' refusals do.
|
|
567
|
+
console.error('[Intutic Guardrail] BLOCKED: ' + (decision.code ? decision.code + ': ' : '') + decision.reason);
|
|
568
|
+
process.exit(2);
|
|
430
569
|
}
|
|
431
570
|
process.exit(0);
|
|
432
571
|
} catch (err) {
|
|
@@ -454,7 +593,7 @@ function sandboxClaudeSettings(hookScriptSandboxPath) {
|
|
|
454
593
|
{
|
|
455
594
|
type: 'command',
|
|
456
595
|
command: `node ${hookScriptSandboxPath}`,
|
|
457
|
-
timeout:
|
|
596
|
+
timeout: HOOK_TIMEOUT_SECONDS,
|
|
458
597
|
statusMessage: 'Verifying tool execution against Intutic SOP policy...',
|
|
459
598
|
},
|
|
460
599
|
],
|
|
@@ -513,8 +652,8 @@ async function writeSandboxUserSettings(session, hookScriptSandboxPath, abortSig
|
|
|
513
652
|
}
|
|
514
653
|
/**
|
|
515
654
|
* Builds `{ bootstrapHash, onBootstrap }` for
|
|
516
|
-
* `HarnessAgentSettings.sandboxConfig` —
|
|
517
|
-
*
|
|
655
|
+
* `HarnessAgentSettings.sandboxConfig` — a channel the framework provides
|
|
656
|
+
* but nothing used:
|
|
518
657
|
*
|
|
519
658
|
* ```ts
|
|
520
659
|
* import { HarnessAgent } from '@ai-sdk/harness/agent'
|
|
@@ -547,7 +686,7 @@ export function intuticSandboxBootstrap(opts = {}) {
|
|
|
547
686
|
const rulesRelPath = `${bootstrapDir}/policy-snapshot.rules`;
|
|
548
687
|
const scriptRelPath = `${bootstrapDir}/claude-code-check.js`;
|
|
549
688
|
const rulesContent = opts.policySnapshotRules ?? '';
|
|
550
|
-
const scriptContent = renderSandboxGateScript('policy-snapshot.rules');
|
|
689
|
+
const scriptContent = renderSandboxGateScript('policy-snapshot.rules', opts.workspaceId ?? '');
|
|
551
690
|
// Same hashing convention `snapshot.ts` already uses for the policy
|
|
552
691
|
// digest (`createHash('sha256')` from node:crypto) — not a new mechanism.
|
|
553
692
|
const bootstrapHash = createHash('sha256')
|
|
@@ -574,6 +713,6 @@ export function intuticSandboxBootstrap(opts = {}) {
|
|
|
574
713
|
},
|
|
575
714
|
};
|
|
576
715
|
}
|
|
577
|
-
// Exposed for the behavioural-parity
|
|
716
|
+
// Exposed for the behavioural-parity tests — not part of the public surface.
|
|
578
717
|
export const _internal = { renderSandboxGateScript, renderSandboxClaudeSettings, sandboxJoin };
|
|
579
718
|
//# sourceMappingURL=harness.js.map
|
package/dist/harness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,MAAM,IAAI,UAAU,EAA6B,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,MAAM,IAAI,UAAU,EAA6B,MAAM,WAAW,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,iCAAiC,EAAE,MAAM,wBAAwB,CAAA;AAC3H,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAmG9G;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,IAAI,KAAK,GAAG,KAAK,CAAA;IACjB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;QAC1B,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,OAAO,KAAkB,CAAA;IAC3B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAA;AAC1B,CAAC;AAED;;0EAE0E;AAC1E,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAwC,EAAE;IAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,UAAU,EAAE,CAAA;QACnC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,8EAA8E;gBAC5E,sFAAsF,CACzF,CAAA;QACH,CAAC;QAED,MAAM,aAAa,GAAsC,EAAE,CAAA;QAC3D,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,IAAI,CAAA;YACnB,IAAI,MAA0B,CAAA;YAC9B,IAAI,CAAC;gBACH,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;YAChE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,GAAG,KAAK,CAAA;gBAChB,IAAI,GAAG,YAAY,kBAAkB,EAAE,CAAC;oBACtC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAA;gBACtB,CAAC;qBAAM,CAAC;oBACN,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBAC/D,MAAM;wBACJ,+CAA+C,MAAM,4BAA4B;4BACjF,qCAAqC,CAAA;gBACzC,CAAC;YACH,CAAC;YACD,MAAM,gBAAgB,GAAgC;gBACpD,IAAI,EAAE,wBAAwB;gBAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,QAAQ;gBACR,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,GAAG,CAAC,GAAG,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1F,CAAA;YACD,aAAa,CAAC,IAAI,CAAC;gBACjB,GAAG,gBAAgB,EAAE,qCAAqC;gBAC1D,gBAAgB,EAAE,qCAAqC;gBACvD,QAAQ,EAAE;oBACR,IAAI,EAAE,WAAW;oBACjB,UAAU,EAAE,GAAG,CAAC,UAAU;oBAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC;oBAC7B,GAAG,CAAC,GAAG,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC1F;aACF,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,aAAa,CAAA;IACtB,CAAC,CAAA;AACH,CAAC;AAYD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA2B,EAC3B,QAA2C,EAC3C,OAAwC,EAAE;IAE1C,MAAM,aAAa,GAAG,MAAM,wBAAwB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAA;IACpE,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IACxD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IAC5C,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;QAC9B,MAAM,MAAM,CAAC;YACX,UAAU,EAAE,CAAC,CAAC,gBAAgB,CAAC,UAAU;YACzC,QAAQ,EAAE,CAAC,CAAC,gBAAgB,CAAC,QAAQ;YACrC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1F,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;AAC3C,CAAC;AASD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAkD,EAClD,OAAsC,EAAE;IAExC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IACvC,MAAM,MAAM,GAA+C,EAAE,CAAA;IAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAA;IAC9D,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAwFD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAA0C,EAAE;IAE5C,MAAM,aAAa,GACjB,IAAI,CAAC,YAAY,KAAK,SAAS,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;QAC7D,CAAC,CAAC;YACE,IAAI,EAAE,QAAQ;YACd,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,iEAAiE;YACjE,kEAAkE;YAClE,WAAW,EAAE,CAAC,oBAAoB,CAAC;SACpC;QACH,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAA;IAC1B,OAAO;QACL,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,aAAa;QACpD,aAAa;QACb,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,MAAM,CAAU,EAAE,CAAC;KAC3E,CAAA;AACH,CAAC;AAsLD,SAAS,WAAW,CAAC,OAAe,EAAE,QAAgB;IACpD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACnE,OAAO,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAA;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAS,uBAAuB,CAAC,aAAqB,EAAE,WAAW,GAAG,EAAE;IACtE,OAAO;;;;;;;;;;;;;;;;0CAgBiC,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;uBAChD,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiFhD,iBAAiB;;;;EAIjB,kBAAkB;;;;EAIlB,sBAAsB;;EAEtB,uBAAuB;;EAEvB,iCAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA+DV,kBAAkB,uBAAuB,oBAAoB;;0BAE5D,kBAAkB;gDACI,kBAAkB,MAAM,kBAAkB,MAAM,oBAAoB;;;;;;;;;;;;;;+BAcrF,gBAAgB;;;;;UAKrC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;CAyBzB,CAAA;AACD,CAAC;AAED;;;;;GAKG;AACH,SAAS,2BAA2B,CAAC,qBAA6B;IAChE,OAAO,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AACrF,CAAC;AAED,SAAS,qBAAqB,CAAC,qBAA6B;IAI1D,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,CAAC;QACtC,OAAO;QACP,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,QAAQ,qBAAqB,EAAE;gBACxC,OAAO,EAAE,oBAAoB;gBAC7B,aAAa,EAAE,wDAAwD;aACxE;SACF;KACF,CAAC,CAAA;IACF,OAAO;QACL,WAAW,EAAE,EAAE,IAAI,EAAE,EAAc,EAAE;QACrC,KAAK,EAAE;YACL,UAAU,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;SACrH;KACF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,KAAK,UAAU,wBAAwB,CACrC,OAA4B,EAC5B,qBAA6B,EAC7B,WAAyB;IAEzB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,qBAAqB,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3G,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IAClC,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,kEAAkE,IAAI,CAAC,QAAQ,uBAAuB;YACpG,mCAAmC,CACtC,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAA;IAC1D,MAAM,IAAI,GAAG,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IACzD,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;IAClG,IAAI,MAAM,GAA4B,IAAI,CAAA;IAC1C,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACxD,IAAI,QAAiB,CAAA;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,0DAA0D,CAAC,CAAA;QAC7G,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAE,QAAoC,CAAC,CAAC,CAAC,EAAE,CAAA;QACvI,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAA;QAC3D,MAAM,UAAU,GAAG,QAAQ,qBAAqB,EAAE,CAAA;QAClD,MAAM,MAAM,GAAG,CAAC,KAAc,EAAW,EAAE,CACzC,KAAK,CAAC,OAAO,CAAE,KAAoC,EAAE,KAAK,CAAC;YAC3D,CAAE,KAA8B,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAkC,EAAE,OAAO,KAAK,UAAU,CAAC,CAAA;QAClH,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACxG,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,CAAA;IAC3F,CAAC;IACD,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAA;AACxF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAuC,EAAE;IAC/E,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,gBAAgB,CAAA;IAC1D,MAAM,YAAY,GAAG,GAAG,YAAY,wBAAwB,CAAA;IAC5D,MAAM,aAAa,GAAG,GAAG,YAAY,uBAAuB,CAAA;IAE5D,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAA;IACnD,MAAM,aAAa,GAAG,uBAAuB,CAAC,uBAAuB,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;IAE9F,oEAAoE;IACpE,0EAA0E;IAC1E,MAAM,aAAa,GAAG,UAAU,CAAC,QAAQ,CAAC;SACvC,MAAM,CAAC,aAAa,IAAI,CAAC,WAAW,IAAI,EAAE,GAAG,CAAC;SAC9C,MAAM,CAAC,GAAG,YAAY,OAAO,YAAY,OAAO,aAAa,OAAO,aAAa,EAAE,CAAC;SACpF,MAAM,CAAC,KAAK,CAAC,CAAA;IAEhB,OAAO;QACL,aAAa;QACb,WAAW,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE;YACvD,8DAA8D;YAC9D,+DAA+D;YAC/D,sEAAsE;YACtE,oEAAoE;YACpE,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;YACzD,MAAM,KAAK,GAAqD;gBAC9D,EAAE,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE;gBACnE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE;gBAC/C,EAAE,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,uBAAuB,CAAC,EAAE,OAAO,EAAE,2BAA2B,CAAC,aAAa,CAAC,EAAE;aAC7G,CAAA;YACD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;YACnE,CAAC;YACD,MAAM,wBAAwB,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,CAAC,CAAA;QACrE,CAAC;KACF,CAAA;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,WAAW,EAAE,CAAA"}
|
package/dist/hold.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval holds — a hold rule refuses the call for now and asks a person.
|
|
3
|
+
*
|
|
4
|
+
* Port of `packages/intutic-clawde/intutic_clawde/gate/hold.py`, and the same
|
|
5
|
+
* mechanism as the MCP proxy's `approvalHold.ts`, through the same
|
|
6
|
+
* control-plane decisions API the harness hook gates use:
|
|
7
|
+
*
|
|
8
|
+
* 1. A call that matches a hold rule (`hold` severity in the policy snapshot,
|
|
9
|
+
* or a `REQUIRE_APPROVAL:` rule in the SOP register) is looked up in the
|
|
10
|
+
* workspace's approved bypasses (`GET /api/v1/decisions/approved-bypasses`).
|
|
11
|
+
* An exact, unexpired match — same rule, same tool, same arguments — lets
|
|
12
|
+
* the call through.
|
|
13
|
+
* 2. Otherwise the call is refused with an {@link IntuticGateHold} and a hold
|
|
14
|
+
* is recorded (`POST /api/v1/decisions`): it joins the review queue, the
|
|
15
|
+
* workspace is notified (`decision.pending`, Slack included), and the
|
|
16
|
+
* refusal carries the hold id.
|
|
17
|
+
* 3. An owner, admin or engineering manager approves it
|
|
18
|
+
* (`intutic decision approve <holdId>` or the Slack card). The identical
|
|
19
|
+
* retry passes only while the workspace's review-hold bypass
|
|
20
|
+
* (`reviewHoldBypassEnabled`) is on, for `reviewHoldBypassTtlMinutes`;
|
|
21
|
+
* with it off, approving records the decision and the retry is held again.
|
|
22
|
+
*
|
|
23
|
+
* "Identical" is the bypass key: the rule id, the tool name trimmed and
|
|
24
|
+
* lower-cased, and a SHA-256 of the arguments serialised with sorted keys, so
|
|
25
|
+
* the same arguments in a different key order still match and any other
|
|
26
|
+
* value does not. `packages/shared-types/fixtures/hold-key-vectors.json` holds
|
|
27
|
+
* this, the Python gate and the MCP proxy to the same hash.
|
|
28
|
+
*
|
|
29
|
+
* A hold needs the control plane both ways, so without one (no client, or an
|
|
30
|
+
* unreachable control plane) the call stays held, whatever `failClosed` says,
|
|
31
|
+
* exactly as a hook gate's hold refuses when it cannot record: a rule that
|
|
32
|
+
* says a person must approve is not satisfied by nobody being reachable.
|
|
33
|
+
*/
|
|
34
|
+
import type { GateClient } from './client.js';
|
|
35
|
+
export type HoldOutcome = {
|
|
36
|
+
kind: 'bypassed';
|
|
37
|
+
holdId: string;
|
|
38
|
+
decidedBy: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'held';
|
|
41
|
+
holdId: string;
|
|
42
|
+
recorded: boolean;
|
|
43
|
+
};
|
|
44
|
+
/** JSON with object keys sorted at every level, so key order never changes a hash. */
|
|
45
|
+
export declare function canonicalJson(value: unknown): string;
|
|
46
|
+
/** The bypass key's call half: the tool and a hash of its arguments. */
|
|
47
|
+
export declare function holdKey(toolName: string, toolInput: unknown): {
|
|
48
|
+
toolNameNormalized: string;
|
|
49
|
+
targetHash: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* What the agent is told, held or not recorded — the one place this package
|
|
53
|
+
* words a hold. Says who can approve, and that a retry passes only with the
|
|
54
|
+
* bypass setting on, because "retry after approval" alone is false under the
|
|
55
|
+
* default settings.
|
|
56
|
+
*/
|
|
57
|
+
export declare function holdMessage(ruleReason: string, ruleId: string, outcome: {
|
|
58
|
+
holdId: string;
|
|
59
|
+
recorded: boolean;
|
|
60
|
+
}): string;
|
|
61
|
+
/**
|
|
62
|
+
* Lets the call through on an approved bypass, or records a hold and returns
|
|
63
|
+
* its id. Never throws: every failure leaves the call held.
|
|
64
|
+
*/
|
|
65
|
+
export declare function requestHold(client: GateClient | null, rule: {
|
|
66
|
+
id: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
}, toolName: string, toolInput: unknown): Promise<HoldOutcome>;
|
|
69
|
+
//# sourceMappingURL=hold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hold.d.ts","sourceRoot":"","sources":["../src/hold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAK7C,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAA;AAEvD,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASpD;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAKhH;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAetH;AAYD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,WAAW,CAAC,CAoBtB"}
|
package/dist/hold.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval holds — a hold rule refuses the call for now and asks a person.
|
|
3
|
+
*
|
|
4
|
+
* Port of `packages/intutic-clawde/intutic_clawde/gate/hold.py`, and the same
|
|
5
|
+
* mechanism as the MCP proxy's `approvalHold.ts`, through the same
|
|
6
|
+
* control-plane decisions API the harness hook gates use:
|
|
7
|
+
*
|
|
8
|
+
* 1. A call that matches a hold rule (`hold` severity in the policy snapshot,
|
|
9
|
+
* or a `REQUIRE_APPROVAL:` rule in the SOP register) is looked up in the
|
|
10
|
+
* workspace's approved bypasses (`GET /api/v1/decisions/approved-bypasses`).
|
|
11
|
+
* An exact, unexpired match — same rule, same tool, same arguments — lets
|
|
12
|
+
* the call through.
|
|
13
|
+
* 2. Otherwise the call is refused with an {@link IntuticGateHold} and a hold
|
|
14
|
+
* is recorded (`POST /api/v1/decisions`): it joins the review queue, the
|
|
15
|
+
* workspace is notified (`decision.pending`, Slack included), and the
|
|
16
|
+
* refusal carries the hold id.
|
|
17
|
+
* 3. An owner, admin or engineering manager approves it
|
|
18
|
+
* (`intutic decision approve <holdId>` or the Slack card). The identical
|
|
19
|
+
* retry passes only while the workspace's review-hold bypass
|
|
20
|
+
* (`reviewHoldBypassEnabled`) is on, for `reviewHoldBypassTtlMinutes`;
|
|
21
|
+
* with it off, approving records the decision and the retry is held again.
|
|
22
|
+
*
|
|
23
|
+
* "Identical" is the bypass key: the rule id, the tool name trimmed and
|
|
24
|
+
* lower-cased, and a SHA-256 of the arguments serialised with sorted keys, so
|
|
25
|
+
* the same arguments in a different key order still match and any other
|
|
26
|
+
* value does not. `packages/shared-types/fixtures/hold-key-vectors.json` holds
|
|
27
|
+
* this, the Python gate and the MCP proxy to the same hash.
|
|
28
|
+
*
|
|
29
|
+
* A hold needs the control plane both ways, so without one (no client, or an
|
|
30
|
+
* unreachable control plane) the call stays held, whatever `failClosed` says,
|
|
31
|
+
* exactly as a hook gate's hold refuses when it cannot record: a rule that
|
|
32
|
+
* says a person must approve is not satisfied by nobody being reachable.
|
|
33
|
+
*/
|
|
34
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
35
|
+
/** The hold-record version the control plane accepts. */
|
|
36
|
+
const HOLD_RECORD_VERSION = 1;
|
|
37
|
+
/** JSON with object keys sorted at every level, so key order never changes a hash. */
|
|
38
|
+
export function canonicalJson(value) {
|
|
39
|
+
if (Array.isArray(value))
|
|
40
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
41
|
+
if (value !== null && typeof value === 'object') {
|
|
42
|
+
const entries = Object.entries(value)
|
|
43
|
+
.filter(([, v]) => v !== undefined)
|
|
44
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
45
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
46
|
+
}
|
|
47
|
+
return JSON.stringify(value ?? null);
|
|
48
|
+
}
|
|
49
|
+
/** The bypass key's call half: the tool and a hash of its arguments. */
|
|
50
|
+
export function holdKey(toolName, toolInput) {
|
|
51
|
+
return {
|
|
52
|
+
toolNameNormalized: toolName.trim().toLowerCase(),
|
|
53
|
+
targetHash: createHash('sha256').update(canonicalJson(toolInput ?? {})).digest('hex'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function newHoldId() {
|
|
57
|
+
return `hold_${Date.now().toString(36)}_${randomBytes(4).toString('hex')}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* What the agent is told, held or not recorded — the one place this package
|
|
61
|
+
* words a hold. Says who can approve, and that a retry passes only with the
|
|
62
|
+
* bypass setting on, because "retry after approval" alone is false under the
|
|
63
|
+
* default settings.
|
|
64
|
+
*/
|
|
65
|
+
export function holdMessage(ruleReason, ruleId, outcome) {
|
|
66
|
+
if (!outcome.recorded) {
|
|
67
|
+
return (`HELD for approval: ${ruleReason} [${ruleId}], but the hold could not be recorded ` +
|
|
68
|
+
`(Intutic control plane unreachable), so there is nothing to approve yet. Do not retry ` +
|
|
69
|
+
`until the control plane is reachable; the retry then asks for approval.`);
|
|
70
|
+
}
|
|
71
|
+
return (`HELD for approval: ${ruleReason} [${ruleId}]. Hold id: ${outcome.holdId}. ` +
|
|
72
|
+
`An owner, admin or engineering manager can approve it with: intutic decision approve ` +
|
|
73
|
+
`${outcome.holdId} (or reject it), or from the Slack card. Retry this exact call after ` +
|
|
74
|
+
`approval only if the workspace's review-hold bypass (reviewHoldBypassEnabled) is on; ` +
|
|
75
|
+
`otherwise approving records the decision and the call stays held.`);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Lets the call through on an approved bypass, or records a hold and returns
|
|
79
|
+
* its id. Never throws: every failure leaves the call held.
|
|
80
|
+
*/
|
|
81
|
+
export async function requestHold(client, rule, toolName, toolInput) {
|
|
82
|
+
const holdId = newHoldId();
|
|
83
|
+
if (client === null)
|
|
84
|
+
return { kind: 'held', holdId, recorded: false };
|
|
85
|
+
const { toolNameNormalized, targetHash } = holdKey(toolName, toolInput);
|
|
86
|
+
const bypass = await findBypass(client, rule.id, toolNameNormalized, targetHash);
|
|
87
|
+
if (bypass)
|
|
88
|
+
return bypass;
|
|
89
|
+
const recorded = await client.recordHold({
|
|
90
|
+
v: HOLD_RECORD_VERSION,
|
|
91
|
+
holdId,
|
|
92
|
+
reason: rule.id,
|
|
93
|
+
tool: toolName,
|
|
94
|
+
sessionId: client.sessionId,
|
|
95
|
+
at: new Date().toISOString(),
|
|
96
|
+
toolNameNormalized,
|
|
97
|
+
targetHash,
|
|
98
|
+
context: { source: 'gate_sdk', harness: client.harness, tool: toolName, rule: rule.reason },
|
|
99
|
+
});
|
|
100
|
+
return { kind: 'held', holdId, recorded };
|
|
101
|
+
}
|
|
102
|
+
async function findBypass(client, ruleId, toolNameNormalized, targetHash) {
|
|
103
|
+
const list = (await client.approvedBypasses());
|
|
104
|
+
if (!list)
|
|
105
|
+
return null;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
for (const raw of list) {
|
|
108
|
+
// The route answers for the key's own workspace; the comparison is a
|
|
109
|
+
// second check, made whenever this client was told its workspace.
|
|
110
|
+
if (client.workspaceId && raw.workspaceId !== client.workspaceId)
|
|
111
|
+
continue;
|
|
112
|
+
if (raw.sopRuleId !== ruleId)
|
|
113
|
+
continue;
|
|
114
|
+
if (raw.toolNameNormalized !== toolNameNormalized || raw.targetHash !== targetHash)
|
|
115
|
+
continue;
|
|
116
|
+
const expires = typeof raw.expiresAt === 'string' ? Date.parse(raw.expiresAt) : NaN;
|
|
117
|
+
if (Number.isNaN(expires) || now >= expires)
|
|
118
|
+
continue;
|
|
119
|
+
return {
|
|
120
|
+
kind: 'bypassed',
|
|
121
|
+
holdId: typeof raw.holdId === 'string' ? raw.holdId : '',
|
|
122
|
+
decidedBy: typeof raw.decidedBy === 'string' ? raw.decidedBy : '',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=hold.js.map
|
package/dist/hold.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hold.js","sourceRoot":"","sources":["../src/hold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGrD,yDAAyD;AACzD,MAAM,mBAAmB,GAAG,CAAC,CAAA;AAM7B,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC1E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;aAClC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACnD,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAA;IAC7F,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;AACtC,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,QAAgB,EAAE,SAAkB;IAC1D,OAAO;QACL,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;QACjD,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;KACtF,CAAA;AACH,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAA;AAC5E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,MAAc,EAAE,OAA8C;IAC5G,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,OAAO,CACL,sBAAsB,UAAU,KAAK,MAAM,wCAAwC;YACnF,wFAAwF;YACxF,yEAAyE,CAC1E,CAAA;IACH,CAAC;IACD,OAAO,CACL,sBAAsB,UAAU,KAAK,MAAM,eAAe,OAAO,CAAC,MAAM,IAAI;QAC5E,uFAAuF;QACvF,GAAG,OAAO,CAAC,MAAM,uEAAuE;QACxF,uFAAuF;QACvF,mEAAmE,CACpE,CAAA;AACH,CAAC;AAYD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAyB,EACzB,IAAoC,EACpC,QAAgB,EAChB,SAAkB;IAElB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IACrE,MAAM,EAAE,kBAAkB,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;IAEvE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAA;IAChF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IAEzB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC;QACvC,CAAC,EAAE,mBAAmB;QACtB,MAAM;QACN,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,IAAI,EAAE,QAAQ;QACd,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,kBAAkB;QAClB,UAAU;QACV,OAAO,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;KAC5F,CAAC,CAAA;IACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;AAC3C,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,MAAkB,EAClB,MAAc,EACd,kBAA0B,EAC1B,UAAkB;IAElB,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAyB,CAAA;IACtE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,qEAAqE;QACrE,kEAAkE;QAClE,IAAI,MAAM,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,KAAK,MAAM,CAAC,WAAW;YAAE,SAAQ;QAC1E,IAAI,GAAG,CAAC,SAAS,KAAK,MAAM;YAAE,SAAQ;QACtC,IAAI,GAAG,CAAC,kBAAkB,KAAK,kBAAkB,IAAI,GAAG,CAAC,UAAU,KAAK,UAAU;YAAE,SAAQ;QAC5F,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;QACnF,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,OAAO;YAAE,SAAQ;QACrD,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACxD,SAAS,EAAE,OAAO,GAAG,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;SAClE,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/dist/imagecheck.d.ts
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* no admission webhook — so this check is the argument-level enforcement
|
|
10
10
|
* point for "deploy only pinned, reviewed images".
|
|
11
11
|
*
|
|
12
|
-
* Fail-closed on anything it cannot read.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* Fail-closed on anything it cannot read. Other tiers follow a
|
|
13
|
+
* fail setting when the control plane cannot be reached; this one never does.
|
|
14
|
+
* The reasoning: a gate that cannot parse what it is being asked to
|
|
15
15
|
* approve does not know what it is approving, and "I could not read it" is
|
|
16
16
|
* not evidence of safety.
|
|
17
17
|
*/
|
|
@@ -21,6 +21,7 @@ export declare const E_UNKNOWN_REGISTRY = "E_UNKNOWN_REGISTRY";
|
|
|
21
21
|
export declare const E_UNKNOWN_IMAGE = "E_UNKNOWN_IMAGE";
|
|
22
22
|
export declare const E_DIGEST_MISMATCH = "E_DIGEST_MISMATCH";
|
|
23
23
|
export declare const E_MANIFEST_UNPARSEABLE = "E_MANIFEST_UNPARSEABLE";
|
|
24
|
+
export type ImageRefusalCode = typeof E_UNPINNED_LATEST | typeof E_UNPINNED_TAG | typeof E_UNKNOWN_REGISTRY | typeof E_UNKNOWN_IMAGE | typeof E_DIGEST_MISMATCH | typeof E_MANIFEST_UNPARSEABLE;
|
|
24
25
|
/** A parsed container image reference. */
|
|
25
26
|
export interface ImageRef {
|
|
26
27
|
raw: string;
|
|
@@ -40,12 +41,18 @@ export interface ImagePolicy {
|
|
|
40
41
|
approved_digests?: string[];
|
|
41
42
|
}>;
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
/** `code` is `''` on a pass and names the failure otherwise. */
|
|
45
|
+
export type Verdict = {
|
|
46
|
+
ok: true;
|
|
47
|
+
code: '';
|
|
46
48
|
detail: string;
|
|
47
49
|
images: ImageRef[];
|
|
48
|
-
}
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
code: ImageRefusalCode;
|
|
53
|
+
detail: string;
|
|
54
|
+
images: ImageRef[];
|
|
55
|
+
};
|
|
49
56
|
/** The single line an operator sees on the dashboard. */
|
|
50
57
|
export declare function verdictReason(v: Verdict): string;
|
|
51
58
|
/**
|
package/dist/imagecheck.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imagecheck.d.ts","sourceRoot":"","sources":["../src/imagecheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AACpD,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAC9C,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AACtD,eAAO,MAAM,eAAe,oBAAoB,CAAA;AAChD,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AACpD,eAAO,MAAM,sBAAsB,2BAA2B,CAAA;AAE9D,0CAA0C;AAC1C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAE/C;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CACzD;AAED,MAAM,
|
|
1
|
+
{"version":3,"file":"imagecheck.d.ts","sourceRoot":"","sources":["../src/imagecheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AACpD,eAAO,MAAM,cAAc,mBAAmB,CAAA;AAC9C,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AACtD,eAAO,MAAM,eAAe,oBAAoB,CAAA;AAChD,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AACpD,eAAO,MAAM,sBAAsB,2BAA2B,CAAA;AAE9D,MAAM,MAAM,gBAAgB,GACxB,OAAO,iBAAiB,GACxB,OAAO,cAAc,GACrB,OAAO,kBAAkB,GACzB,OAAO,eAAe,GACtB,OAAO,iBAAiB,GACxB,OAAO,sBAAsB,CAAA;AAEjC,0CAA0C;AAC1C,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,2BAA2B;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAE/C;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CACzD;AAED,gEAAgE;AAChE,MAAM,MAAM,OAAO,GACf;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAA;CAAE,GAC1D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAA;AAE7E,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAGhD;AAUD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,QAAQ,CAmBhE;AA+BD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAoChG;AAqBD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,CAqBnE;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAuDrF;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAqC5F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAchG;AAED,mDAAmD;AACnD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAE7E"}
|
package/dist/imagecheck.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* no admission webhook — so this check is the argument-level enforcement
|
|
10
10
|
* point for "deploy only pinned, reviewed images".
|
|
11
11
|
*
|
|
12
|
-
* Fail-closed on anything it cannot read.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* Fail-closed on anything it cannot read. Other tiers follow a
|
|
13
|
+
* fail setting when the control plane cannot be reached; this one never does.
|
|
14
|
+
* The reasoning: a gate that cannot parse what it is being asked to
|
|
15
15
|
* approve does not know what it is approving, and "I could not read it" is
|
|
16
16
|
* not evidence of safety.
|
|
17
17
|
*/
|