@vibecheckai/cli 3.4.0 → 3.5.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.
Files changed (166) hide show
  1. package/bin/registry.js +243 -152
  2. package/bin/runners/cli-utils.js +2 -33
  3. package/bin/runners/context/generators/cursor.js +49 -2
  4. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
  5. package/bin/runners/lib/analyzers.js +544 -19
  6. package/bin/runners/lib/audit-logger.js +532 -0
  7. package/bin/runners/lib/authority/authorities/architecture.js +364 -0
  8. package/bin/runners/lib/authority/authorities/compliance.js +341 -0
  9. package/bin/runners/lib/authority/authorities/human.js +343 -0
  10. package/bin/runners/lib/authority/authorities/quality.js +420 -0
  11. package/bin/runners/lib/authority/authorities/security.js +228 -0
  12. package/bin/runners/lib/authority/index.js +293 -0
  13. package/bin/runners/lib/authority-badge.js +425 -425
  14. package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
  15. package/bin/runners/lib/cli-charts.js +368 -0
  16. package/bin/runners/lib/cli-config-display.js +405 -0
  17. package/bin/runners/lib/cli-demo.js +275 -0
  18. package/bin/runners/lib/cli-errors.js +438 -0
  19. package/bin/runners/lib/cli-help-formatter.js +439 -0
  20. package/bin/runners/lib/cli-interactive-menu.js +509 -0
  21. package/bin/runners/lib/cli-prompts.js +441 -0
  22. package/bin/runners/lib/cli-scan-cards.js +362 -0
  23. package/bin/runners/lib/compliance-reporter.js +710 -0
  24. package/bin/runners/lib/conductor/index.js +671 -0
  25. package/bin/runners/lib/easy/README.md +123 -0
  26. package/bin/runners/lib/easy/index.js +140 -0
  27. package/bin/runners/lib/easy/interactive-wizard.js +788 -0
  28. package/bin/runners/lib/easy/one-click-firewall.js +564 -0
  29. package/bin/runners/lib/easy/zero-config-reality.js +714 -0
  30. package/bin/runners/lib/engines/accessibility-engine.js +218 -18
  31. package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
  32. package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
  33. package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
  34. package/bin/runners/lib/engines/confidence-scoring.js +276 -0
  35. package/bin/runners/lib/engines/context-detection.js +264 -0
  36. package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
  37. package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
  38. package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
  39. package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
  40. package/bin/runners/lib/engines/env-variables-engine.js +458 -0
  41. package/bin/runners/lib/engines/error-handling-engine.js +437 -0
  42. package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
  43. package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
  44. package/bin/runners/lib/engines/framework-detection.js +508 -0
  45. package/bin/runners/lib/engines/import-order-engine.js +429 -0
  46. package/bin/runners/lib/engines/mock-data-engine.js +53 -10
  47. package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
  48. package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
  49. package/bin/runners/lib/engines/orchestrator.js +334 -0
  50. package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
  51. package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
  52. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
  53. package/bin/runners/lib/engines/type-aware-engine.js +263 -39
  54. package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
  55. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
  56. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
  57. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
  58. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
  59. package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
  60. package/bin/runners/lib/enhanced-features/index.js +305 -0
  61. package/bin/runners/lib/enhanced-output.js +631 -0
  62. package/bin/runners/lib/enterprise.js +300 -0
  63. package/bin/runners/lib/entitlements-v2.js +103 -11
  64. package/bin/runners/lib/firewall/command-validator.js +351 -0
  65. package/bin/runners/lib/firewall/config.js +341 -0
  66. package/bin/runners/lib/firewall/content-validator.js +519 -0
  67. package/bin/runners/lib/firewall/index.js +101 -0
  68. package/bin/runners/lib/firewall/path-validator.js +256 -0
  69. package/bin/runners/lib/html-proof-report.js +350 -700
  70. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
  71. package/bin/runners/lib/mcp-utils.js +425 -0
  72. package/bin/runners/lib/missions/plan.js +46 -6
  73. package/bin/runners/lib/missions/templates.js +232 -0
  74. package/bin/runners/lib/output/index.js +1022 -0
  75. package/bin/runners/lib/policy-engine.js +652 -0
  76. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
  77. package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
  78. package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
  79. package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
  80. package/bin/runners/lib/polish/autofix/index.js +200 -0
  81. package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
  82. package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
  83. package/bin/runners/lib/polish/backend-checks.js +148 -0
  84. package/bin/runners/lib/polish/documentation-checks.js +111 -0
  85. package/bin/runners/lib/polish/frontend-checks.js +168 -0
  86. package/bin/runners/lib/polish/index.js +71 -0
  87. package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
  88. package/bin/runners/lib/polish/library-detection.js +175 -0
  89. package/bin/runners/lib/polish/performance-checks.js +100 -0
  90. package/bin/runners/lib/polish/security-checks.js +148 -0
  91. package/bin/runners/lib/polish/utils.js +203 -0
  92. package/bin/runners/lib/prompt-builder.js +540 -0
  93. package/bin/runners/lib/proof-certificate.js +634 -0
  94. package/bin/runners/lib/reality/accessibility-audit.js +946 -0
  95. package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
  96. package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
  97. package/bin/runners/lib/reality/performance-tracker.js +1077 -0
  98. package/bin/runners/lib/reality/scenario-generator.js +1404 -0
  99. package/bin/runners/lib/reality/visual-regression.js +852 -0
  100. package/bin/runners/lib/reality-profiler.js +717 -0
  101. package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
  102. package/bin/runners/lib/review/ai-code-review.js +832 -0
  103. package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
  104. package/bin/runners/lib/sbom-generator.js +641 -0
  105. package/bin/runners/lib/scan-output-enhanced.js +512 -0
  106. package/bin/runners/lib/scan-output.js +47 -0
  107. package/bin/runners/lib/security/owasp-scanner.js +939 -0
  108. package/bin/runners/lib/terminal-ui.js +113 -1
  109. package/bin/runners/lib/unified-cli-output.js +603 -430
  110. package/bin/runners/lib/validators/contract-validator.js +283 -0
  111. package/bin/runners/lib/validators/dead-export-detector.js +279 -0
  112. package/bin/runners/lib/validators/dep-audit.js +245 -0
  113. package/bin/runners/lib/validators/env-validator.js +319 -0
  114. package/bin/runners/lib/validators/index.js +120 -0
  115. package/bin/runners/lib/validators/license-checker.js +252 -0
  116. package/bin/runners/lib/validators/route-validator.js +290 -0
  117. package/bin/runners/runAIAgent.js +5 -10
  118. package/bin/runners/runAgent.js +3 -0
  119. package/bin/runners/runApprove.js +1233 -1200
  120. package/bin/runners/runAuth.js +22 -1
  121. package/bin/runners/runAuthority.js +528 -0
  122. package/bin/runners/runCheckpoint.js +4 -24
  123. package/bin/runners/runClassify.js +862 -859
  124. package/bin/runners/runConductor.js +772 -0
  125. package/bin/runners/runContainer.js +366 -0
  126. package/bin/runners/runContext.js +3 -0
  127. package/bin/runners/runDoctor.js +28 -41
  128. package/bin/runners/runEasy.js +410 -0
  129. package/bin/runners/runFirewall.js +3 -0
  130. package/bin/runners/runFirewallHook.js +3 -0
  131. package/bin/runners/runFix.js +76 -66
  132. package/bin/runners/runGuard.js +411 -18
  133. package/bin/runners/runIaC.js +372 -0
  134. package/bin/runners/runInit.js +10 -60
  135. package/bin/runners/runMcp.js +11 -12
  136. package/bin/runners/runPolish.js +240 -64
  137. package/bin/runners/runPromptFirewall.js +5 -12
  138. package/bin/runners/runProve.js +20 -55
  139. package/bin/runners/runReality.js +68 -59
  140. package/bin/runners/runReport.js +31 -5
  141. package/bin/runners/runRuntime.js +5 -8
  142. package/bin/runners/runScan.js +194 -1286
  143. package/bin/runners/runShip.js +695 -47
  144. package/bin/runners/runTruth.js +3 -0
  145. package/bin/runners/runValidate.js +7 -11
  146. package/bin/runners/runVibe.js +791 -0
  147. package/bin/runners/runWatch.js +14 -23
  148. package/bin/vibecheck.js +175 -56
  149. package/mcp-server/index.js +190 -14
  150. package/mcp-server/package.json +1 -1
  151. package/mcp-server/tools-v3.js +397 -64
  152. package/mcp-server/tools.js +495 -0
  153. package/package.json +1 -1
  154. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
  155. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
  156. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
  157. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
  158. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
  159. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
  160. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
  161. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
  162. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
  163. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
  166. package/mcp-server/index-v1.js +0 -698
@@ -0,0 +1,652 @@
1
+ /**
2
+ * Enterprise Policy Enforcement Engine
3
+ *
4
+ * Define and enforce security policies:
5
+ * - Custom rules
6
+ * - Threshold-based policies
7
+ * - Team/org policies
8
+ * - Branch policies
9
+ * - Time-based policies
10
+ */
11
+
12
+ "use strict";
13
+
14
+ const fs = require("fs");
15
+ const path = require("path");
16
+ const crypto = require("crypto");
17
+
18
+ /**
19
+ * Policy condition operators
20
+ */
21
+ const OPERATORS = {
22
+ eq: (a, b) => a === b,
23
+ ne: (a, b) => a !== b,
24
+ gt: (a, b) => a > b,
25
+ gte: (a, b) => a >= b,
26
+ lt: (a, b) => a < b,
27
+ lte: (a, b) => a <= b,
28
+ contains: (a, b) => String(a).includes(String(b)),
29
+ notContains: (a, b) => !String(a).includes(String(b)),
30
+ matches: (a, b) => new RegExp(b).test(String(a)),
31
+ notMatches: (a, b) => !new RegExp(b).test(String(a)),
32
+ in: (a, b) => Array.isArray(b) && b.includes(a),
33
+ notIn: (a, b) => Array.isArray(b) && !b.includes(a),
34
+ exists: (a) => a !== undefined && a !== null,
35
+ notExists: (a) => a === undefined || a === null,
36
+ };
37
+
38
+ /**
39
+ * Policy actions
40
+ */
41
+ const ACTIONS = {
42
+ BLOCK: "block",
43
+ WARN: "warn",
44
+ ALLOW: "allow",
45
+ NOTIFY: "notify",
46
+ REQUIRE_APPROVAL: "require_approval",
47
+ AUTO_FIX: "auto_fix",
48
+ ESCALATE: "escalate",
49
+ };
50
+
51
+ /**
52
+ * Built-in policy templates
53
+ */
54
+ const POLICY_TEMPLATES = {
55
+ "no-secrets": {
56
+ id: "no-secrets",
57
+ name: "No Hardcoded Secrets",
58
+ description: "Block deployments with hardcoded secrets",
59
+ enabled: true,
60
+ priority: 100,
61
+ conditions: [
62
+ {
63
+ field: "findings",
64
+ path: "category",
65
+ operator: "contains",
66
+ value: "secret",
67
+ },
68
+ ],
69
+ action: ACTIONS.BLOCK,
70
+ message: "Hardcoded secrets detected. Remove all secrets before deploying.",
71
+ remediation: "Use environment variables or a secrets manager for sensitive data.",
72
+ tags: ["security", "secrets", "critical"],
73
+ },
74
+
75
+ "no-critical-vulns": {
76
+ id: "no-critical-vulns",
77
+ name: "No Critical Vulnerabilities",
78
+ description: "Block deployments with critical vulnerabilities",
79
+ enabled: true,
80
+ priority: 95,
81
+ conditions: [
82
+ {
83
+ field: "summary.critical",
84
+ operator: "gt",
85
+ value: 0,
86
+ },
87
+ ],
88
+ action: ACTIONS.BLOCK,
89
+ message: "Critical vulnerabilities detected. Fix all critical issues before deploying.",
90
+ tags: ["security", "vulnerabilities", "critical"],
91
+ },
92
+
93
+ "max-high-issues": {
94
+ id: "max-high-issues",
95
+ name: "Maximum High-Severity Issues",
96
+ description: "Limit number of high-severity issues",
97
+ enabled: true,
98
+ priority: 80,
99
+ conditions: [
100
+ {
101
+ field: "summary.high",
102
+ operator: "gt",
103
+ value: 5,
104
+ },
105
+ ],
106
+ action: ACTIONS.WARN,
107
+ message: "Too many high-severity issues detected (>5).",
108
+ tags: ["quality", "threshold"],
109
+ },
110
+
111
+ "minimum-score": {
112
+ id: "minimum-score",
113
+ name: "Minimum Quality Score",
114
+ description: "Require minimum quality score for deployment",
115
+ enabled: true,
116
+ priority: 70,
117
+ conditions: [
118
+ {
119
+ field: "score",
120
+ operator: "lt",
121
+ value: 70,
122
+ },
123
+ ],
124
+ action: ACTIONS.BLOCK,
125
+ message: "Quality score too low. Minimum required: 70.",
126
+ tags: ["quality", "threshold"],
127
+ },
128
+
129
+ "no-mock-data": {
130
+ id: "no-mock-data",
131
+ name: "No Mock Data in Production",
132
+ description: "Block mock/fake data in production code",
133
+ enabled: true,
134
+ priority: 90,
135
+ conditions: [
136
+ {
137
+ field: "findings",
138
+ path: "category",
139
+ operator: "matches",
140
+ value: "mock|fake|stub|test-data",
141
+ },
142
+ ],
143
+ action: ACTIONS.BLOCK,
144
+ message: "Mock data detected in production paths.",
145
+ tags: ["quality", "data"],
146
+ },
147
+
148
+ "require-tests": {
149
+ id: "require-tests",
150
+ name: "Require Test Coverage",
151
+ description: "Ensure test coverage meets minimum threshold",
152
+ enabled: false,
153
+ priority: 60,
154
+ conditions: [
155
+ {
156
+ field: "coverage",
157
+ operator: "lt",
158
+ value: 80,
159
+ },
160
+ ],
161
+ action: ACTIONS.WARN,
162
+ message: "Test coverage below 80%.",
163
+ tags: ["quality", "testing"],
164
+ },
165
+
166
+ "branch-protection": {
167
+ id: "branch-protection",
168
+ name: "Main Branch Protection",
169
+ description: "Stricter rules for main/master branch",
170
+ enabled: true,
171
+ priority: 100,
172
+ conditions: [
173
+ {
174
+ field: "branch",
175
+ operator: "matches",
176
+ value: "^(main|master)$",
177
+ },
178
+ {
179
+ field: "summary.critical",
180
+ operator: "gt",
181
+ value: 0,
182
+ },
183
+ ],
184
+ action: ACTIONS.BLOCK,
185
+ message: "Cannot merge to main/master with critical issues.",
186
+ tags: ["branch", "protection"],
187
+ },
188
+
189
+ "after-hours-approval": {
190
+ id: "after-hours-approval",
191
+ name: "After-Hours Approval Required",
192
+ description: "Require approval for deployments outside business hours",
193
+ enabled: false,
194
+ priority: 50,
195
+ conditions: [
196
+ {
197
+ field: "time.hour",
198
+ operator: "notIn",
199
+ value: [9, 10, 11, 12, 13, 14, 15, 16, 17],
200
+ },
201
+ ],
202
+ action: ACTIONS.REQUIRE_APPROVAL,
203
+ message: "Deployment outside business hours requires approval.",
204
+ tags: ["time", "approval"],
205
+ },
206
+
207
+ "weekend-block": {
208
+ id: "weekend-block",
209
+ name: "Weekend Deployment Block",
210
+ description: "Block deployments on weekends",
211
+ enabled: false,
212
+ priority: 40,
213
+ conditions: [
214
+ {
215
+ field: "time.dayOfWeek",
216
+ operator: "in",
217
+ value: [0, 6], // Sunday, Saturday
218
+ },
219
+ ],
220
+ action: ACTIONS.BLOCK,
221
+ message: "Deployments are not allowed on weekends.",
222
+ tags: ["time", "weekend"],
223
+ },
224
+ };
225
+
226
+ /**
227
+ * Policy evaluation result
228
+ */
229
+ class PolicyResult {
230
+ constructor(policy, matched, action, message) {
231
+ this.policyId = policy.id;
232
+ this.policyName = policy.name;
233
+ this.matched = matched;
234
+ this.action = action;
235
+ this.message = message;
236
+ this.remediation = policy.remediation;
237
+ this.priority = policy.priority;
238
+ this.tags = policy.tags || [];
239
+ this.timestamp = new Date().toISOString();
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Policy Engine class
245
+ */
246
+ class PolicyEngine {
247
+ constructor(options = {}) {
248
+ this.options = {
249
+ policiesDir: options.policiesDir || path.join(process.cwd(), ".vibecheck", "policies"),
250
+ enableBuiltIn: options.enableBuiltIn !== false,
251
+ strictMode: options.strictMode || false,
252
+ ...options,
253
+ };
254
+
255
+ this.policies = new Map();
256
+ this.customPolicies = new Map();
257
+
258
+ // Load built-in policies
259
+ if (this.options.enableBuiltIn) {
260
+ this.loadBuiltInPolicies();
261
+ }
262
+
263
+ // Load custom policies
264
+ this.loadCustomPolicies();
265
+ }
266
+
267
+ /**
268
+ * Load built-in policies
269
+ */
270
+ loadBuiltInPolicies() {
271
+ for (const [id, policy] of Object.entries(POLICY_TEMPLATES)) {
272
+ this.policies.set(id, { ...policy, builtIn: true });
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Load custom policies from directory
278
+ */
279
+ loadCustomPolicies() {
280
+ if (!fs.existsSync(this.options.policiesDir)) {
281
+ return;
282
+ }
283
+
284
+ const files = fs.readdirSync(this.options.policiesDir)
285
+ .filter(f => f.endsWith(".json") || f.endsWith(".yaml") || f.endsWith(".yml"));
286
+
287
+ for (const file of files) {
288
+ try {
289
+ const content = fs.readFileSync(path.join(this.options.policiesDir, file), "utf-8");
290
+ const policy = JSON.parse(content);
291
+
292
+ if (this.validatePolicy(policy)) {
293
+ this.customPolicies.set(policy.id, { ...policy, builtIn: false });
294
+ }
295
+ } catch (error) {
296
+ console.error(`Failed to load policy ${file}:`, error.message);
297
+ }
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Validate policy structure
303
+ */
304
+ validatePolicy(policy) {
305
+ if (!policy.id || !policy.name || !policy.conditions || !policy.action) {
306
+ return false;
307
+ }
308
+
309
+ if (!Array.isArray(policy.conditions)) {
310
+ return false;
311
+ }
312
+
313
+ for (const condition of policy.conditions) {
314
+ if (!condition.field || !condition.operator) {
315
+ return false;
316
+ }
317
+ if (!OPERATORS[condition.operator]) {
318
+ return false;
319
+ }
320
+ }
321
+
322
+ if (!Object.values(ACTIONS).includes(policy.action)) {
323
+ return false;
324
+ }
325
+
326
+ return true;
327
+ }
328
+
329
+ /**
330
+ * Add or update a policy
331
+ */
332
+ addPolicy(policy) {
333
+ if (!this.validatePolicy(policy)) {
334
+ throw new Error("Invalid policy structure");
335
+ }
336
+
337
+ this.customPolicies.set(policy.id, { ...policy, builtIn: false });
338
+
339
+ // Save to disk
340
+ const policyPath = path.join(this.options.policiesDir, `${policy.id}.json`);
341
+ fs.mkdirSync(this.options.policiesDir, { recursive: true });
342
+ fs.writeFileSync(policyPath, JSON.stringify(policy, null, 2));
343
+
344
+ return policy;
345
+ }
346
+
347
+ /**
348
+ * Remove a custom policy
349
+ */
350
+ removePolicy(policyId) {
351
+ if (!this.customPolicies.has(policyId)) {
352
+ return false;
353
+ }
354
+
355
+ this.customPolicies.delete(policyId);
356
+
357
+ const policyPath = path.join(this.options.policiesDir, `${policyId}.json`);
358
+ if (fs.existsSync(policyPath)) {
359
+ fs.unlinkSync(policyPath);
360
+ }
361
+
362
+ return true;
363
+ }
364
+
365
+ /**
366
+ * Get all policies (built-in + custom)
367
+ */
368
+ getAllPolicies() {
369
+ const all = new Map([...this.policies, ...this.customPolicies]);
370
+ return Array.from(all.values())
371
+ .filter(p => p.enabled)
372
+ .sort((a, b) => (b.priority || 0) - (a.priority || 0));
373
+ }
374
+
375
+ /**
376
+ * Evaluate a condition against context
377
+ */
378
+ evaluateCondition(condition, context) {
379
+ let value = context;
380
+
381
+ // Navigate to field
382
+ const fieldParts = condition.field.split(".");
383
+ for (const part of fieldParts) {
384
+ if (value === undefined || value === null) break;
385
+ value = value[part];
386
+ }
387
+
388
+ // If path is specified, check array items
389
+ if (condition.path && Array.isArray(value)) {
390
+ return value.some(item => {
391
+ let itemValue = item;
392
+ const pathParts = condition.path.split(".");
393
+ for (const part of pathParts) {
394
+ if (itemValue === undefined || itemValue === null) break;
395
+ itemValue = itemValue[part];
396
+ }
397
+ return OPERATORS[condition.operator](itemValue, condition.value);
398
+ });
399
+ }
400
+
401
+ // Regular evaluation
402
+ return OPERATORS[condition.operator](value, condition.value);
403
+ }
404
+
405
+ /**
406
+ * Evaluate all policies against context
407
+ */
408
+ evaluate(context) {
409
+ const results = [];
410
+ const policies = this.getAllPolicies();
411
+
412
+ // Add time context
413
+ const now = new Date();
414
+ context.time = {
415
+ hour: now.getHours(),
416
+ minute: now.getMinutes(),
417
+ dayOfWeek: now.getDay(),
418
+ date: now.toISOString().split("T")[0],
419
+ timestamp: now.toISOString(),
420
+ };
421
+
422
+ // Evaluate each policy
423
+ for (const policy of policies) {
424
+ let matched = true;
425
+
426
+ // All conditions must match (AND logic)
427
+ for (const condition of policy.conditions) {
428
+ if (!this.evaluateCondition(condition, context)) {
429
+ matched = false;
430
+ break;
431
+ }
432
+ }
433
+
434
+ if (matched) {
435
+ results.push(new PolicyResult(
436
+ policy,
437
+ true,
438
+ policy.action,
439
+ policy.message
440
+ ));
441
+ }
442
+ }
443
+
444
+ return results;
445
+ }
446
+
447
+ /**
448
+ * Get the most severe action from results
449
+ */
450
+ getMostSevereAction(results) {
451
+ const actionPriority = {
452
+ [ACTIONS.BLOCK]: 100,
453
+ [ACTIONS.REQUIRE_APPROVAL]: 80,
454
+ [ACTIONS.ESCALATE]: 70,
455
+ [ACTIONS.WARN]: 50,
456
+ [ACTIONS.NOTIFY]: 30,
457
+ [ACTIONS.AUTO_FIX]: 20,
458
+ [ACTIONS.ALLOW]: 0,
459
+ };
460
+
461
+ let mostSevere = ACTIONS.ALLOW;
462
+ let maxPriority = 0;
463
+
464
+ for (const result of results) {
465
+ const priority = actionPriority[result.action] || 0;
466
+ if (priority > maxPriority) {
467
+ maxPriority = priority;
468
+ mostSevere = result.action;
469
+ }
470
+ }
471
+
472
+ return mostSevere;
473
+ }
474
+
475
+ /**
476
+ * Check if deployment should be blocked
477
+ */
478
+ shouldBlock(results) {
479
+ return results.some(r => r.action === ACTIONS.BLOCK);
480
+ }
481
+
482
+ /**
483
+ * Check if approval is required
484
+ */
485
+ requiresApproval(results) {
486
+ return results.some(r => r.action === ACTIONS.REQUIRE_APPROVAL);
487
+ }
488
+
489
+ /**
490
+ * Generate policy enforcement summary
491
+ */
492
+ generateSummary(results) {
493
+ const summary = {
494
+ totalPolicies: this.getAllPolicies().length,
495
+ evaluated: results.length,
496
+ blocked: results.filter(r => r.action === ACTIONS.BLOCK).length,
497
+ warnings: results.filter(r => r.action === ACTIONS.WARN).length,
498
+ requiresApproval: results.filter(r => r.action === ACTIONS.REQUIRE_APPROVAL).length,
499
+ finalAction: this.getMostSevereAction(results),
500
+ policies: results.map(r => ({
501
+ id: r.policyId,
502
+ name: r.policyName,
503
+ action: r.action,
504
+ message: r.message,
505
+ })),
506
+ };
507
+
508
+ return summary;
509
+ }
510
+
511
+ /**
512
+ * Create override request
513
+ */
514
+ createOverrideRequest(results, requestedBy, reason) {
515
+ const request = {
516
+ id: crypto.randomUUID(),
517
+ timestamp: new Date().toISOString(),
518
+ requestedBy,
519
+ reason,
520
+ policies: results.filter(r => r.action === ACTIONS.BLOCK).map(r => r.policyId),
521
+ status: "pending",
522
+ approvals: [],
523
+ expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours
524
+ };
525
+
526
+ // Save override request
527
+ const overridesDir = path.join(this.options.policiesDir, "overrides");
528
+ fs.mkdirSync(overridesDir, { recursive: true });
529
+ fs.writeFileSync(
530
+ path.join(overridesDir, `${request.id}.json`),
531
+ JSON.stringify(request, null, 2)
532
+ );
533
+
534
+ return request;
535
+ }
536
+
537
+ /**
538
+ * Approve override request
539
+ */
540
+ approveOverride(requestId, approvedBy) {
541
+ const overridesDir = path.join(this.options.policiesDir, "overrides");
542
+ const requestPath = path.join(overridesDir, `${requestId}.json`);
543
+
544
+ if (!fs.existsSync(requestPath)) {
545
+ throw new Error("Override request not found");
546
+ }
547
+
548
+ const request = JSON.parse(fs.readFileSync(requestPath, "utf-8"));
549
+
550
+ if (new Date(request.expiresAt) < new Date()) {
551
+ request.status = "expired";
552
+ } else {
553
+ request.approvals.push({
554
+ approvedBy,
555
+ timestamp: new Date().toISOString(),
556
+ });
557
+ request.status = "approved";
558
+ }
559
+
560
+ fs.writeFileSync(requestPath, JSON.stringify(request, null, 2));
561
+
562
+ return request;
563
+ }
564
+
565
+ /**
566
+ * Check if override is valid
567
+ */
568
+ isOverrideValid(requestId) {
569
+ const overridesDir = path.join(this.options.policiesDir, "overrides");
570
+ const requestPath = path.join(overridesDir, `${requestId}.json`);
571
+
572
+ if (!fs.existsSync(requestPath)) {
573
+ return false;
574
+ }
575
+
576
+ const request = JSON.parse(fs.readFileSync(requestPath, "utf-8"));
577
+
578
+ return request.status === "approved" && new Date(request.expiresAt) > new Date();
579
+ }
580
+ }
581
+
582
+ // Terminal UI rendering
583
+ let terminalUI;
584
+ try {
585
+ terminalUI = require("./terminal-ui");
586
+ } catch {
587
+ terminalUI = {
588
+ c: { reset: "", bold: "", dim: "" },
589
+ rgb: () => "",
590
+ };
591
+ }
592
+
593
+ const { c, rgb } = terminalUI;
594
+
595
+ const colors = {
596
+ block: rgb(255, 80, 80),
597
+ warn: rgb(255, 200, 0),
598
+ allow: rgb(0, 255, 150),
599
+ approval: rgb(255, 150, 50),
600
+ };
601
+
602
+ /**
603
+ * Render policy results for terminal
604
+ */
605
+ function renderPolicyResults(results) {
606
+ const lines = [];
607
+
608
+ lines.push(` ${c.dim}╭${"─".repeat(60)}╮${c.reset}`);
609
+ lines.push(` ${c.dim}│${c.reset} ${rgb(0, 200, 255)}${c.bold}🛡️ POLICY ENFORCEMENT${c.reset} ${c.dim}│${c.reset}`);
610
+ lines.push(` ${c.dim}├${"─".repeat(60)}┤${c.reset}`);
611
+
612
+ if (results.length === 0) {
613
+ lines.push(` ${c.dim}│${c.reset} ${colors.allow}✓${c.reset} All policies passed ${c.dim}│${c.reset}`);
614
+ } else {
615
+ for (const result of results) {
616
+ const actionIcon = {
617
+ block: `${colors.block}✗`,
618
+ warn: `${colors.warn}⚠`,
619
+ allow: `${colors.allow}✓`,
620
+ require_approval: `${colors.approval}🔐`,
621
+ notify: `${rgb(100, 200, 255)}📢`,
622
+ }[result.action] || "•";
623
+
624
+ const actionColor = {
625
+ block: colors.block,
626
+ warn: colors.warn,
627
+ allow: colors.allow,
628
+ require_approval: colors.approval,
629
+ }[result.action] || c.dim;
630
+
631
+ lines.push(` ${c.dim}│${c.reset} ${actionIcon}${c.reset} ${result.policyName.padEnd(45)} ${actionColor}${result.action.toUpperCase().padEnd(6)}${c.reset}${c.dim}│${c.reset}`);
632
+
633
+ if (result.message) {
634
+ const msg = result.message.substring(0, 54);
635
+ lines.push(` ${c.dim}│${c.reset} ${c.dim}${msg}${c.reset}`.padEnd(73) + `${c.dim}│${c.reset}`);
636
+ }
637
+ }
638
+ }
639
+
640
+ lines.push(` ${c.dim}╰${"─".repeat(60)}╯${c.reset}`);
641
+
642
+ return lines.join("\n");
643
+ }
644
+
645
+ module.exports = {
646
+ PolicyEngine,
647
+ PolicyResult,
648
+ renderPolicyResults,
649
+ OPERATORS,
650
+ ACTIONS,
651
+ POLICY_TEMPLATES,
652
+ };