@denial-web/clawguard 0.1.13 → 0.1.14
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/package.json +1 -1
- package/web/app.js +49 -0
- package/web/index.html +22 -0
- package/web/styles.css +47 -2
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -40,6 +40,10 @@ const elements = {
|
|
|
40
40
|
installVerdict: document.querySelector("#install-verdict"),
|
|
41
41
|
installMessage: document.querySelector("#install-message"),
|
|
42
42
|
installCommand: document.querySelector("#install-command"),
|
|
43
|
+
approvalTitle: document.querySelector("#approval-title"),
|
|
44
|
+
approvalSummary: document.querySelector("#approval-summary"),
|
|
45
|
+
approvalCommand: document.querySelector("#approval-command"),
|
|
46
|
+
demoCommand: document.querySelector("#demo-command"),
|
|
43
47
|
actions: document.querySelector("#actions"),
|
|
44
48
|
critical: document.querySelector("#critical-count"),
|
|
45
49
|
high: document.querySelector("#high-count"),
|
|
@@ -239,6 +243,7 @@ function renderResult(result) {
|
|
|
239
243
|
elements.downloadHtml.disabled = false;
|
|
240
244
|
|
|
241
245
|
renderInstallGate(result);
|
|
246
|
+
renderApprovalFlow(result);
|
|
242
247
|
renderFindings(scan.findings ?? []);
|
|
243
248
|
}
|
|
244
249
|
|
|
@@ -268,6 +273,50 @@ function renderInstallGate(result) {
|
|
|
268
273
|
elements.installMessage.textContent = "ClawGuard would require review, sandboxing, or approval before copying files.";
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
function renderApprovalFlow(result) {
|
|
277
|
+
const scan = result.scan;
|
|
278
|
+
const policy = scan.policy ?? {};
|
|
279
|
+
const decision = policy.decision ?? "allow";
|
|
280
|
+
const target = installTargetFor(result);
|
|
281
|
+
const installName = safeInstallName(result.displayTarget ?? "skill");
|
|
282
|
+
const preset = policy.preset ?? elements.policy.value;
|
|
283
|
+
const framework = "openclaw";
|
|
284
|
+
|
|
285
|
+
elements.demoCommand.textContent = "npx @denial-web/clawguard approvals demo-flow --keep";
|
|
286
|
+
elements.approvalCommand.textContent = [
|
|
287
|
+
"npx",
|
|
288
|
+
"@denial-web/clawguard",
|
|
289
|
+
framework,
|
|
290
|
+
"install",
|
|
291
|
+
target,
|
|
292
|
+
"--to",
|
|
293
|
+
"./.agents/skills",
|
|
294
|
+
"--name",
|
|
295
|
+
installName,
|
|
296
|
+
"--policy",
|
|
297
|
+
preset,
|
|
298
|
+
"--approval-out",
|
|
299
|
+
"./.clawguard/approvals.jsonl",
|
|
300
|
+
"--approval-mode",
|
|
301
|
+
"always"
|
|
302
|
+
].join(" ");
|
|
303
|
+
|
|
304
|
+
if (decision === "allow") {
|
|
305
|
+
elements.approvalTitle.textContent = "Approval can still be required";
|
|
306
|
+
elements.approvalSummary.textContent = "Even a clean scan can pause before trust when you use approval-mode always. That gives the owner final control over autonomous skill installs.";
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (decision === "block") {
|
|
311
|
+
elements.approvalTitle.textContent = "Blocked before trust";
|
|
312
|
+
elements.approvalSummary.textContent = "A blocked result should not be copied into a trusted skill folder. Send the report to the owner instead of applying the install.";
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
elements.approvalTitle.textContent = "Pause and ask the owner";
|
|
317
|
+
elements.approvalSummary.textContent = "Warn, review, sandbox, and dual-approval decisions create a pending approval request before any files are copied into the trusted skill folder.";
|
|
318
|
+
}
|
|
319
|
+
|
|
271
320
|
function renderFindings(findings) {
|
|
272
321
|
if (findings.length === 0) {
|
|
273
322
|
elements.findings.className = "findings empty-state";
|
package/web/index.html
CHANGED
|
@@ -86,6 +86,28 @@
|
|
|
86
86
|
</div>
|
|
87
87
|
</section>
|
|
88
88
|
|
|
89
|
+
<section class="approval-panel" aria-label="Approval loop demo">
|
|
90
|
+
<div class="approval-copy">
|
|
91
|
+
<p class="eyebrow">Approval Loop Demo</p>
|
|
92
|
+
<h2 id="approval-title">Prove the full guarded install loop</h2>
|
|
93
|
+
<p id="approval-summary">Create a harmless temporary skill, scan it, write an approval request, record an owner approval, and install only after the decision is applied.</p>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="approval-flow" aria-label="Approval flow">
|
|
96
|
+
<span>install hook</span>
|
|
97
|
+
<span>policy gate</span>
|
|
98
|
+
<span>owner approval</span>
|
|
99
|
+
<span>apply</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="command-stack">
|
|
102
|
+
<div class="command-box">
|
|
103
|
+
<code id="demo-command">npx @denial-web/clawguard approvals demo-flow --keep</code>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="command-box">
|
|
106
|
+
<code id="approval-command">npx @denial-web/clawguard openclaw install ./skill --to ./.agents/skills --approval-out ./.clawguard/approvals.jsonl --approval-mode always</code>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
|
|
89
111
|
<section class="metrics" aria-label="Finding summary">
|
|
90
112
|
<div><span>Critical</span><strong id="critical-count">0</strong></div>
|
|
91
113
|
<div><span>High</span><strong id="high-count">0</strong></div>
|
package/web/styles.css
CHANGED
|
@@ -264,7 +264,7 @@ input[type="file"] {
|
|
|
264
264
|
align-items: stretch;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
.score-ring, .decision, .install-panel, .metrics div, .metadata-grid div, .finding-card, .empty-state {
|
|
267
|
+
.score-ring, .decision, .install-panel, .approval-panel, .metrics div, .metadata-grid div, .finding-card, .empty-state {
|
|
268
268
|
border: 1px solid var(--line);
|
|
269
269
|
border-radius: 8px;
|
|
270
270
|
background: var(--panel);
|
|
@@ -339,6 +339,51 @@ input[type="file"] {
|
|
|
339
339
|
overflow-wrap: anywhere;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
.approval-panel {
|
|
343
|
+
display: grid;
|
|
344
|
+
grid-template-columns: minmax(220px, 0.9fr) minmax(160px, 0.65fr) minmax(0, 1.25fr);
|
|
345
|
+
gap: 14px;
|
|
346
|
+
align-items: stretch;
|
|
347
|
+
padding: 16px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.approval-copy h2 {
|
|
351
|
+
margin-top: 5px;
|
|
352
|
+
font-size: 22px;
|
|
353
|
+
text-transform: uppercase;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
#approval-summary {
|
|
357
|
+
margin-top: 6px;
|
|
358
|
+
color: var(--muted);
|
|
359
|
+
line-height: 1.4;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.approval-flow {
|
|
363
|
+
display: grid;
|
|
364
|
+
gap: 8px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.approval-flow span {
|
|
368
|
+
min-height: 34px;
|
|
369
|
+
display: flex;
|
|
370
|
+
align-items: center;
|
|
371
|
+
border: 1px solid var(--line);
|
|
372
|
+
border-radius: 8px;
|
|
373
|
+
background: #edf1ea;
|
|
374
|
+
color: var(--ink);
|
|
375
|
+
padding: 7px 10px;
|
|
376
|
+
font-size: 12px;
|
|
377
|
+
font-weight: 900;
|
|
378
|
+
text-transform: uppercase;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.command-stack {
|
|
382
|
+
min-width: 0;
|
|
383
|
+
display: grid;
|
|
384
|
+
gap: 10px;
|
|
385
|
+
}
|
|
386
|
+
|
|
342
387
|
.action-tags {
|
|
343
388
|
display: flex;
|
|
344
389
|
flex-wrap: wrap;
|
|
@@ -459,7 +504,7 @@ input[type="file"] {
|
|
|
459
504
|
}
|
|
460
505
|
|
|
461
506
|
@media (max-width: 900px) {
|
|
462
|
-
.workbench, .score-panel, .install-panel {
|
|
507
|
+
.workbench, .score-panel, .install-panel, .approval-panel {
|
|
463
508
|
grid-template-columns: 1fr;
|
|
464
509
|
}
|
|
465
510
|
|