@vibecheckai/cli 3.2.0 → 3.2.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 (60) hide show
  1. package/bin/runners/lib/agent-firewall/change-packet/builder.js +214 -0
  2. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  3. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  4. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  5. package/bin/runners/lib/agent-firewall/claims/extractor.js +214 -0
  6. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  7. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  8. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  9. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +118 -0
  10. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  11. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +142 -0
  12. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  13. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  14. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  15. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  16. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  17. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  18. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  19. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  20. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  21. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  22. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  23. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  24. package/bin/runners/lib/agent-firewall/policy/default-policy.json +84 -0
  25. package/bin/runners/lib/agent-firewall/policy/engine.js +72 -0
  26. package/bin/runners/lib/agent-firewall/policy/loader.js +143 -0
  27. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  28. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  29. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +61 -0
  30. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +50 -0
  31. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +50 -0
  32. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  33. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  34. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  35. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  36. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  37. package/bin/runners/lib/agent-firewall/truthpack/loader.js +116 -0
  38. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  39. package/bin/runners/lib/analysis-core.js +198 -180
  40. package/bin/runners/lib/analyzers.js +1119 -536
  41. package/bin/runners/lib/cli-output.js +236 -210
  42. package/bin/runners/lib/detectors-v2.js +547 -785
  43. package/bin/runners/lib/fingerprint.js +377 -0
  44. package/bin/runners/lib/route-truth.js +1167 -322
  45. package/bin/runners/lib/scan-output.js +144 -738
  46. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  47. package/bin/runners/lib/terminal-ui.js +188 -770
  48. package/bin/runners/lib/truth.js +1004 -321
  49. package/bin/runners/lib/unified-output.js +162 -158
  50. package/bin/runners/runAgent.js +161 -0
  51. package/bin/runners/runFirewall.js +134 -0
  52. package/bin/runners/runFirewallHook.js +56 -0
  53. package/bin/runners/runScan.js +113 -10
  54. package/bin/runners/runShip.js +7 -8
  55. package/bin/runners/runTruth.js +89 -0
  56. package/mcp-server/agent-firewall-interceptor.js +164 -0
  57. package/mcp-server/index.js +347 -313
  58. package/mcp-server/truth-context.js +131 -90
  59. package/mcp-server/truth-firewall-tools.js +1412 -1045
  60. package/package.json +1 -1
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Scope Explosion Rule
3
+ *
4
+ * Blocks if too many files touched.
5
+ */
6
+
7
+ "use strict";
8
+
9
+ /**
10
+ * Evaluate scope explosion rule
11
+ * @param {object} params
12
+ * @param {array} params.files - Changed files
13
+ * @param {string} params.intent - Agent intent message
14
+ * @param {object} params.policy - Policy configuration
15
+ * @returns {object|null} Violation or null
16
+ */
17
+ function evaluate({ files, intent, policy }) {
18
+ const ruleConfig = policy.rules?.scope_explosion;
19
+
20
+ if (!ruleConfig || !ruleConfig.enabled) {
21
+ return null;
22
+ }
23
+
24
+ const scope = policy.scope || {};
25
+ const maxFiles = scope.max_files_touched || 10;
26
+ const maxLines = scope.max_lines_changed || 600;
27
+ const requireIntent = scope.require_intent_for_expand_scope || false;
28
+
29
+ const totalFiles = files.length;
30
+ const totalLines = files.reduce((sum, f) => sum + (f.linesChanged || 0), 0);
31
+
32
+ // Check file count
33
+ if (totalFiles > maxFiles) {
34
+ const hasIntent = intent && intent.trim().length > 0;
35
+
36
+ if (requireIntent && !hasIntent) {
37
+ return {
38
+ rule: "scope_explosion",
39
+ severity: ruleConfig.severity || "block",
40
+ message: `Scope explosion: ${totalFiles} files touched (max: ${maxFiles}). Intent required for scope expansion.`,
41
+ metadata: {
42
+ totalFiles,
43
+ maxFiles,
44
+ hasIntent
45
+ }
46
+ };
47
+ } else if (!requireIntent) {
48
+ return {
49
+ rule: "scope_explosion",
50
+ severity: ruleConfig.severity || "block",
51
+ message: `Scope explosion: ${totalFiles} files touched (max: ${maxFiles})`,
52
+ metadata: {
53
+ totalFiles,
54
+ maxFiles
55
+ }
56
+ };
57
+ }
58
+ }
59
+
60
+ // Check line count
61
+ if (totalLines > maxLines) {
62
+ const hasIntent = intent && intent.trim().length > 0;
63
+
64
+ if (requireIntent && !hasIntent) {
65
+ return {
66
+ rule: "scope_explosion",
67
+ severity: ruleConfig.severity || "block",
68
+ message: `Scope explosion: ${totalLines} lines changed (max: ${maxLines}). Intent required for scope expansion.`,
69
+ metadata: {
70
+ totalLines,
71
+ maxLines,
72
+ hasIntent
73
+ }
74
+ };
75
+ } else if (!requireIntent) {
76
+ return {
77
+ rule: "scope_explosion",
78
+ severity: ruleConfig.severity || "block",
79
+ message: `Scope explosion: ${totalLines} lines changed (max: ${maxLines})`,
80
+ metadata: {
81
+ totalLines,
82
+ maxLines
83
+ }
84
+ };
85
+ }
86
+ }
87
+
88
+ return null;
89
+ }
90
+
91
+ module.exports = {
92
+ evaluate
93
+ };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Unsafe Side Effect Rule
3
+ *
4
+ * Blocks unverified side effects.
5
+ */
6
+
7
+ "use strict";
8
+
9
+ const { CLAIM_TYPES } = require("../../claims/claim-types");
10
+
11
+ /**
12
+ * Evaluate unsafe side effect rule
13
+ * @param {object} params
14
+ * @param {array} params.claims - Extracted claims
15
+ * @param {array} params.evidence - Evidence resolution results
16
+ * @param {object} params.policy - Policy configuration
17
+ * @returns {object|null} Violation or null
18
+ */
19
+ function evaluate({ claims, evidence, policy }) {
20
+ const ruleConfig = policy.rules?.unsafe_side_effect;
21
+
22
+ if (!ruleConfig || !ruleConfig.enabled) {
23
+ return null;
24
+ }
25
+
26
+ const verification = policy.verification || {};
27
+ const requireForDomains = verification.require_for_domains || [];
28
+
29
+ // Find side effect claims
30
+ for (let i = 0; i < claims.length; i++) {
31
+ const claim = claims[i];
32
+
33
+ if (claim.type === CLAIM_TYPES.SIDE_EFFECT) {
34
+ const ev = evidence.find(e => e.claimId === `claim_${i}`);
35
+
36
+ // Check if domain requires verification
37
+ const claimDomain = claim.domain || "general";
38
+ const requiresVerification = requireForDomains.includes(claimDomain);
39
+
40
+ if (requiresVerification && ev && ev.result === "UNPROVEN") {
41
+ return {
42
+ rule: "unsafe_side_effect",
43
+ severity: ruleConfig.severity || "block",
44
+ message: `Unsafe side effect: ${claim.value} requires verification (test coverage or reality proof)`,
45
+ claimId: `claim_${i}`,
46
+ claim
47
+ };
48
+ }
49
+ }
50
+ }
51
+
52
+ return null;
53
+ }
54
+
55
+ module.exports = {
56
+ evaluate
57
+ };
@@ -0,0 +1,183 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["version", "mode", "scope", "rules"],
5
+ "properties": {
6
+ "version": {
7
+ "type": "string",
8
+ "description": "Policy schema version"
9
+ },
10
+ "mode": {
11
+ "type": "string",
12
+ "enum": ["observe", "enforce"],
13
+ "description": "Firewall mode: observe (log only) or enforce (block writes)"
14
+ },
15
+ "profile": {
16
+ "type": "string",
17
+ "description": "Policy profile name (e.g., 'repo-lock', 'balanced', 'permissive')"
18
+ },
19
+ "scope": {
20
+ "type": "object",
21
+ "properties": {
22
+ "max_files_touched": {
23
+ "type": "number",
24
+ "description": "Maximum number of files that can be touched in a single change"
25
+ },
26
+ "max_lines_changed": {
27
+ "type": "number",
28
+ "description": "Maximum number of lines that can be changed in a single change"
29
+ },
30
+ "blocked_paths": {
31
+ "type": "array",
32
+ "items": {
33
+ "type": "string"
34
+ },
35
+ "description": "Glob patterns for paths that are blocked from changes"
36
+ },
37
+ "allowed_paths": {
38
+ "type": "array",
39
+ "items": {
40
+ "type": "string"
41
+ },
42
+ "description": "Glob patterns for paths that are allowed for changes"
43
+ },
44
+ "require_intent_for_expand_scope": {
45
+ "type": "boolean",
46
+ "description": "Require explicit intent message when scope exceeds limits"
47
+ }
48
+ }
49
+ },
50
+ "hard_domains": {
51
+ "type": "object",
52
+ "properties": {
53
+ "routes": {
54
+ "type": "boolean",
55
+ "description": "Enforce route truth"
56
+ },
57
+ "env": {
58
+ "type": "boolean",
59
+ "description": "Enforce environment variable truth"
60
+ },
61
+ "auth": {
62
+ "type": "boolean",
63
+ "description": "Enforce authentication truth"
64
+ },
65
+ "contracts": {
66
+ "type": "boolean",
67
+ "description": "Enforce API contract truth"
68
+ },
69
+ "payments": {
70
+ "type": "boolean",
71
+ "description": "Enforce payment-related changes"
72
+ },
73
+ "side_effects": {
74
+ "type": "boolean",
75
+ "description": "Enforce side effect verification"
76
+ }
77
+ }
78
+ },
79
+ "rules": {
80
+ "type": "object",
81
+ "additionalProperties": {
82
+ "type": "object",
83
+ "properties": {
84
+ "severity": {
85
+ "type": "string",
86
+ "enum": ["allow", "warn", "block"],
87
+ "description": "Rule severity level"
88
+ },
89
+ "block_if_domain": {
90
+ "type": "array",
91
+ "items": {
92
+ "type": "string",
93
+ "enum": ["auth", "payments", "side_effects", "routes", "env", "contracts"]
94
+ },
95
+ "description": "Upgrade to block if change affects these domains"
96
+ },
97
+ "enabled": {
98
+ "type": "boolean",
99
+ "description": "Whether this rule is enabled"
100
+ }
101
+ }
102
+ }
103
+ },
104
+ "evidence": {
105
+ "type": "object",
106
+ "properties": {
107
+ "require_pointers": {
108
+ "type": "boolean",
109
+ "description": "Require file:line pointers in evidence"
110
+ },
111
+ "acceptable_sources": {
112
+ "type": "array",
113
+ "items": {
114
+ "type": "string",
115
+ "enum": ["truthpack.routes", "truthpack.env", "truthpack.auth", "truthpack.contracts", "repo.search"]
116
+ },
117
+ "description": "Acceptable evidence sources"
118
+ },
119
+ "pointer_format": {
120
+ "type": "string",
121
+ "description": "Expected pointer format (e.g., 'file:lineStart-lineEnd')"
122
+ }
123
+ }
124
+ },
125
+ "verification": {
126
+ "type": "object",
127
+ "properties": {
128
+ "require_for_domains": {
129
+ "type": "array",
130
+ "items": {
131
+ "type": "string",
132
+ "enum": ["auth", "payments", "side_effects"]
133
+ },
134
+ "description": "Domains that require verification"
135
+ },
136
+ "accepted": {
137
+ "type": "array",
138
+ "items": {
139
+ "type": "string",
140
+ "enum": ["tests", "reality"]
141
+ },
142
+ "description": "Accepted verification methods"
143
+ },
144
+ "reality": {
145
+ "type": "object",
146
+ "properties": {
147
+ "enabled": {
148
+ "type": "boolean"
149
+ },
150
+ "block_on": {
151
+ "type": "array",
152
+ "items": {
153
+ "type": "string",
154
+ "enum": ["fake_success", "no_mutation", "network_error"]
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ },
161
+ "output": {
162
+ "type": "object",
163
+ "properties": {
164
+ "write_change_packets": {
165
+ "type": "boolean",
166
+ "description": "Write change packets to disk"
167
+ },
168
+ "packet_dir": {
169
+ "type": "string",
170
+ "description": "Directory for change packets"
171
+ },
172
+ "report_formats": {
173
+ "type": "array",
174
+ "items": {
175
+ "type": "string",
176
+ "enum": ["md", "html", "json", "sarif"]
177
+ },
178
+ "description": "Report output formats"
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Verdict Generator
3
+ *
4
+ * Combines rule results into final verdict.
5
+ * Priority: BLOCK > WARN > ALLOW
6
+ */
7
+
8
+ "use strict";
9
+
10
+ /**
11
+ * Generate verdict from violations
12
+ * @param {array} violations - Array of rule violations
13
+ * @returns {object} Verdict object
14
+ */
15
+ function generateVerdict(violations) {
16
+ if (!violations || violations.length === 0) {
17
+ return {
18
+ decision: "ALLOW",
19
+ violations: [],
20
+ message: "No violations detected"
21
+ };
22
+ }
23
+
24
+ // Check for blocks (highest priority)
25
+ const blocks = violations.filter(v => v.severity === "block");
26
+ if (blocks.length > 0) {
27
+ return {
28
+ decision: "BLOCK",
29
+ violations,
30
+ message: `BLOCKED: ${blocks.length} blocking violation(s) found`
31
+ };
32
+ }
33
+
34
+ // Check for warnings
35
+ const warns = violations.filter(v => v.severity === "warn");
36
+ if (warns.length > 0) {
37
+ return {
38
+ decision: "WARN",
39
+ violations,
40
+ message: `WARNING: ${warns.length} warning(s) found`
41
+ };
42
+ }
43
+
44
+ // Only allows (shouldn't happen, but handle gracefully)
45
+ return {
46
+ decision: "ALLOW",
47
+ violations,
48
+ message: "Violations found but all are allow-level"
49
+ };
50
+ }
51
+
52
+ module.exports = {
53
+ generateVerdict
54
+ };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Truthpack Accessor
3
+ *
4
+ * Unified interface for accessing truthpack data.
5
+ */
6
+
7
+ "use strict";
8
+
9
+ const { loadTruthpack } = require("./loader");
10
+
11
+ /**
12
+ * Get routes from truthpack
13
+ * @param {string} projectRoot - Project root directory
14
+ * @returns {array} Array of routes
15
+ */
16
+ function getRoutes(projectRoot) {
17
+ const truthpack = loadTruthpack(projectRoot);
18
+ return truthpack.routes?.routes || [];
19
+ }
20
+
21
+ /**
22
+ * Get environment variables from truthpack
23
+ * @param {string} projectRoot - Project root directory
24
+ * @returns {object} Env vars data
25
+ */
26
+ function getEnvVars(projectRoot) {
27
+ const truthpack = loadTruthpack(projectRoot);
28
+ return truthpack.env || { vars: [], declared: [], declaredSources: [] };
29
+ }
30
+
31
+ /**
32
+ * Get auth rules from truthpack
33
+ * @param {string} projectRoot - Project root directory
34
+ * @returns {object} Auth rules data
35
+ */
36
+ function getAuthRules(projectRoot) {
37
+ const truthpack = loadTruthpack(projectRoot);
38
+ return truthpack.auth || { nextMiddleware: [], nextMatcherPatterns: [], fastify: {} };
39
+ }
40
+
41
+ /**
42
+ * Get contracts from truthpack
43
+ * @param {string} projectRoot - Project root directory
44
+ * @returns {object} Contracts data
45
+ */
46
+ function getContracts(projectRoot) {
47
+ const truthpack = loadTruthpack(projectRoot);
48
+ return truthpack.contracts || {};
49
+ }
50
+
51
+ /**
52
+ * Get UI graph from truthpack
53
+ * @param {string} projectRoot - Project root directory
54
+ * @returns {object|null} UI graph or null
55
+ */
56
+ function getUIGraph(projectRoot) {
57
+ const truthpack = loadTruthpack(projectRoot);
58
+ return truthpack.uiGraph || null;
59
+ }
60
+
61
+ module.exports = {
62
+ getRoutes,
63
+ getEnvVars,
64
+ getAuthRules,
65
+ getContracts,
66
+ getUIGraph
67
+ };
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Truthpack Loader
3
+ *
4
+ * Loads truthpack files from .vibecheck/truthpack/
5
+ * Caches for performance and validates freshness.
6
+ */
7
+
8
+ "use strict";
9
+
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+
13
+ const TRUTHPACK_DIR = ".vibecheck/truthpack";
14
+ const STALE_AFTER_HOURS = 24;
15
+
16
+ let cache = {
17
+ routes: null,
18
+ env: null,
19
+ auth: null,
20
+ contracts: null,
21
+ loadedAt: null
22
+ };
23
+
24
+ /**
25
+ * Load truthpack from project root
26
+ * @param {string} projectRoot - Project root directory
27
+ * @param {boolean} forceRefresh - Force reload even if cached
28
+ * @returns {object} Truthpack data
29
+ */
30
+ function loadTruthpack(projectRoot, forceRefresh = false) {
31
+ const truthpackPath = path.join(projectRoot, TRUTHPACK_DIR);
32
+
33
+ // Check cache freshness
34
+ if (!forceRefresh && cache.loadedAt) {
35
+ const ageHours = (Date.now() - cache.loadedAt) / (1000 * 60 * 60);
36
+ if (ageHours < STALE_AFTER_HOURS && cache.routes !== null) {
37
+ return cache;
38
+ }
39
+ }
40
+
41
+ const truthpack = {
42
+ routes: loadTruthpackFile(projectRoot, "routes.json"),
43
+ env: loadTruthpackFile(projectRoot, "env.json"),
44
+ auth: loadTruthpackFile(projectRoot, "auth.json"),
45
+ contracts: loadTruthpackFile(projectRoot, "contracts.json"),
46
+ uiGraph: loadTruthpackFile(projectRoot, "ui.graph.json")
47
+ };
48
+
49
+ // Update cache
50
+ cache = {
51
+ ...truthpack,
52
+ loadedAt: Date.now()
53
+ };
54
+
55
+ return truthpack;
56
+ }
57
+
58
+ /**
59
+ * Load a specific truthpack file
60
+ * @param {string} projectRoot - Project root directory
61
+ * @param {string} filename - Truthpack filename
62
+ * @returns {object|null} Parsed JSON or null if not found
63
+ */
64
+ function loadTruthpackFile(projectRoot, filename) {
65
+ const filePath = path.join(projectRoot, TRUTHPACK_DIR, filename);
66
+
67
+ if (!fs.existsSync(filePath)) {
68
+ return null;
69
+ }
70
+
71
+ try {
72
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
73
+ } catch (error) {
74
+ console.warn(`Failed to load truthpack file ${filename}: ${error.message}`);
75
+ return null;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Check if truthpack is fresh
81
+ * @param {string} projectRoot - Project root directory
82
+ * @returns {boolean} True if truthpack exists and is fresh
83
+ */
84
+ function isTruthpackFresh(projectRoot) {
85
+ const truthpackPath = path.join(projectRoot, TRUTHPACK_DIR);
86
+ const routesPath = path.join(truthpackPath, "routes.json");
87
+
88
+ if (!fs.existsSync(routesPath)) {
89
+ return false;
90
+ }
91
+
92
+ const stats = fs.statSync(routesPath);
93
+ const ageHours = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60);
94
+
95
+ return ageHours < STALE_AFTER_HOURS;
96
+ }
97
+
98
+ /**
99
+ * Clear truthpack cache
100
+ */
101
+ function clearCache() {
102
+ cache = {
103
+ routes: null,
104
+ env: null,
105
+ auth: null,
106
+ contracts: null,
107
+ loadedAt: null
108
+ };
109
+ }
110
+
111
+ module.exports = {
112
+ loadTruthpack,
113
+ loadTruthpackFile,
114
+ isTruthpackFresh,
115
+ clearCache
116
+ };