multicorn-shield 1.2.0 → 1.3.1
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/CHANGELOG.md +22 -1
- package/dist/multicorn-proxy.js +263 -179
- package/dist/multicorn-shield.js +263 -179
- package/dist/openclaw-hook/handler.js +3 -3
- package/dist/openclaw-plugin/multicorn-shield.js +3 -3
- package/dist/shield-extension.js +14 -19
- package/package.json +2 -1
- package/plugins/multicorn-shield/.claude-plugin/plugin.json +8 -0
- package/plugins/multicorn-shield/hooks/hooks.json +26 -0
- package/plugins/multicorn-shield/hooks/scripts/claude-code-tool-map.cjs +138 -0
- package/plugins/multicorn-shield/hooks/scripts/post-tool-use.cjs +253 -0
- package/plugins/multicorn-shield/hooks/scripts/pre-tool-use.cjs +690 -0
- package/plugins/multicorn-shield/skills/shield-governance/SKILL.md +24 -0
- package/plugins/windsurf/hooks/scripts/pre-action.cjs +69 -25
|
@@ -400,6 +400,17 @@ function writeConsentMarker(agentName) {
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
+
/**
|
|
404
|
+
* @param {string} agentName
|
|
405
|
+
*/
|
|
406
|
+
function removeConsentMarker(agentName) {
|
|
407
|
+
try {
|
|
408
|
+
fs.unlinkSync(consentMarkerPath(agentName));
|
|
409
|
+
} catch {
|
|
410
|
+
/* ignore */
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
403
414
|
/**
|
|
404
415
|
* @param {string} url
|
|
405
416
|
*/
|
|
@@ -430,34 +441,16 @@ function sleep(ms) {
|
|
|
430
441
|
}
|
|
431
442
|
|
|
432
443
|
/**
|
|
444
|
+
* Polls GET /api/v1/approvals/{id} until the approval is decided or timeout.
|
|
445
|
+
* Returns true if approved, false on timeout.
|
|
446
|
+
* Exits the process on denial/expiry.
|
|
447
|
+
*
|
|
433
448
|
* @param {{ apiKey: string; baseUrl: string; agentName: string }} config
|
|
434
449
|
* @param {string} approvalId
|
|
435
|
-
* @param {string} service
|
|
436
|
-
* @param {string} actionType
|
|
437
450
|
* @param {string} approvalsUrl
|
|
438
|
-
* @returns {Promise<
|
|
451
|
+
* @returns {Promise<boolean>}
|
|
439
452
|
*/
|
|
440
|
-
async function
|
|
441
|
-
config,
|
|
442
|
-
approvalId,
|
|
443
|
-
service,
|
|
444
|
-
actionType,
|
|
445
|
-
approvalsUrl,
|
|
446
|
-
) {
|
|
447
|
-
if (hasConsentMarker(config.agentName)) {
|
|
448
|
-
process.stderr.write(
|
|
449
|
-
`${LOG_PREFIX} Action blocked: this action requires approval before it can run.\n` +
|
|
450
|
-
` Grant access in the Shield dashboard and retry.\n` +
|
|
451
|
-
` Detail: ${approvalsUrl}\n`,
|
|
452
|
-
);
|
|
453
|
-
process.exit(2);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const url = consentUrl(config.baseUrl, config.agentName, service, actionType);
|
|
457
|
-
writeConsentMarker(config.agentName);
|
|
458
|
-
openBrowser(url);
|
|
459
|
-
process.stderr.write("Opening Shield consent screen... Waiting for approval (up to 5 min).\n");
|
|
460
|
-
|
|
453
|
+
async function pollApprovalStatus(config, approvalId, approvalsUrl) {
|
|
461
454
|
for (let i = 0; i < MAX_APPROVAL_POLLS; i++) {
|
|
462
455
|
if (i > 0) {
|
|
463
456
|
await sleep(POLL_INTERVAL_MS);
|
|
@@ -482,7 +475,7 @@ async function handlePendingWithConsentAndPoll(
|
|
|
482
475
|
const d = /** @type {Record<string, unknown>} */ (data);
|
|
483
476
|
const st = String(d.status ?? "").toLowerCase();
|
|
484
477
|
if (st === "approved") {
|
|
485
|
-
|
|
478
|
+
return true;
|
|
486
479
|
}
|
|
487
480
|
if (st === "blocked" || st === "denied" || st === "rejected") {
|
|
488
481
|
const reason =
|
|
@@ -506,6 +499,57 @@ async function handlePendingWithConsentAndPoll(
|
|
|
506
499
|
continue;
|
|
507
500
|
}
|
|
508
501
|
}
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* @param {{ apiKey: string; baseUrl: string; agentName: string }} config
|
|
507
|
+
* @param {string} approvalId
|
|
508
|
+
* @param {string} service
|
|
509
|
+
* @param {string} actionType
|
|
510
|
+
* @param {string} approvalsUrl
|
|
511
|
+
* @returns {Promise<void>}
|
|
512
|
+
*/
|
|
513
|
+
async function handlePendingWithConsentAndPoll(
|
|
514
|
+
config,
|
|
515
|
+
approvalId,
|
|
516
|
+
service,
|
|
517
|
+
actionType,
|
|
518
|
+
approvalsUrl,
|
|
519
|
+
) {
|
|
520
|
+
if (hasConsentMarker(config.agentName)) {
|
|
521
|
+
// Consent was previously completed. Poll for the approval decision.
|
|
522
|
+
process.stderr.write(
|
|
523
|
+
`${LOG_PREFIX} Waiting for approval (up to 5 min)...\n` +
|
|
524
|
+
` Approve in the Shield dashboard: ${approvalsUrl}\n`,
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
const approved = await pollApprovalStatus(config, approvalId, approvalsUrl);
|
|
528
|
+
if (approved) {
|
|
529
|
+
process.exit(0);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Timed out. Remove stale consent marker so next call triggers consent flow.
|
|
533
|
+
removeConsentMarker(config.agentName);
|
|
534
|
+
|
|
535
|
+
process.stderr.write(
|
|
536
|
+
`${LOG_PREFIX} Action blocked: approval timed out after 5 minutes.\n` +
|
|
537
|
+
` Approve in the Shield dashboard, then retry.\n` +
|
|
538
|
+
` Detail: approvalsUrl=${approvalsUrl}\n`,
|
|
539
|
+
);
|
|
540
|
+
process.exit(2);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// No consent marker: first-time flow. Open the consent screen.
|
|
544
|
+
const url = consentUrl(config.baseUrl, config.agentName, service, actionType);
|
|
545
|
+
writeConsentMarker(config.agentName);
|
|
546
|
+
openBrowser(url);
|
|
547
|
+
process.stderr.write("Opening Shield consent screen... Waiting for approval (up to 5 min).\n");
|
|
548
|
+
|
|
549
|
+
const approved = await pollApprovalStatus(config, approvalId, approvalsUrl);
|
|
550
|
+
if (approved) {
|
|
551
|
+
process.exit(0);
|
|
552
|
+
}
|
|
509
553
|
|
|
510
554
|
process.stderr.write(
|
|
511
555
|
`${LOG_PREFIX} Action blocked: approval timed out after 5 minutes.\n` +
|