clawclamp 0.1.14 → 0.1.15
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/assets/app.js +3 -3
- package/assets/index.html +34 -26
- package/assets/styles.css +58 -12
- package/package.json +1 -1
- package/src/guard.ts +8 -0
- package/src/http.ts +6 -0
package/assets/app.js
CHANGED
|
@@ -324,7 +324,7 @@ policyCreateBtn.addEventListener("click", async () => {
|
|
|
324
324
|
body: JSON.stringify(body),
|
|
325
325
|
});
|
|
326
326
|
policyStatusEl.textContent = "已新增策略。";
|
|
327
|
-
await
|
|
327
|
+
await refreshAll();
|
|
328
328
|
});
|
|
329
329
|
|
|
330
330
|
policyUpdateBtn.addEventListener("click", async () => {
|
|
@@ -343,7 +343,7 @@ policyUpdateBtn.addEventListener("click", async () => {
|
|
|
343
343
|
body: JSON.stringify({ content }),
|
|
344
344
|
});
|
|
345
345
|
policyStatusEl.textContent = "已保存策略。";
|
|
346
|
-
await
|
|
346
|
+
await refreshAll();
|
|
347
347
|
});
|
|
348
348
|
|
|
349
349
|
policyDeleteBtn.addEventListener("click", async () => {
|
|
@@ -358,5 +358,5 @@ policyDeleteBtn.addEventListener("click", async () => {
|
|
|
358
358
|
policyStatusEl.textContent = "已删除策略。";
|
|
359
359
|
policyIdInput.value = "";
|
|
360
360
|
policyContentInput.value = "";
|
|
361
|
-
await
|
|
361
|
+
await refreshAll();
|
|
362
362
|
});
|
package/assets/index.html
CHANGED
|
@@ -21,6 +21,40 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
</header>
|
|
23
23
|
|
|
24
|
+
<section class="card policy-shell">
|
|
25
|
+
<div class="policy-head">
|
|
26
|
+
<div>
|
|
27
|
+
<h2>Cedar 策略</h2>
|
|
28
|
+
<div class="note">查看与维护当前策略集(policyStoreUri 配置时为只读)。</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="policy-badge">Policy Lab</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="policy-grid">
|
|
33
|
+
<div class="policy-list-wrap">
|
|
34
|
+
<div class="label">策略列表</div>
|
|
35
|
+
<div class="policy-list" id="policy-list"></div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="policy-editor">
|
|
38
|
+
<div class="policy-form">
|
|
39
|
+
<label>
|
|
40
|
+
Policy ID
|
|
41
|
+
<input id="policy-id" placeholder="policy-id" />
|
|
42
|
+
</label>
|
|
43
|
+
<label>
|
|
44
|
+
Policy 内容
|
|
45
|
+
<textarea id="policy-content" rows="12" placeholder="permit(principal, action, resource) when { ... }"></textarea>
|
|
46
|
+
</label>
|
|
47
|
+
<div class="policy-actions">
|
|
48
|
+
<button id="policy-create" class="btn primary">新增</button>
|
|
49
|
+
<button id="policy-update" class="btn">保存</button>
|
|
50
|
+
<button id="policy-delete" class="btn warn">删除</button>
|
|
51
|
+
</div>
|
|
52
|
+
<div id="policy-status" class="note"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
|
|
24
58
|
<section class="grid">
|
|
25
59
|
<div class="card">
|
|
26
60
|
<h2>模式</h2>
|
|
@@ -84,32 +118,6 @@
|
|
|
84
118
|
</div>
|
|
85
119
|
<div class="table" id="audit"></div>
|
|
86
120
|
</section>
|
|
87
|
-
|
|
88
|
-
<section class="card">
|
|
89
|
-
<h2>Cedar 策略</h2>
|
|
90
|
-
<div class="note">查看与维护当前策略集(policyStoreUri 配置时为只读)。</div>
|
|
91
|
-
<div class="policy-grid">
|
|
92
|
-
<div class="policy-list" id="policy-list"></div>
|
|
93
|
-
<div class="policy-editor">
|
|
94
|
-
<div class="policy-form">
|
|
95
|
-
<label>
|
|
96
|
-
Policy ID
|
|
97
|
-
<input id="policy-id" placeholder="policy-id" />
|
|
98
|
-
</label>
|
|
99
|
-
<label>
|
|
100
|
-
Policy 内容
|
|
101
|
-
<textarea id="policy-content" rows="8" placeholder="permit(principal, action, resource) when { ... }"></textarea>
|
|
102
|
-
</label>
|
|
103
|
-
<div class="policy-actions">
|
|
104
|
-
<button id="policy-create" class="btn primary">新增</button>
|
|
105
|
-
<button id="policy-update" class="btn">保存</button>
|
|
106
|
-
<button id="policy-delete" class="btn warn">删除</button>
|
|
107
|
-
</div>
|
|
108
|
-
<div id="policy-status" class="note"></div>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
</section>
|
|
113
121
|
</div>
|
|
114
122
|
|
|
115
123
|
<script type="module" src="/plugins/clawclamp/assets/app.js"></script>
|
package/assets/styles.css
CHANGED
|
@@ -84,6 +84,33 @@ h1 {
|
|
|
84
84
|
box-shadow: var(--shadow);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
.policy-shell {
|
|
88
|
+
margin-bottom: 16px;
|
|
89
|
+
background:
|
|
90
|
+
linear-gradient(135deg, rgba(0, 163, 255, 0.1), rgba(124, 140, 255, 0.06)),
|
|
91
|
+
var(--panel);
|
|
92
|
+
border-color: #b9d7ea;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.policy-head {
|
|
96
|
+
display: flex;
|
|
97
|
+
justify-content: space-between;
|
|
98
|
+
align-items: flex-start;
|
|
99
|
+
gap: 12px;
|
|
100
|
+
margin-bottom: 12px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.policy-badge {
|
|
104
|
+
padding: 6px 10px;
|
|
105
|
+
border: 1px solid #a7d7ff;
|
|
106
|
+
border-radius: 999px;
|
|
107
|
+
font-size: 0.72rem;
|
|
108
|
+
letter-spacing: 0.08em;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
color: #03527d;
|
|
111
|
+
background: rgba(255, 255, 255, 0.8);
|
|
112
|
+
}
|
|
113
|
+
|
|
87
114
|
h2 {
|
|
88
115
|
margin: 0 0 8px;
|
|
89
116
|
font-size: 1rem;
|
|
@@ -280,40 +307,58 @@ select {
|
|
|
280
307
|
|
|
281
308
|
.policy-grid {
|
|
282
309
|
display: grid;
|
|
283
|
-
grid-template-columns:
|
|
310
|
+
grid-template-columns: 260px 1fr;
|
|
284
311
|
gap: 12px;
|
|
285
312
|
margin-top: 10px;
|
|
286
313
|
}
|
|
287
314
|
|
|
315
|
+
.policy-list-wrap {
|
|
316
|
+
display: grid;
|
|
317
|
+
gap: 8px;
|
|
318
|
+
}
|
|
319
|
+
|
|
288
320
|
.policy-list {
|
|
289
321
|
display: grid;
|
|
290
322
|
gap: 6px;
|
|
291
323
|
align-content: start;
|
|
292
|
-
max-height:
|
|
324
|
+
max-height: 420px;
|
|
293
325
|
overflow: auto;
|
|
294
|
-
padding
|
|
326
|
+
padding: 8px;
|
|
327
|
+
border: 1px solid var(--border);
|
|
328
|
+
border-radius: 10px;
|
|
329
|
+
background: rgba(255, 255, 255, 0.7);
|
|
330
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
|
295
331
|
}
|
|
296
332
|
|
|
297
333
|
.policy-item {
|
|
298
|
-
padding:
|
|
299
|
-
border-radius:
|
|
300
|
-
border: 1px solid
|
|
301
|
-
background: #
|
|
334
|
+
padding: 8px 10px;
|
|
335
|
+
border-radius: 8px;
|
|
336
|
+
border: 1px solid #d6e3f0;
|
|
337
|
+
background: linear-gradient(180deg, #ffffff, #f5f9fd);
|
|
302
338
|
font-family: var(--mono);
|
|
303
339
|
font-size: 0.75rem;
|
|
304
340
|
text-align: left;
|
|
305
341
|
cursor: pointer;
|
|
342
|
+
transition: transform 120ms ease, border-color 120ms ease, background 120ms ease;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.policy-item:hover {
|
|
346
|
+
transform: translateY(-1px);
|
|
347
|
+
border-color: #8ecdf6;
|
|
348
|
+
background: linear-gradient(180deg, #ffffff, #eef8ff);
|
|
306
349
|
}
|
|
307
350
|
|
|
308
351
|
.policy-editor {
|
|
309
|
-
display:
|
|
310
|
-
grid-template-columns: 1fr 1fr;
|
|
311
|
-
gap: 12px;
|
|
352
|
+
display: block;
|
|
312
353
|
}
|
|
313
354
|
|
|
314
355
|
.policy-form {
|
|
315
356
|
display: grid;
|
|
316
|
-
gap:
|
|
357
|
+
gap: 10px;
|
|
358
|
+
padding: 12px;
|
|
359
|
+
border: 1px solid #d6e3f0;
|
|
360
|
+
border-radius: 12px;
|
|
361
|
+
background: rgba(255, 255, 255, 0.86);
|
|
317
362
|
}
|
|
318
363
|
|
|
319
364
|
.policy-form label {
|
|
@@ -329,8 +374,9 @@ select {
|
|
|
329
374
|
padding: 8px 10px;
|
|
330
375
|
font-size: 0.8rem;
|
|
331
376
|
font-family: var(--mono);
|
|
332
|
-
min-height:
|
|
377
|
+
min-height: 260px;
|
|
333
378
|
resize: vertical;
|
|
379
|
+
background: #fbfdff;
|
|
334
380
|
}
|
|
335
381
|
|
|
336
382
|
.policy-actions {
|
package/package.json
CHANGED
package/src/guard.ts
CHANGED
|
@@ -106,6 +106,7 @@ function parseCedarDecision(raw: unknown): CedarEvaluation {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
async function evaluateCedar(params: {
|
|
109
|
+
api: OpenClawPluginApi;
|
|
109
110
|
cedarling: CedarlingInstance;
|
|
110
111
|
config: ClawClampConfig;
|
|
111
112
|
toolName: string;
|
|
@@ -133,10 +134,16 @@ async function evaluateCedar(params: {
|
|
|
133
134
|
risk: params.risk,
|
|
134
135
|
},
|
|
135
136
|
};
|
|
137
|
+
params.api.logger.info(
|
|
138
|
+
`[clawclamp] cedar request ${JSON.stringify(request)}`,
|
|
139
|
+
);
|
|
136
140
|
|
|
137
141
|
try {
|
|
138
142
|
const result = await params.cedarling.authorize_unsigned(request);
|
|
139
143
|
const jsonString = result.json_string();
|
|
144
|
+
params.api.logger.info(
|
|
145
|
+
`[clawclamp] cedar response ${JSON.stringify({ request, result: jsonString })}`,
|
|
146
|
+
);
|
|
140
147
|
const parsed = JSON.parse(jsonString) as unknown;
|
|
141
148
|
return parseCedarDecision(parsed);
|
|
142
149
|
} catch (error) {
|
|
@@ -172,6 +179,7 @@ export class ClawClampService {
|
|
|
172
179
|
try {
|
|
173
180
|
const cedarling = await this.getCedarlingInstance();
|
|
174
181
|
cedarDecision = await evaluateCedar({
|
|
182
|
+
api: this.api,
|
|
175
183
|
cedarling,
|
|
176
184
|
config: this.config,
|
|
177
185
|
toolName,
|
package/src/http.ts
CHANGED
|
@@ -402,6 +402,9 @@ export function createClawClampHttpHandler(params: {
|
|
|
402
402
|
ttlSeconds,
|
|
403
403
|
note,
|
|
404
404
|
});
|
|
405
|
+
if (params.onPolicyUpdate) {
|
|
406
|
+
await params.onPolicyUpdate();
|
|
407
|
+
}
|
|
405
408
|
sendJson(res, 200, { grant });
|
|
406
409
|
return true;
|
|
407
410
|
} catch (error) {
|
|
@@ -417,6 +420,9 @@ export function createClawClampHttpHandler(params: {
|
|
|
417
420
|
return true;
|
|
418
421
|
}
|
|
419
422
|
const removed = await revokeGrant(params.stateDir, grantId);
|
|
423
|
+
if (params.onPolicyUpdate) {
|
|
424
|
+
await params.onPolicyUpdate();
|
|
425
|
+
}
|
|
420
426
|
sendJson(res, 200, { ok: removed });
|
|
421
427
|
return true;
|
|
422
428
|
}
|