@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@denial-web/clawguard",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Explainable security scanner for OpenClaw-style skills and MCP tool configs.",
5
5
  "type": "module",
6
6
  "repository": {
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