@vibecheckai/cli 3.5.0 → 3.5.2

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 (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -1,652 +0,0 @@
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
- };