guardrail-core 1.0.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 (189) hide show
  1. package/dist/__tests__/autopilot.test.d.ts +7 -0
  2. package/dist/__tests__/autopilot.test.d.ts.map +1 -0
  3. package/dist/__tests__/autopilot.test.js +156 -0
  4. package/dist/__tests__/tier-config.test.d.ts +9 -0
  5. package/dist/__tests__/tier-config.test.d.ts.map +1 -0
  6. package/dist/__tests__/tier-config.test.js +230 -0
  7. package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
  8. package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
  9. package/dist/__tests__/utils/hash-inline.test.js +62 -0
  10. package/dist/__tests__/utils/hash.test.d.ts +3 -0
  11. package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
  12. package/dist/__tests__/utils/hash.test.js +95 -0
  13. package/dist/__tests__/utils/simple.test.d.ts +1 -0
  14. package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
  15. package/dist/__tests__/utils/simple.test.js +10 -0
  16. package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
  17. package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
  18. package/dist/__tests__/utils/utils-simple.test.js +6 -0
  19. package/dist/__tests__/utils/utils.test.d.ts +15 -0
  20. package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
  21. package/dist/__tests__/utils/utils.test.js +172 -0
  22. package/dist/autopilot/autopilot-runner.d.ts +33 -0
  23. package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
  24. package/dist/autopilot/autopilot-runner.js +479 -0
  25. package/dist/autopilot/index.d.ts +6 -0
  26. package/dist/autopilot/index.d.ts.map +1 -0
  27. package/dist/autopilot/index.js +25 -0
  28. package/dist/autopilot/types.d.ts +102 -0
  29. package/dist/autopilot/types.d.ts.map +1 -0
  30. package/dist/autopilot/types.js +18 -0
  31. package/dist/cache/index.d.ts +7 -0
  32. package/dist/cache/index.d.ts.map +1 -0
  33. package/dist/cache/index.js +22 -0
  34. package/dist/cache/redis-cache.d.ts +145 -0
  35. package/dist/cache/redis-cache.d.ts.map +1 -0
  36. package/dist/cache/redis-cache.js +459 -0
  37. package/dist/ci/github-actions.d.ts +77 -0
  38. package/dist/ci/github-actions.d.ts.map +1 -0
  39. package/dist/ci/github-actions.js +277 -0
  40. package/dist/ci/index.d.ts +12 -0
  41. package/dist/ci/index.d.ts.map +1 -0
  42. package/dist/ci/index.js +27 -0
  43. package/dist/ci/pre-commit.d.ts +65 -0
  44. package/dist/ci/pre-commit.d.ts.map +1 -0
  45. package/dist/ci/pre-commit.js +286 -0
  46. package/dist/entitlements.d.ts +149 -0
  47. package/dist/entitlements.d.ts.map +1 -0
  48. package/dist/entitlements.js +464 -0
  49. package/dist/env.d.ts +113 -0
  50. package/dist/env.d.ts.map +1 -0
  51. package/dist/env.js +204 -0
  52. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
  53. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
  54. package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
  55. package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
  56. package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
  57. package/dist/fix-packs/generate-fix-packs.js +505 -0
  58. package/dist/fix-packs/index.d.ts +8 -0
  59. package/dist/fix-packs/index.d.ts.map +1 -0
  60. package/dist/fix-packs/index.js +23 -0
  61. package/dist/fix-packs/types.d.ts +113 -0
  62. package/dist/fix-packs/types.d.ts.map +1 -0
  63. package/dist/fix-packs/types.js +71 -0
  64. package/dist/index.d.ts +13 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +28 -0
  67. package/dist/metrics/prometheus.d.ts +99 -0
  68. package/dist/metrics/prometheus.d.ts.map +1 -0
  69. package/dist/metrics/prometheus.js +306 -0
  70. package/dist/quota-ledger.d.ts +119 -0
  71. package/dist/quota-ledger.d.ts.map +1 -0
  72. package/dist/quota-ledger.js +462 -0
  73. package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
  74. package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
  75. package/dist/rbac/__tests__/permissions.test.js +350 -0
  76. package/dist/rbac/index.d.ts +9 -0
  77. package/dist/rbac/index.d.ts.map +1 -0
  78. package/dist/rbac/index.js +32 -0
  79. package/dist/rbac/permissions.d.ts +71 -0
  80. package/dist/rbac/permissions.d.ts.map +1 -0
  81. package/dist/rbac/permissions.js +247 -0
  82. package/dist/rbac/types.d.ts +69 -0
  83. package/dist/rbac/types.d.ts.map +1 -0
  84. package/dist/rbac/types.js +213 -0
  85. package/dist/tier-config.d.ts +203 -0
  86. package/dist/tier-config.d.ts.map +1 -0
  87. package/dist/tier-config.js +675 -0
  88. package/dist/types.d.ts +365 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +5 -0
  91. package/dist/utils.d.ts +36 -0
  92. package/dist/utils.d.ts.map +1 -0
  93. package/dist/utils.js +127 -0
  94. package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
  95. package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
  96. package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
  97. package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
  98. package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
  99. package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
  100. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
  101. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
  102. package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
  103. package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
  104. package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
  105. package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
  106. package/dist/verified-autofix/format-validator.d.ts +101 -0
  107. package/dist/verified-autofix/format-validator.d.ts.map +1 -0
  108. package/dist/verified-autofix/format-validator.js +446 -0
  109. package/dist/verified-autofix/index.d.ts +14 -0
  110. package/dist/verified-autofix/index.d.ts.map +1 -0
  111. package/dist/verified-autofix/index.js +39 -0
  112. package/dist/verified-autofix/pipeline.d.ts +68 -0
  113. package/dist/verified-autofix/pipeline.d.ts.map +1 -0
  114. package/dist/verified-autofix/pipeline.js +330 -0
  115. package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
  116. package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
  117. package/dist/verified-autofix/repo-fingerprint.js +396 -0
  118. package/dist/verified-autofix/workspace.d.ts +83 -0
  119. package/dist/verified-autofix/workspace.d.ts.map +1 -0
  120. package/dist/verified-autofix/workspace.js +454 -0
  121. package/dist/verified-autofix.d.ts +182 -0
  122. package/dist/verified-autofix.d.ts.map +1 -0
  123. package/dist/verified-autofix.js +1021 -0
  124. package/dist/visualization/dependency-graph.d.ts +79 -0
  125. package/dist/visualization/dependency-graph.d.ts.map +1 -0
  126. package/dist/visualization/dependency-graph.js +399 -0
  127. package/dist/visualization/index.d.ts +5 -0
  128. package/dist/visualization/index.d.ts.map +1 -0
  129. package/dist/visualization/index.js +20 -0
  130. package/package.json +29 -0
  131. package/src/__tests__/autopilot.test.ts +196 -0
  132. package/src/__tests__/tier-config.test.ts +289 -0
  133. package/src/__tests__/utils/hash-inline.test.ts +76 -0
  134. package/src/__tests__/utils/hash.test.ts +119 -0
  135. package/src/__tests__/utils/simple.test.ts +10 -0
  136. package/src/__tests__/utils/utils-simple.test.ts +5 -0
  137. package/src/__tests__/utils/utils.test.ts +203 -0
  138. package/src/autopilot/autopilot-runner.ts +503 -0
  139. package/src/autopilot/index.ts +6 -0
  140. package/src/autopilot/types.ts +119 -0
  141. package/src/cache/index.ts +7 -0
  142. package/src/cache/redis-cache.d.ts +155 -0
  143. package/src/cache/redis-cache.d.ts.map +1 -0
  144. package/src/cache/redis-cache.ts +517 -0
  145. package/src/ci/github-actions.ts +335 -0
  146. package/src/ci/index.ts +12 -0
  147. package/src/ci/pre-commit.ts +338 -0
  148. package/src/db/usage-schema.prisma +114 -0
  149. package/src/entitlements.ts +570 -0
  150. package/src/env.d.ts +68 -0
  151. package/src/env.d.ts.map +1 -0
  152. package/src/env.ts +247 -0
  153. package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
  154. package/src/fix-packs/generate-fix-packs.ts +577 -0
  155. package/src/fix-packs/index.ts +8 -0
  156. package/src/fix-packs/types.ts +206 -0
  157. package/src/index.d.ts +7 -0
  158. package/src/index.d.ts.map +1 -0
  159. package/src/index.ts +12 -0
  160. package/src/metrics/prometheus.d.ts +104 -0
  161. package/src/metrics/prometheus.d.ts.map +1 -0
  162. package/src/metrics/prometheus.ts +446 -0
  163. package/src/quota-ledger.ts +548 -0
  164. package/src/rbac/__tests__/permissions.test.ts +446 -0
  165. package/src/rbac/index.ts +46 -0
  166. package/src/rbac/permissions.ts +301 -0
  167. package/src/rbac/types.ts +298 -0
  168. package/src/tier-config.json +157 -0
  169. package/src/tier-config.ts +815 -0
  170. package/src/types.d.ts +365 -0
  171. package/src/types.d.ts.map +1 -0
  172. package/src/types.ts +441 -0
  173. package/src/utils.d.ts +36 -0
  174. package/src/utils.d.ts.map +1 -0
  175. package/src/utils.ts +140 -0
  176. package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
  177. package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
  178. package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
  179. package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
  180. package/src/verified-autofix/format-validator.ts +517 -0
  181. package/src/verified-autofix/index.ts +63 -0
  182. package/src/verified-autofix/pipeline.ts +403 -0
  183. package/src/verified-autofix/repo-fingerprint.ts +459 -0
  184. package/src/verified-autofix/workspace.ts +531 -0
  185. package/src/verified-autofix.ts +1187 -0
  186. package/src/visualization/dependency-graph.d.ts +85 -0
  187. package/src/visualization/dependency-graph.d.ts.map +1 -0
  188. package/src/visualization/dependency-graph.ts +495 -0
  189. package/src/visualization/index.ts +5 -0
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ const crypto = require('node:crypto');
5
+ // Inline implementations for testing
6
+ function generateCorrelationId() {
7
+ return `corr_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
8
+ }
9
+ function generateTaskId() {
10
+ return `task_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;
11
+ }
12
+ function calculateHash(content) {
13
+ return crypto.createHash('sha256').update(content).digest('hex');
14
+ }
15
+ function calculateEntropy(str) {
16
+ const len = str.length;
17
+ const frequencies = {};
18
+ for (let i = 0; i < len; i++) {
19
+ const char = str[i];
20
+ if (char) {
21
+ frequencies[char] = (frequencies[char] || 0) + 1;
22
+ }
23
+ }
24
+ let entropy = 0;
25
+ for (const char in frequencies) {
26
+ const frequency = frequencies[char];
27
+ if (frequency !== undefined) {
28
+ const p = frequency / len;
29
+ entropy -= p * Math.log2(p);
30
+ }
31
+ }
32
+ return entropy;
33
+ }
34
+ function maskSensitiveValue(value) {
35
+ if (value.length <= 8) {
36
+ return '***';
37
+ }
38
+ return `${value.substring(0, 4)}...${value.substring(value.length - 4)}`;
39
+ }
40
+ function isPathAllowed(path, allowedPaths, deniedPaths) {
41
+ const normalizedPath = path.replace(/\\/g, '/');
42
+ // Check denied paths first (more restrictive)
43
+ for (const deniedPath of deniedPaths) {
44
+ if (normalizedPath.startsWith(deniedPath.replace(/\\/g, '/'))) {
45
+ return false;
46
+ }
47
+ }
48
+ // If no allowed paths specified, allow all (except denied)
49
+ if (allowedPaths.length === 0) {
50
+ return true;
51
+ }
52
+ // Check allowed paths
53
+ for (const allowedPath of allowedPaths) {
54
+ if (normalizedPath.startsWith(allowedPath.replace(/\\/g, '/'))) {
55
+ return true;
56
+ }
57
+ }
58
+ return false;
59
+ }
60
+ function isDomainAllowed(url, allowedDomains, deniedDomains) {
61
+ try {
62
+ const urlObj = new URL(url);
63
+ const hostname = urlObj.hostname;
64
+ // Check denied domains first
65
+ for (const deniedDomain of deniedDomains) {
66
+ if (hostname === deniedDomain || hostname.endsWith(`.${deniedDomain}`)) {
67
+ return false;
68
+ }
69
+ }
70
+ // If no allowed domains specified, allow all (except denied)
71
+ if (allowedDomains.length === 0) {
72
+ return true;
73
+ }
74
+ // Check allowed domains
75
+ for (const allowedDomain of allowedDomains) {
76
+ if (hostname === allowedDomain || hostname.endsWith(`.${allowedDomain}`)) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ catch (error) {
83
+ return false;
84
+ }
85
+ }
86
+ function sanitizeError(error) {
87
+ if (error instanceof Error) {
88
+ return {
89
+ message: error.message.replace(/\/[^\s:]+/g, '[path]'),
90
+ code: error.code,
91
+ };
92
+ }
93
+ return { message: 'Unknown error occurred' };
94
+ }
95
+ describe('Core Utils', () => {
96
+ describe('generateCorrelationId', () => {
97
+ it('should generate a correlation ID with correct format', () => {
98
+ const id = generateCorrelationId();
99
+ expect(id).toMatch(/^corr_\d+_[a-f0-9]{16}$/);
100
+ });
101
+ it('should generate unique IDs', () => {
102
+ const id1 = generateCorrelationId();
103
+ const id2 = generateCorrelationId();
104
+ expect(id1).not.toBe(id2);
105
+ });
106
+ });
107
+ describe('generateTaskId', () => {
108
+ it('should generate a task ID with correct format', () => {
109
+ const id = generateTaskId();
110
+ expect(id).toMatch(/^task_\d+_[a-f0-9]{16}$/);
111
+ });
112
+ });
113
+ describe('calculateEntropy', () => {
114
+ it('should calculate entropy for string with all unique characters', () => {
115
+ const entropy = calculateEntropy('abcdef');
116
+ expect(entropy).toBeCloseTo(2.585, 2);
117
+ });
118
+ it('should calculate entropy for string with repeated characters', () => {
119
+ const entropy = calculateEntropy('aaaaaa');
120
+ expect(entropy).toBe(0);
121
+ });
122
+ it('should handle empty string', () => {
123
+ const entropy = calculateEntropy('');
124
+ expect(entropy).toBe(0);
125
+ });
126
+ });
127
+ describe('maskSensitiveValue', () => {
128
+ it('should mask long values correctly', () => {
129
+ const value = '1234567890123456';
130
+ const masked = maskSensitiveValue(value);
131
+ expect(masked).toBe('1234...3456');
132
+ });
133
+ it('should mask short values with asterisks', () => {
134
+ expect(maskSensitiveValue('12345678')).toBe('***');
135
+ });
136
+ });
137
+ describe('isPathAllowed', () => {
138
+ it('should allow paths in allowed list', () => {
139
+ const allowed = ['/src', '/lib'];
140
+ const denied = [];
141
+ expect(isPathAllowed('/src/app.ts', allowed, denied)).toBe(true);
142
+ });
143
+ it('should deny paths in denied list', () => {
144
+ const allowed = ['/src'];
145
+ const denied = ['/src/secret'];
146
+ expect(isPathAllowed('/src/secret/config.ts', allowed, denied)).toBe(false);
147
+ });
148
+ });
149
+ describe('isDomainAllowed', () => {
150
+ it('should allow domains in allowed list', () => {
151
+ const allowed = ['example.com'];
152
+ const denied = [];
153
+ expect(isDomainAllowed('https://example.com/path', allowed, denied)).toBe(true);
154
+ });
155
+ it('should deny domains in denied list', () => {
156
+ const allowed = [];
157
+ const denied = ['malicious.com'];
158
+ expect(isDomainAllowed('https://malicious.com', allowed, denied)).toBe(false);
159
+ });
160
+ });
161
+ describe('sanitizeError', () => {
162
+ it('should sanitize error message', () => {
163
+ const error = new Error('Failed to read /home/user/secret.txt');
164
+ const sanitized = sanitizeError(error);
165
+ expect(sanitized.message).toBe('Failed to read [path]');
166
+ });
167
+ it('should handle non-Error objects', () => {
168
+ const sanitized = sanitizeError('string error');
169
+ expect(sanitized.message).toBe('Unknown error occurred');
170
+ });
171
+ });
172
+ });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Autopilot Runner - PRO/COMPLIANCE+ Feature
3
+ *
4
+ * Batch remediation system that:
5
+ * 1. Scans for issues using existing scanners
6
+ * 2. Groups findings into Fix Packs
7
+ * 3. Generates verified patches
8
+ * 4. Applies in temp workspace
9
+ * 5. Re-scans to verify
10
+ * 6. Outputs final verdict
11
+ */
12
+ import { AutopilotOptions, AutopilotResult, AutopilotFinding, AutopilotFixPack } from './types';
13
+ export declare class AutopilotRunner {
14
+ private tempDir;
15
+ constructor();
16
+ run(options: AutopilotOptions): Promise<AutopilotResult>;
17
+ private runPlan;
18
+ private runApply;
19
+ private runScan;
20
+ groupIntoFixPacks(findings: AutopilotFinding[], maxFixes?: number): AutopilotFixPack[];
21
+ private getCategoryName;
22
+ private getCategoryDescription;
23
+ private createTempWorkspace;
24
+ private copyProject;
25
+ private applyFixPack;
26
+ private runVerification;
27
+ private applyToProject;
28
+ private cleanupWorkspace;
29
+ private findSourceFiles;
30
+ }
31
+ export declare const autopilotRunner: AutopilotRunner;
32
+ export declare const runAutopilot: (options: AutopilotOptions) => Promise<AutopilotResult>;
33
+ //# sourceMappingURL=autopilot-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autopilot-runner.d.ts","sourceRoot":"","sources":["../../src/autopilot/autopilot-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,EACL,gBAAgB,EAChB,eAAe,EAGf,gBAAgB,EAChB,gBAAgB,EAMjB,MAAM,SAAS,CAAC;AAKjB,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAS;;IAMlB,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;YAiBhD,OAAO;YAoCP,QAAQ;YAyHR,OAAO;IA2ErB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAkCtF,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,sBAAsB;YAahB,mBAAmB;YAyBnB,WAAW;YAkBX,YAAY;YA4BZ,eAAe;YAiCf,cAAc;YAgBd,gBAAgB;IAY9B,OAAO,CAAC,eAAe;CAiBxB;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC;AACrD,eAAO,MAAM,YAAY,GAAI,SAAS,gBAAgB,6BAAiC,CAAC"}
@@ -0,0 +1,479 @@
1
+ "use strict";
2
+ /**
3
+ * Autopilot Runner - PRO/COMPLIANCE+ Feature
4
+ *
5
+ * Batch remediation system that:
6
+ * 1. Scans for issues using existing scanners
7
+ * 2. Groups findings into Fix Packs
8
+ * 3. Generates verified patches
9
+ * 4. Applies in temp workspace
10
+ * 5. Re-scans to verify
11
+ * 6. Outputs final verdict
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.runAutopilot = exports.autopilotRunner = exports.AutopilotRunner = void 0;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ const crypto = __importStar(require("crypto"));
52
+ const child_process_1 = require("child_process");
53
+ const types_1 = require("./types");
54
+ const entitlements_1 = require("../entitlements");
55
+ const entitlements = new entitlements_1.EntitlementsManager();
56
+ class AutopilotRunner {
57
+ tempDir;
58
+ constructor() {
59
+ this.tempDir = path.join(os.tmpdir(), 'guardrail-autopilot');
60
+ }
61
+ async run(options) {
62
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
63
+ await entitlements.enforceFeature('autopilot');
64
+ const limitCheck = await entitlements.checkLimit('scans');
65
+ if (!limitCheck.allowed) {
66
+ throw new Error(limitCheck.reason || 'Scan limit exceeded');
67
+ }
68
+ }
69
+ if (options.mode === 'plan') {
70
+ return this.runPlan(options);
71
+ }
72
+ else {
73
+ return this.runApply(options);
74
+ }
75
+ }
76
+ async runPlan(options) {
77
+ const startTime = Date.now();
78
+ options.onProgress?.('scan', 'Running initial scan...');
79
+ const scanResult = await this.runScan(options.projectPath, options.profile || 'ship');
80
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
81
+ await entitlements.trackUsage('scans', 1);
82
+ }
83
+ options.onProgress?.('group', 'Grouping findings into fix packs...');
84
+ const packs = this.groupIntoFixPacks(scanResult.findings, options.maxFixes);
85
+ const fixableCount = scanResult.findings.filter(f => f.fixable).length;
86
+ const riskAssessment = {
87
+ low: packs.filter(p => p.estimatedRisk === 'low').length,
88
+ medium: packs.filter(p => p.estimatedRisk === 'medium').length,
89
+ high: packs.filter(p => p.estimatedRisk === 'high').length,
90
+ };
91
+ void (Date.now() - startTime); // Duration tracked for future logging
92
+ const estimatedApplyTime = Math.ceil((packs.length * 30 + 60) / 60);
93
+ return {
94
+ mode: 'plan',
95
+ projectPath: options.projectPath,
96
+ profile: options.profile || 'ship',
97
+ timestamp: new Date().toISOString(),
98
+ totalFindings: scanResult.findings.length,
99
+ fixableFindings: fixableCount,
100
+ packs,
101
+ estimatedDuration: `${estimatedApplyTime} min`,
102
+ riskAssessment,
103
+ };
104
+ }
105
+ async runApply(options) {
106
+ const startTime = new Date();
107
+ const appliedFixes = [];
108
+ const errors = [];
109
+ let verification = null;
110
+ options.onProgress?.('scan', 'Running initial scan...');
111
+ const initialScan = await this.runScan(options.projectPath, options.profile || 'ship');
112
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
113
+ await entitlements.trackUsage('scans', 1);
114
+ }
115
+ options.onProgress?.('group', 'Grouping findings into fix packs...');
116
+ const packs = this.groupIntoFixPacks(initialScan.findings, options.maxFixes);
117
+ if (packs.length === 0) {
118
+ return {
119
+ mode: 'apply',
120
+ projectPath: options.projectPath,
121
+ profile: options.profile || 'ship',
122
+ timestamp: new Date().toISOString(),
123
+ startTime: startTime.toISOString(),
124
+ endTime: new Date().toISOString(),
125
+ duration: Date.now() - startTime.getTime(),
126
+ packsAttempted: 0,
127
+ packsSucceeded: 0,
128
+ packsFailed: 0,
129
+ appliedFixes,
130
+ verification: null,
131
+ remainingFindings: initialScan.findings.length,
132
+ newScanVerdict: 'skipped',
133
+ errors: ['No fixable issues found'],
134
+ };
135
+ }
136
+ let workspacePath = null;
137
+ let packsSucceeded = 0;
138
+ let packsFailed = 0;
139
+ try {
140
+ options.onProgress?.('workspace', 'Creating temp workspace...');
141
+ workspacePath = await this.createTempWorkspace(options.projectPath, options.branchStrategy);
142
+ for (const pack of packs) {
143
+ options.onProgress?.('fix', `Applying ${pack.name}...`);
144
+ try {
145
+ const fixes = await this.applyFixPack(workspacePath, pack, options);
146
+ appliedFixes.push(...fixes);
147
+ const successCount = fixes.filter(f => f.success).length;
148
+ if (successCount > 0) {
149
+ packsSucceeded++;
150
+ }
151
+ else {
152
+ packsFailed++;
153
+ }
154
+ }
155
+ catch (e) {
156
+ packsFailed++;
157
+ errors.push(`Pack ${pack.id}: ${e.message}`);
158
+ }
159
+ }
160
+ if (options.verify !== false) {
161
+ options.onProgress?.('verify', 'Running verification...');
162
+ verification = await this.runVerification(workspacePath, options.profile || 'ship');
163
+ if (verification.passed && !options.dryRun) {
164
+ options.onProgress?.('apply', 'Applying changes to project...');
165
+ await this.applyToProject(options.projectPath, workspacePath);
166
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
167
+ await entitlements.trackUsage('fixRuns', appliedFixes.filter(f => f.success).length);
168
+ }
169
+ }
170
+ }
171
+ else if (!options.dryRun) {
172
+ options.onProgress?.('apply', 'Applying changes (unverified)...');
173
+ await this.applyToProject(options.projectPath, workspacePath);
174
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
175
+ await entitlements.trackUsage('fixRuns', appliedFixes.filter(f => f.success).length);
176
+ }
177
+ }
178
+ }
179
+ finally {
180
+ if (workspacePath) {
181
+ await this.cleanupWorkspace(workspacePath, options.projectPath);
182
+ }
183
+ }
184
+ let newScanVerdict = 'skipped';
185
+ let remainingFindings = initialScan.findings.length;
186
+ if (verification?.passed && !options.dryRun) {
187
+ options.onProgress?.('rescan', 'Running verification scan...');
188
+ const finalScan = await this.runScan(options.projectPath, options.profile || 'ship');
189
+ remainingFindings = finalScan.findings.length;
190
+ newScanVerdict = finalScan.verdict;
191
+ if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] !== '1') {
192
+ await entitlements.trackUsage('scans', 1);
193
+ }
194
+ }
195
+ const endTime = new Date();
196
+ return {
197
+ mode: 'apply',
198
+ projectPath: options.projectPath,
199
+ profile: options.profile || 'ship',
200
+ timestamp: new Date().toISOString(),
201
+ startTime: startTime.toISOString(),
202
+ endTime: endTime.toISOString(),
203
+ duration: endTime.getTime() - startTime.getTime(),
204
+ packsAttempted: packs.length,
205
+ packsSucceeded,
206
+ packsFailed,
207
+ appliedFixes,
208
+ verification,
209
+ remainingFindings,
210
+ newScanVerdict,
211
+ errors,
212
+ };
213
+ }
214
+ async runScan(projectPath, _profile) {
215
+ const findings = [];
216
+ try {
217
+ const cmd = `npx tsc --noEmit 2>&1 || true`;
218
+ const output = (0, child_process_1.execSync)(cmd, { cwd: projectPath, encoding: 'utf8', timeout: 60000 });
219
+ const errorRegex = /(.+)\((\d+),\d+\):\s*error\s+TS(\d+):\s*(.+)/g;
220
+ let match;
221
+ while ((match = errorRegex.exec(output)) !== null) {
222
+ const [, fileName, lineNum, errorCode, errorMsg] = match;
223
+ if (fileName && lineNum && errorCode && errorMsg) {
224
+ findings.push({
225
+ id: `TS${errorCode}-${crypto.randomBytes(4).toString('hex')}`,
226
+ category: 'type-errors',
227
+ severity: 'high',
228
+ file: fileName,
229
+ line: parseInt(lineNum, 10),
230
+ message: errorMsg,
231
+ fixable: true,
232
+ });
233
+ }
234
+ }
235
+ }
236
+ catch (e) {
237
+ // TypeScript not available or error
238
+ }
239
+ try {
240
+ const files = this.findSourceFiles(projectPath);
241
+ for (const file of files.slice(0, 100)) {
242
+ const content = fs.readFileSync(file, 'utf8');
243
+ const relPath = path.relative(projectPath, file);
244
+ const todoMatches = content.matchAll(/\/\/\s*TODO[:\s](.+)/gi);
245
+ for (const match of todoMatches) {
246
+ const line = content.substring(0, match.index ?? 0).split('\n').length;
247
+ findings.push({
248
+ id: `TODO-${crypto.randomBytes(4).toString('hex')}`,
249
+ category: 'quality',
250
+ severity: 'low',
251
+ file: relPath,
252
+ line,
253
+ message: `Unresolved TODO: ${(match[1] || '').trim()}`,
254
+ fixable: false,
255
+ });
256
+ }
257
+ const consoleMatches = content.matchAll(/console\.(log|warn|error)\(/g);
258
+ for (const match of consoleMatches) {
259
+ const line = content.substring(0, match.index ?? 0).split('\n').length;
260
+ findings.push({
261
+ id: `CONSOLE-${crypto.randomBytes(4).toString('hex')}`,
262
+ category: 'quality',
263
+ severity: 'low',
264
+ file: relPath,
265
+ line,
266
+ message: `console.${match[1]} statement found`,
267
+ fixable: true,
268
+ });
269
+ }
270
+ }
271
+ }
272
+ catch (e) {
273
+ // Scan error
274
+ }
275
+ const score = Math.max(0, 100 - findings.length * 2);
276
+ return {
277
+ findings,
278
+ score,
279
+ verdict: score >= 70 ? 'pass' : 'fail',
280
+ duration: 0,
281
+ };
282
+ }
283
+ groupIntoFixPacks(findings, maxFixes) {
284
+ const byCategory = new Map();
285
+ for (const finding of findings) {
286
+ if (!finding.fixable)
287
+ continue;
288
+ const list = byCategory.get(finding.category) || [];
289
+ list.push(finding);
290
+ byCategory.set(finding.category, list);
291
+ }
292
+ const packs = [];
293
+ for (const [category, categoryFindings] of byCategory) {
294
+ const limited = maxFixes ? categoryFindings.slice(0, maxFixes) : categoryFindings;
295
+ if (limited.length === 0)
296
+ continue;
297
+ const impactedFiles = [...new Set(limited.map(f => f.file))];
298
+ const hasCritical = limited.some(f => f.severity === 'critical' || f.severity === 'high');
299
+ packs.push({
300
+ id: `pack-${category}-${crypto.randomBytes(4).toString('hex')}`,
301
+ category,
302
+ name: this.getCategoryName(category),
303
+ description: this.getCategoryDescription(category),
304
+ findings: limited,
305
+ estimatedRisk: hasCritical ? 'medium' : 'low',
306
+ impactedFiles,
307
+ priority: types_1.AUTOPILOT_FIX_PACK_PRIORITY[category] || 99,
308
+ });
309
+ }
310
+ return packs.sort((a, b) => a.priority - b.priority);
311
+ }
312
+ getCategoryName(category) {
313
+ const names = {
314
+ 'security': 'Security Fixes',
315
+ 'quality': 'Code Quality',
316
+ 'type-errors': 'TypeScript Errors',
317
+ 'build-blockers': 'Build Blockers',
318
+ 'test-failures': 'Test Failures',
319
+ 'placeholders': 'Placeholder Removal',
320
+ 'route-integrity': 'Route Integrity',
321
+ };
322
+ return names[category] || category;
323
+ }
324
+ getCategoryDescription(category) {
325
+ const descs = {
326
+ 'security': 'Fix security vulnerabilities and exposed secrets',
327
+ 'quality': 'Remove console.logs, TODOs, and improve code quality',
328
+ 'type-errors': 'Resolve TypeScript compilation errors',
329
+ 'build-blockers': 'Fix issues preventing successful builds',
330
+ 'test-failures': 'Fix failing test cases',
331
+ 'placeholders': 'Remove lorem ipsum and mock data',
332
+ 'route-integrity': 'Fix dead links and orphan routes',
333
+ };
334
+ return descs[category] || '';
335
+ }
336
+ async createTempWorkspace(projectPath, strategy) {
337
+ const id = crypto.randomBytes(8).toString('hex');
338
+ const workspacePath = path.join(this.tempDir, id);
339
+ await fs.promises.mkdir(workspacePath, { recursive: true });
340
+ if (strategy === 'worktree') {
341
+ try {
342
+ const gitDir = path.join(projectPath, '.git');
343
+ if (fs.existsSync(gitDir)) {
344
+ (0, child_process_1.execSync)(`git worktree add "${workspacePath}" HEAD --detach`, {
345
+ cwd: projectPath,
346
+ stdio: 'pipe',
347
+ });
348
+ return workspacePath;
349
+ }
350
+ }
351
+ catch {
352
+ // Fall through to copy
353
+ }
354
+ }
355
+ await this.copyProject(projectPath, workspacePath);
356
+ return workspacePath;
357
+ }
358
+ async copyProject(src, dest) {
359
+ const entries = await fs.promises.readdir(src, { withFileTypes: true });
360
+ for (const entry of entries) {
361
+ if (['node_modules', '.git', 'dist', 'build', '.next'].includes(entry.name))
362
+ continue;
363
+ const srcPath = path.join(src, entry.name);
364
+ const destPath = path.join(dest, entry.name);
365
+ if (entry.isDirectory()) {
366
+ await fs.promises.mkdir(destPath, { recursive: true });
367
+ await this.copyProject(srcPath, destPath);
368
+ }
369
+ else {
370
+ await fs.promises.copyFile(srcPath, destPath);
371
+ }
372
+ }
373
+ }
374
+ async applyFixPack(workspacePath, pack, _options) {
375
+ const fixes = [];
376
+ for (const finding of pack.findings) {
377
+ try {
378
+ if (finding.category === 'quality' && finding.message.includes('console.')) {
379
+ const filePath = path.join(workspacePath, finding.file);
380
+ if (fs.existsSync(filePath)) {
381
+ const content = await fs.promises.readFile(filePath, 'utf8');
382
+ const lines = content.split('\n');
383
+ const lineContent = lines[finding.line - 1];
384
+ if (lineContent?.includes('console.')) {
385
+ lines[finding.line - 1] = lineContent.replace(/console\.(log|warn|error)\([^)]*\);?/g, '// Removed by Guardrail Autopilot');
386
+ await fs.promises.writeFile(filePath, lines.join('\n'));
387
+ fixes.push({ packId: pack.id, findingId: finding.id, file: finding.file, success: true });
388
+ continue;
389
+ }
390
+ }
391
+ }
392
+ fixes.push({ packId: pack.id, findingId: finding.id, file: finding.file, success: false, error: 'Auto-fix not implemented for this issue type' });
393
+ }
394
+ catch (e) {
395
+ fixes.push({ packId: pack.id, findingId: finding.id, file: finding.file, success: false, error: e.message });
396
+ }
397
+ }
398
+ return fixes;
399
+ }
400
+ async runVerification(workspacePath, profile) {
401
+ const result = {
402
+ passed: true,
403
+ typecheck: { passed: true, errors: [] },
404
+ build: { passed: true, errors: [] },
405
+ tests: { passed: true, errors: [] },
406
+ duration: 0,
407
+ };
408
+ const startTime = Date.now();
409
+ try {
410
+ (0, child_process_1.execSync)('npx tsc --noEmit', { cwd: workspacePath, stdio: 'pipe', timeout: 120000 });
411
+ }
412
+ catch (e) {
413
+ result.typecheck.passed = false;
414
+ result.typecheck.errors.push(e.stderr?.toString() || 'TypeScript check failed');
415
+ result.passed = false;
416
+ }
417
+ if (profile === 'ship' || profile === 'full') {
418
+ try {
419
+ (0, child_process_1.execSync)('npm run build', { cwd: workspacePath, stdio: 'pipe', timeout: 300000 });
420
+ }
421
+ catch (e) {
422
+ result.build.passed = false;
423
+ result.build.errors.push(e.stderr?.toString() || 'Build failed');
424
+ result.passed = false;
425
+ }
426
+ }
427
+ result.duration = Date.now() - startTime;
428
+ return result;
429
+ }
430
+ async applyToProject(projectPath, workspacePath) {
431
+ const files = this.findSourceFiles(workspacePath);
432
+ for (const wsFile of files) {
433
+ const relPath = path.relative(workspacePath, wsFile);
434
+ const projFile = path.join(projectPath, relPath);
435
+ const projDir = path.dirname(projFile);
436
+ if (!fs.existsSync(projDir)) {
437
+ await fs.promises.mkdir(projDir, { recursive: true });
438
+ }
439
+ await fs.promises.copyFile(wsFile, projFile);
440
+ }
441
+ }
442
+ async cleanupWorkspace(workspacePath, projectPath) {
443
+ try {
444
+ (0, child_process_1.execSync)(`git worktree remove "${workspacePath}" --force`, { cwd: projectPath, stdio: 'pipe' });
445
+ }
446
+ catch {
447
+ try {
448
+ await fs.promises.rm(workspacePath, { recursive: true, force: true });
449
+ }
450
+ catch {
451
+ // Ignore cleanup errors
452
+ }
453
+ }
454
+ }
455
+ findSourceFiles(dir, files = []) {
456
+ try {
457
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
458
+ for (const entry of entries) {
459
+ if (['node_modules', '.git', 'dist', 'build'].includes(entry.name))
460
+ continue;
461
+ const fullPath = path.join(dir, entry.name);
462
+ if (entry.isDirectory()) {
463
+ this.findSourceFiles(fullPath, files);
464
+ }
465
+ else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) {
466
+ files.push(fullPath);
467
+ }
468
+ }
469
+ }
470
+ catch {
471
+ // Ignore
472
+ }
473
+ return files;
474
+ }
475
+ }
476
+ exports.AutopilotRunner = AutopilotRunner;
477
+ exports.autopilotRunner = new AutopilotRunner();
478
+ const runAutopilot = (options) => exports.autopilotRunner.run(options);
479
+ exports.runAutopilot = runAutopilot;