@mnemom/mnemom 0.7.2 → 0.8.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/dist/commands/agents.d.ts +0 -14
- package/dist/commands/agents.js +35 -295
- package/dist/commands/auth.js +2 -7
- package/dist/commands/card.d.ts +21 -8
- package/dist/commands/card.js +328 -275
- package/dist/commands/integrity.js +6 -6
- package/dist/commands/license.js +14 -29
- package/dist/commands/logs.js +8 -8
- package/dist/commands/policy.d.ts +10 -23
- package/dist/commands/policy.js +20 -533
- package/dist/commands/protection.d.ts +14 -0
- package/dist/commands/protection.js +308 -0
- package/dist/commands/status.js +42 -50
- package/dist/index.js +114 -160
- package/dist/lib/api.d.ts +41 -0
- package/dist/lib/api.js +131 -1
- package/dist/lib/auth.d.ts +30 -21
- package/dist/lib/auth.js +95 -49
- package/dist/lib/config.d.ts +11 -98
- package/dist/lib/config.js +12 -220
- package/dist/lib/model-cache.js +5 -6
- package/dist/smoltbot-shim.js +1 -1
- package/package.json +2 -2
- package/dist/commands/init.d.ts +0 -7
- package/dist/commands/init.js +0 -763
- package/dist/commands/migrate-config.d.ts +0 -2
- package/dist/commands/migrate-config.js +0 -72
- package/dist/commands/register.d.ts +0 -6
- package/dist/commands/register.js +0 -362
package/dist/commands/policy.js
CHANGED
|
@@ -1,543 +1,30 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { requireAgent } from "../lib/config.js";
|
|
4
|
-
import { fmt } from "../lib/format.js";
|
|
5
|
-
import { askYesNo, isInteractive } from "../lib/prompt.js";
|
|
6
|
-
import { getPolicy, publishPolicy } from "../lib/api.js";
|
|
7
|
-
import { requireAuth } from "../lib/auth.js";
|
|
8
|
-
import { validatePolicySchema, evaluatePolicy, } from "@mnemom/policy-engine";
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// YAML parsing (minimal — parse structured YAML without external dep)
|
|
11
|
-
// ============================================================================
|
|
12
1
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
2
|
+
* The `policy` command group has been removed in UC-9.
|
|
3
|
+
* Policy capabilities are now part of the alignment card.
|
|
4
|
+
*
|
|
5
|
+
* Each exported function is a stub that prints a helpful migration message.
|
|
6
|
+
* The exports are preserved so index.ts imports don't break.
|
|
15
7
|
*/
|
|
16
|
-
function
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// Not JSON — try YAML via JSON conversion (user must install js-yaml or use JSON)
|
|
23
|
-
try {
|
|
24
|
-
// Dynamic import would be async; for simplicity, require JSON for now
|
|
25
|
-
// and provide clear error message
|
|
26
|
-
throw new Error("not json");
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
throw new Error(`Failed to parse ${path.basename(filePath)}. ` +
|
|
30
|
-
`Policy files must be valid JSON. ` +
|
|
31
|
-
`YAML support coming soon — convert with: npx js-yaml ${filePath}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// ============================================================================
|
|
36
|
-
// Policy display
|
|
37
|
-
// ============================================================================
|
|
38
|
-
function displayPolicy(policy) {
|
|
39
|
-
const p = policy.policy_json;
|
|
40
|
-
console.log(fmt.header("Policy"));
|
|
41
|
-
console.log();
|
|
42
|
-
console.log(fmt.label(" Policy ID:", ` ${policy.id}`));
|
|
43
|
-
console.log(fmt.label(" Name: ", ` ${policy.name}`));
|
|
44
|
-
if (policy.description) {
|
|
45
|
-
console.log(fmt.label(" Desc: ", ` ${policy.description}`));
|
|
46
|
-
}
|
|
47
|
-
console.log(fmt.label(" Version: ", ` ${policy.version}`));
|
|
48
|
-
console.log(fmt.label(" Created: ", ` ${policy.created_at}`));
|
|
49
|
-
// Capability mappings
|
|
50
|
-
const mappings = p.capability_mappings;
|
|
51
|
-
if (mappings && Object.keys(mappings).length > 0) {
|
|
52
|
-
console.log(fmt.section("Capability Mappings"));
|
|
53
|
-
console.log();
|
|
54
|
-
for (const [name, mapping] of Object.entries(mappings)) {
|
|
55
|
-
console.log(` ${fmt.success(name)}`);
|
|
56
|
-
if (mapping.description) {
|
|
57
|
-
console.log(` ${mapping.description}`);
|
|
58
|
-
}
|
|
59
|
-
console.log(` Tools: ${mapping.tools.join(", ")}`);
|
|
60
|
-
console.log(` Card actions: ${mapping.card_actions.join(", ")}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Forbidden rules
|
|
64
|
-
const forbidden = p.forbidden;
|
|
65
|
-
if (Array.isArray(forbidden) && forbidden.length > 0) {
|
|
66
|
-
console.log(fmt.section("Forbidden Rules"));
|
|
67
|
-
console.log();
|
|
68
|
-
for (const rule of forbidden) {
|
|
69
|
-
console.log(` ${fmt.error(`${rule.pattern} [${rule.severity}]`)}`);
|
|
70
|
-
console.log(` ${rule.reason}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Escalation triggers
|
|
74
|
-
const triggers = p.escalation_triggers;
|
|
75
|
-
if (Array.isArray(triggers) && triggers.length > 0) {
|
|
76
|
-
console.log(fmt.section("Escalation Triggers"));
|
|
77
|
-
console.log();
|
|
78
|
-
for (const trigger of triggers) {
|
|
79
|
-
console.log(` ${fmt.warn(`${trigger.condition} -> ${trigger.action}`)}`);
|
|
80
|
-
console.log(` ${trigger.reason}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Defaults
|
|
84
|
-
const defaults = p.defaults;
|
|
85
|
-
if (defaults) {
|
|
86
|
-
console.log(fmt.section("Defaults"));
|
|
87
|
-
console.log();
|
|
88
|
-
console.log(fmt.label(" Unmapped action: ", defaults.unmapped_tool_action));
|
|
89
|
-
console.log(fmt.label(" Unmapped severity:", ` ${defaults.unmapped_severity}`));
|
|
90
|
-
console.log(fmt.label(" Fail open: ", ` ${defaults.fail_open}`));
|
|
91
|
-
}
|
|
92
|
-
console.log();
|
|
8
|
+
function removed(oldCmd, newCmd) {
|
|
9
|
+
console.error(`\nThe \`mnemom ${oldCmd}\` command has been removed.\n`);
|
|
10
|
+
console.error(`Use \`mnemom ${newCmd}\` instead.\n`);
|
|
11
|
+
process.exit(1);
|
|
93
12
|
}
|
|
94
|
-
// ============================================================================
|
|
95
|
-
// Subcommands
|
|
96
|
-
// ============================================================================
|
|
97
|
-
/**
|
|
98
|
-
* smoltbot policy init — scaffold a policy.json with commented examples
|
|
99
|
-
*/
|
|
100
13
|
export async function policyInitCommand() {
|
|
101
|
-
|
|
102
|
-
if (fs.existsSync(outputPath)) {
|
|
103
|
-
console.log("\n" + fmt.warn("policy.json already exists in current directory") + "\n");
|
|
104
|
-
if (isInteractive()) {
|
|
105
|
-
const overwrite = await askYesNo("Overwrite?", false);
|
|
106
|
-
if (!overwrite) {
|
|
107
|
-
console.log("\nCancelled.\n");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
process.exit(1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const scaffold = {
|
|
116
|
-
meta: {
|
|
117
|
-
schema_version: "1.0",
|
|
118
|
-
name: "my-policy",
|
|
119
|
-
description: "Policy for my agent",
|
|
120
|
-
scope: "agent",
|
|
121
|
-
},
|
|
122
|
-
capability_mappings: {
|
|
123
|
-
web_fetch: {
|
|
124
|
-
description: "Web browsing and fetching",
|
|
125
|
-
tools: [
|
|
126
|
-
"WebFetch",
|
|
127
|
-
"WebSearch",
|
|
128
|
-
"mcp__browser__*",
|
|
129
|
-
"mcp__chrome-devtools__navigate_page",
|
|
130
|
-
],
|
|
131
|
-
card_actions: ["web_fetch", "web_browse"],
|
|
132
|
-
},
|
|
133
|
-
file_system: {
|
|
134
|
-
description: "File system operations",
|
|
135
|
-
tools: ["Read", "Write", "Edit", "Glob"],
|
|
136
|
-
card_actions: ["file_read", "file_write"],
|
|
137
|
-
},
|
|
138
|
-
code_execution: {
|
|
139
|
-
description: "Code and shell execution",
|
|
140
|
-
tools: ["Bash", "mcp__*__evaluate_script", "mcp__*__execute*"],
|
|
141
|
-
card_actions: ["code_execution"],
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
forbidden: [
|
|
145
|
-
{
|
|
146
|
-
pattern: "mcp__*__delete*",
|
|
147
|
-
reason: "Destructive deletion forbidden",
|
|
148
|
-
severity: "critical",
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
escalation_triggers: [
|
|
152
|
-
{
|
|
153
|
-
condition: "tool_matches('*payment*')",
|
|
154
|
-
action: "escalate",
|
|
155
|
-
reason: "Payment tools require human approval",
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
defaults: {
|
|
159
|
-
unmapped_tool_action: "warn",
|
|
160
|
-
unmapped_severity: "medium",
|
|
161
|
-
fail_open: true,
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
const tmpPath = `${outputPath}.${process.pid}.tmp`;
|
|
165
|
-
fs.writeFileSync(tmpPath, JSON.stringify(scaffold, null, 2) + "\n");
|
|
166
|
-
fs.renameSync(tmpPath, outputPath);
|
|
167
|
-
console.log(fmt.success(`Created policy.json`));
|
|
168
|
-
console.log(fmt.label(" Path:", ` ${outputPath}`));
|
|
169
|
-
console.log("\nEdit the file, then validate with:\n");
|
|
170
|
-
console.log(" smoltbot policy validate policy.json\n");
|
|
14
|
+
removed("policy init", "card validate <file.yaml>");
|
|
171
15
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
*/
|
|
175
|
-
export async function policyValidateCommand(file) {
|
|
176
|
-
const filePath = path.resolve(file);
|
|
177
|
-
if (!fs.existsSync(filePath)) {
|
|
178
|
-
console.log("\n" + fmt.error(`File not found: ${filePath}`) + "\n");
|
|
179
|
-
process.exit(1);
|
|
180
|
-
}
|
|
181
|
-
let raw;
|
|
182
|
-
try {
|
|
183
|
-
raw = fs.readFileSync(filePath, "utf-8");
|
|
184
|
-
}
|
|
185
|
-
catch (e) {
|
|
186
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
187
|
-
console.log("\n" + fmt.error(`Could not read file: ${msg}`) + "\n");
|
|
188
|
-
process.exit(1);
|
|
189
|
-
}
|
|
190
|
-
let parsed;
|
|
191
|
-
try {
|
|
192
|
-
parsed = parsePolicyFile(raw, filePath);
|
|
193
|
-
}
|
|
194
|
-
catch (e) {
|
|
195
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
196
|
-
console.log("\n" + fmt.error(msg) + "\n");
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
199
|
-
const result = validatePolicySchema(parsed);
|
|
200
|
-
console.log(fmt.header("Policy Validation Report"));
|
|
201
|
-
console.log();
|
|
202
|
-
console.log(fmt.label(" File:", ` ${filePath}`));
|
|
203
|
-
console.log();
|
|
204
|
-
if (result.valid) {
|
|
205
|
-
console.log(fmt.success("Policy schema is valid"));
|
|
206
|
-
const meta = parsed.meta;
|
|
207
|
-
if (meta) {
|
|
208
|
-
console.log(fmt.label(" Name: ", ` ${meta.name}`));
|
|
209
|
-
console.log(fmt.label(" Scope:", ` ${meta.scope}`));
|
|
210
|
-
}
|
|
211
|
-
const mappings = parsed.capability_mappings;
|
|
212
|
-
if (mappings) {
|
|
213
|
-
console.log(fmt.label(" Capabilities:", ` ${Object.keys(mappings).length}`));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
for (const error of result.errors) {
|
|
218
|
-
console.log(fmt.error(`${error.path}: ${error.message}`));
|
|
219
|
-
}
|
|
220
|
-
console.log();
|
|
221
|
-
console.log(fmt.error(`${result.errors.length} validation error(s)`) + "\n");
|
|
222
|
-
process.exit(1);
|
|
223
|
-
}
|
|
224
|
-
console.log();
|
|
16
|
+
export async function policyValidateCommand(_file) {
|
|
17
|
+
removed("policy validate", "card validate <file.yaml>");
|
|
225
18
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
*/
|
|
229
|
-
export async function policyPublishCommand(file, agentName) {
|
|
230
|
-
const agent = await requireAgent(agentName);
|
|
231
|
-
const filePath = path.resolve(file);
|
|
232
|
-
if (!fs.existsSync(filePath)) {
|
|
233
|
-
console.log("\n" + fmt.error(`File not found: ${filePath}`) + "\n");
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
let raw;
|
|
237
|
-
try {
|
|
238
|
-
raw = fs.readFileSync(filePath, "utf-8");
|
|
239
|
-
}
|
|
240
|
-
catch (e) {
|
|
241
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
242
|
-
console.log("\n" + fmt.error(`Could not read file: ${msg}`) + "\n");
|
|
243
|
-
process.exit(1);
|
|
244
|
-
}
|
|
245
|
-
let parsed;
|
|
246
|
-
try {
|
|
247
|
-
parsed = parsePolicyFile(raw, filePath);
|
|
248
|
-
}
|
|
249
|
-
catch (e) {
|
|
250
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
251
|
-
console.log("\n" + fmt.error(msg) + "\n");
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
// Validate locally first
|
|
255
|
-
const validation = validatePolicySchema(parsed);
|
|
256
|
-
console.log(fmt.header("Policy Validation"));
|
|
257
|
-
console.log();
|
|
258
|
-
if (!validation.valid) {
|
|
259
|
-
for (const error of validation.errors) {
|
|
260
|
-
console.log(fmt.error(`${error.path}: ${error.message}`));
|
|
261
|
-
}
|
|
262
|
-
console.log();
|
|
263
|
-
console.log(fmt.error("Validation failed. Fix the errors above before publishing.") + "\n");
|
|
264
|
-
process.exit(1);
|
|
265
|
-
}
|
|
266
|
-
console.log(fmt.success("Policy schema is valid"));
|
|
267
|
-
console.log();
|
|
268
|
-
// Require authentication
|
|
269
|
-
await requireAuth();
|
|
270
|
-
// Confirm
|
|
271
|
-
if (isInteractive()) {
|
|
272
|
-
const meta = parsed.meta;
|
|
273
|
-
const confirm = await askYesNo(`Publish policy "${meta?.name || "unnamed"}" for agent ${agent.agentId}?`, false);
|
|
274
|
-
if (!confirm) {
|
|
275
|
-
console.log("\nPublish cancelled.\n");
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
// Publish
|
|
280
|
-
try {
|
|
281
|
-
console.log("\nPublishing policy...");
|
|
282
|
-
const result = await publishPolicy(agent.agentId, parsed);
|
|
283
|
-
console.log(fmt.success("Policy published successfully!"));
|
|
284
|
-
console.log(fmt.label(" Policy ID:", ` ${result.id}`));
|
|
285
|
-
console.log(fmt.label(" Version: ", ` ${result.version}`) + "\n");
|
|
286
|
-
}
|
|
287
|
-
catch (error) {
|
|
288
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
289
|
-
console.log("\n" + fmt.error(`Failed to publish policy: ${message}`) + "\n");
|
|
290
|
-
process.exit(1);
|
|
291
|
-
}
|
|
19
|
+
export async function policyPublishCommand(_file, _agentName) {
|
|
20
|
+
removed("policy publish", "card publish <file.yaml>");
|
|
292
21
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
*/
|
|
296
|
-
export async function policyListCommand(agentName) {
|
|
297
|
-
const agent = await requireAgent(agentName);
|
|
298
|
-
console.log("\nFetching policy...\n");
|
|
299
|
-
try {
|
|
300
|
-
const response = await getPolicy(agent.agentId);
|
|
301
|
-
if (!response || !response.policy) {
|
|
302
|
-
console.log(fmt.warn("No active policy"));
|
|
303
|
-
console.log("\nCreate a policy with:\n");
|
|
304
|
-
console.log(" smoltbot policy init");
|
|
305
|
-
console.log(" smoltbot policy publish policy.json\n");
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
displayPolicy(response.policy);
|
|
309
|
-
}
|
|
310
|
-
catch (error) {
|
|
311
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
312
|
-
console.log("\n" + fmt.error(`Failed to fetch policy: ${message}`) + "\n");
|
|
313
|
-
process.exit(1);
|
|
314
|
-
}
|
|
22
|
+
export async function policyListCommand(_agentName) {
|
|
23
|
+
removed("policy list", "card show");
|
|
315
24
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
*/
|
|
319
|
-
export async function policyTestCommand(file, agentName) {
|
|
320
|
-
const agent = await requireAgent(agentName);
|
|
321
|
-
const filePath = path.resolve(file);
|
|
322
|
-
if (!fs.existsSync(filePath)) {
|
|
323
|
-
console.log("\n" + fmt.error(`File not found: ${filePath}`) + "\n");
|
|
324
|
-
process.exit(1);
|
|
325
|
-
}
|
|
326
|
-
let raw;
|
|
327
|
-
try {
|
|
328
|
-
raw = fs.readFileSync(filePath, "utf-8");
|
|
329
|
-
}
|
|
330
|
-
catch (e) {
|
|
331
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
332
|
-
console.log("\n" + fmt.error(`Could not read file: ${msg}`) + "\n");
|
|
333
|
-
process.exit(1);
|
|
334
|
-
}
|
|
335
|
-
let parsed;
|
|
336
|
-
try {
|
|
337
|
-
parsed = parsePolicyFile(raw, filePath);
|
|
338
|
-
}
|
|
339
|
-
catch (e) {
|
|
340
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
341
|
-
console.log("\n" + fmt.error(msg) + "\n");
|
|
342
|
-
process.exit(1);
|
|
343
|
-
}
|
|
344
|
-
// Validate first
|
|
345
|
-
const validation = validatePolicySchema(parsed);
|
|
346
|
-
if (!validation.valid) {
|
|
347
|
-
console.log(fmt.error("Policy validation failed:"));
|
|
348
|
-
for (const error of validation.errors) {
|
|
349
|
-
console.log(fmt.error(` ${error.path}: ${error.message}`));
|
|
350
|
-
}
|
|
351
|
-
console.log();
|
|
352
|
-
process.exit(1);
|
|
353
|
-
}
|
|
354
|
-
// Require authentication
|
|
355
|
-
await requireAuth();
|
|
356
|
-
console.log(`\nTesting policy against historical traces for agent ${agent.agentId}...\n`);
|
|
357
|
-
try {
|
|
358
|
-
const { testPolicyHistorical } = await import("../lib/api.js");
|
|
359
|
-
const result = await testPolicyHistorical(agent.agentId, parsed);
|
|
360
|
-
console.log(fmt.header("Policy Test Results"));
|
|
361
|
-
console.log();
|
|
362
|
-
console.log(fmt.label(" Policy: ", ` ${result.policy_name}`));
|
|
363
|
-
console.log(fmt.label(" Total traces:", ` ${result.total_traces}`));
|
|
364
|
-
console.log();
|
|
365
|
-
const s = result.summary;
|
|
366
|
-
if (s.pass > 0)
|
|
367
|
-
console.log(fmt.success(`${s.pass} passed`));
|
|
368
|
-
if (s.warn > 0)
|
|
369
|
-
console.log(fmt.warn(`${s.warn} warnings`));
|
|
370
|
-
if (s.fail > 0)
|
|
371
|
-
console.log(fmt.error(`${s.fail} failed`));
|
|
372
|
-
if (s.skipped > 0)
|
|
373
|
-
console.log(fmt.label(" Skipped:", ` ${s.skipped} (no tools)`));
|
|
374
|
-
// Show first few failures
|
|
375
|
-
const failures = result.results.filter((r) => r.verdict === "fail");
|
|
376
|
-
if (failures.length > 0) {
|
|
377
|
-
console.log(fmt.section("Failed Traces (first 5)"));
|
|
378
|
-
console.log();
|
|
379
|
-
for (const f of failures.slice(0, 5)) {
|
|
380
|
-
console.log(fmt.error(` ${f.trace_id}`));
|
|
381
|
-
for (const v of f.violations ?? []) {
|
|
382
|
-
console.log(` ${v.type}: ${v.tool} — ${v.reason}`);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
console.log();
|
|
387
|
-
}
|
|
388
|
-
catch (error) {
|
|
389
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
390
|
-
console.log("\n" + fmt.error(`Failed to test policy: ${message}`) + "\n");
|
|
391
|
-
process.exit(1);
|
|
392
|
-
}
|
|
25
|
+
export async function policyTestCommand(_file, _agentName) {
|
|
26
|
+
removed("policy test", "card evaluate <file.yaml> --tools <tools>");
|
|
393
27
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
*
|
|
397
|
-
* Runs entirely locally using the embedded policy engine. No API key needed.
|
|
398
|
-
*/
|
|
399
|
-
export async function policyEvaluateCommand(file, options) {
|
|
400
|
-
// 1. Read + validate policy file
|
|
401
|
-
const policyPath = path.resolve(file);
|
|
402
|
-
if (!fs.existsSync(policyPath)) {
|
|
403
|
-
console.log("\n" + fmt.error(`Policy file not found: ${policyPath}`) + "\n");
|
|
404
|
-
process.exit(1);
|
|
405
|
-
}
|
|
406
|
-
let policyRaw;
|
|
407
|
-
try {
|
|
408
|
-
policyRaw = fs.readFileSync(policyPath, "utf-8");
|
|
409
|
-
}
|
|
410
|
-
catch (e) {
|
|
411
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
412
|
-
console.log("\n" + fmt.error(`Could not read policy file: ${msg}`) + "\n");
|
|
413
|
-
process.exit(1);
|
|
414
|
-
}
|
|
415
|
-
let policyParsed;
|
|
416
|
-
try {
|
|
417
|
-
policyParsed = parsePolicyFile(policyRaw, policyPath);
|
|
418
|
-
}
|
|
419
|
-
catch (e) {
|
|
420
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
421
|
-
console.log("\n" + fmt.error(msg) + "\n");
|
|
422
|
-
process.exit(1);
|
|
423
|
-
}
|
|
424
|
-
const validation = validatePolicySchema(policyParsed);
|
|
425
|
-
if (!validation.valid) {
|
|
426
|
-
console.log(fmt.error("Policy validation failed:"));
|
|
427
|
-
for (const error of validation.errors) {
|
|
428
|
-
console.log(fmt.error(` ${error.path}: ${error.message}`));
|
|
429
|
-
}
|
|
430
|
-
console.log();
|
|
431
|
-
process.exit(1);
|
|
432
|
-
}
|
|
433
|
-
// 2. Read + parse card file
|
|
434
|
-
let cardContent = {};
|
|
435
|
-
if (options.card) {
|
|
436
|
-
const cardPath = path.resolve(options.card);
|
|
437
|
-
if (!fs.existsSync(cardPath)) {
|
|
438
|
-
console.log("\n" + fmt.error(`Card file not found: ${cardPath}`) + "\n");
|
|
439
|
-
process.exit(1);
|
|
440
|
-
}
|
|
441
|
-
try {
|
|
442
|
-
const cardRaw = fs.readFileSync(cardPath, "utf-8");
|
|
443
|
-
cardContent = JSON.parse(cardRaw);
|
|
444
|
-
}
|
|
445
|
-
catch (e) {
|
|
446
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
447
|
-
console.log("\n" + fmt.error(`Could not read card file: ${msg}`) + "\n");
|
|
448
|
-
process.exit(1);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// 3. Parse tool list from --tools or --tool-manifest
|
|
452
|
-
let tools = [];
|
|
453
|
-
if (options.tools) {
|
|
454
|
-
tools = options.tools.split(",").map((t) => ({ name: t.trim() })).filter((t) => t.name);
|
|
455
|
-
}
|
|
456
|
-
else if (options.toolManifest) {
|
|
457
|
-
const manifestPath = path.resolve(options.toolManifest);
|
|
458
|
-
if (!fs.existsSync(manifestPath)) {
|
|
459
|
-
console.log("\n" + fmt.error(`Tool manifest file not found: ${manifestPath}`) + "\n");
|
|
460
|
-
process.exit(1);
|
|
461
|
-
}
|
|
462
|
-
try {
|
|
463
|
-
const manifestRaw = fs.readFileSync(manifestPath, "utf-8");
|
|
464
|
-
const manifest = JSON.parse(manifestRaw);
|
|
465
|
-
if (Array.isArray(manifest)) {
|
|
466
|
-
tools = manifest.map((t) => typeof t === "string" ? { name: t } : { name: t.name });
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
catch (e) {
|
|
470
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
471
|
-
console.log("\n" + fmt.error(`Could not read tool manifest: ${msg}`) + "\n");
|
|
472
|
-
process.exit(1);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (tools.length === 0) {
|
|
476
|
-
console.log("\n" + fmt.error("No tools specified. Use --tools or --tool-manifest") + "\n");
|
|
477
|
-
process.exit(1);
|
|
478
|
-
}
|
|
479
|
-
// 4. Run evaluation
|
|
480
|
-
const result = evaluatePolicy({
|
|
481
|
-
context: "cicd",
|
|
482
|
-
policy: policyParsed,
|
|
483
|
-
card: cardContent,
|
|
484
|
-
tools,
|
|
485
|
-
});
|
|
486
|
-
// 5. Display results
|
|
487
|
-
console.log(fmt.header("Policy Evaluation Report"));
|
|
488
|
-
console.log();
|
|
489
|
-
console.log(fmt.label(" Policy:", ` ${policyParsed.meta?.name ?? "unknown"}`));
|
|
490
|
-
console.log(fmt.label(" Context:", " cicd"));
|
|
491
|
-
console.log(fmt.label(" Tools:", ` ${tools.length} (${tools.map((t) => t.name).join(", ")})`));
|
|
492
|
-
console.log();
|
|
493
|
-
// Verdict
|
|
494
|
-
if (result.verdict === "pass") {
|
|
495
|
-
console.log(fmt.success("PASS — all tools comply with policy"));
|
|
496
|
-
}
|
|
497
|
-
else if (result.verdict === "warn") {
|
|
498
|
-
console.log(fmt.warn("WARN — policy warnings detected"));
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
console.log(fmt.error("FAIL — policy violations detected"));
|
|
502
|
-
}
|
|
503
|
-
console.log();
|
|
504
|
-
// Violations
|
|
505
|
-
if (result.violations.length > 0) {
|
|
506
|
-
console.log(fmt.section("Violations"));
|
|
507
|
-
console.log();
|
|
508
|
-
for (const v of result.violations) {
|
|
509
|
-
console.log(fmt.error(` ${v.tool} [${v.severity}] — ${v.type}`));
|
|
510
|
-
console.log(` ${v.reason}`);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
// Warnings
|
|
514
|
-
if (result.warnings.length > 0) {
|
|
515
|
-
console.log(fmt.section("Warnings"));
|
|
516
|
-
console.log();
|
|
517
|
-
for (const w of result.warnings) {
|
|
518
|
-
console.log(fmt.warn(` ${w.tool} — ${w.type}`));
|
|
519
|
-
console.log(` ${w.reason}`);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
// Coverage
|
|
523
|
-
const cov = result.coverage;
|
|
524
|
-
console.log(fmt.section("Coverage"));
|
|
525
|
-
console.log();
|
|
526
|
-
console.log(fmt.label(" Card actions:", ` ${cov.total_card_actions}`));
|
|
527
|
-
console.log(fmt.label(" Mapped: ", ` ${cov.mapped_card_actions.length}`));
|
|
528
|
-
console.log(fmt.label(" Unmapped: ", ` ${cov.unmapped_card_actions.length}`));
|
|
529
|
-
console.log(fmt.label(" Coverage: ", ` ${cov.coverage_pct.toFixed(1)}%`));
|
|
530
|
-
if (cov.unmapped_card_actions.length > 0) {
|
|
531
|
-
console.log(fmt.label(" Unmapped list:", ` ${cov.unmapped_card_actions.join(", ")}`));
|
|
532
|
-
}
|
|
533
|
-
console.log();
|
|
534
|
-
console.log(fmt.label(" Duration:", ` ${result.duration_ms}ms`));
|
|
535
|
-
console.log();
|
|
536
|
-
// 6. Exit code
|
|
537
|
-
if (result.verdict === "fail") {
|
|
538
|
-
process.exit(1);
|
|
539
|
-
}
|
|
540
|
-
if (options.strict && result.verdict === "warn") {
|
|
541
|
-
process.exit(1);
|
|
542
|
-
}
|
|
28
|
+
export async function policyEvaluateCommand(_file, _options) {
|
|
29
|
+
removed("policy evaluate", "card evaluate <file.yaml> --tools <tools>");
|
|
543
30
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ValidationCheck {
|
|
2
|
+
name: string;
|
|
3
|
+
passed: boolean;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Validate a protection card object.
|
|
8
|
+
* Schema: mode, thresholds, screen_surfaces, trusted_sources
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateProtectionCard(card: Record<string, unknown>): ValidationCheck[];
|
|
11
|
+
export declare function protectionShowCommand(agentName?: string): Promise<void>;
|
|
12
|
+
export declare function protectionPublishCommand(file: string, agentName?: string): Promise<void>;
|
|
13
|
+
export declare function protectionValidateCommand(file: string): Promise<void>;
|
|
14
|
+
export declare function protectionEditCommand(agentName?: string): Promise<void>;
|