codeproof 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/run.js +48 -22
- package/core/safetyGuards.js +11 -4
- package/hooks/preCommit.js +2 -2
- package/package.json +1 -1
- package/utils/apiClient.js +78 -38
package/commands/run.js
CHANGED
|
@@ -118,7 +118,7 @@ export async function runCli({ args = [], cwd }) {
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
if (features.reporting) {
|
|
121
|
-
withFailOpenReporting(() => {
|
|
121
|
+
await withFailOpenReporting(async () => {
|
|
122
122
|
const timestamp = new Date().toISOString();
|
|
123
123
|
const reportId = randomUUID();
|
|
124
124
|
const projectId = config.projectId || "";
|
|
@@ -138,62 +138,88 @@ export async function runCli({ args = [], cwd }) {
|
|
|
138
138
|
aiReviewed,
|
|
139
139
|
timestamp
|
|
140
140
|
});
|
|
141
|
-
//
|
|
141
|
+
// Report is saved to file and sent to server regardless of findings
|
|
142
|
+
logInfo("Saving report to file...");
|
|
142
143
|
writeReport({ projectRoot: gitRoot, report });
|
|
144
|
+
logSuccess("Report saved locally.");
|
|
143
145
|
|
|
144
146
|
const integration = config?.integration || {};
|
|
145
147
|
const integrationEnabled = features.integration && Boolean(integration.enabled);
|
|
148
|
+
|
|
149
|
+
// Always send to server in pre-commit or manual mode
|
|
146
150
|
if (integrationEnabled) {
|
|
147
|
-
|
|
151
|
+
logInfo("Syncing report to server...");
|
|
152
|
+
await withFailOpenIntegration(async () => {
|
|
148
153
|
// Network calls are fail-open; never affect exit codes.
|
|
149
|
-
sendReportToServer(report, {
|
|
154
|
+
return await sendReportToServer(report, {
|
|
150
155
|
enabled: true,
|
|
151
156
|
endpointUrl: integration.endpointUrl
|
|
152
157
|
});
|
|
153
158
|
});
|
|
159
|
+
logSuccess("Report synced to server.");
|
|
154
160
|
} else {
|
|
155
161
|
reportFeatureDisabled("Integration", verbose, logInfo);
|
|
156
162
|
}
|
|
157
163
|
}, () => {
|
|
158
|
-
logWarn("Failed to
|
|
164
|
+
logWarn("Failed to process report. Continuing without blocking.");
|
|
159
165
|
});
|
|
160
166
|
} else {
|
|
161
167
|
reportFeatureDisabled("Reporting", verbose, logInfo);
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
if (blockFindings.length > 0) {
|
|
165
|
-
logError(
|
|
171
|
+
logError(`\n❌ CRITICAL ISSUES FOUND (${blockFindings.length}):\n`);
|
|
166
172
|
for (const finding of blockFindings) {
|
|
167
173
|
const relative = path.relative(gitRoot, finding.filePath) || finding.filePath;
|
|
168
|
-
logError(
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
|
|
174
|
+
logError(` • ${finding.ruleId.toUpperCase()}`);
|
|
175
|
+
logError(` File: ${relative}:${finding.line}`);
|
|
176
|
+
logError(` Issue: ${finding.message}`);
|
|
177
|
+
// console.log(` Code: ${finding.snippet}`);
|
|
178
|
+
logError("");
|
|
172
179
|
}
|
|
173
180
|
}
|
|
174
181
|
|
|
175
182
|
if (warnFindings.length > 0) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
logWarn(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
// Filter to show only HIGH risk warnings
|
|
184
|
+
const highRiskWarnings = warnFindings.filter(f => f.confidence === "high");
|
|
185
|
+
if (highRiskWarnings.length > 0) {
|
|
186
|
+
logWarn(`\n⚠️ HIGH RISK WARNINGS (${highRiskWarnings.length}):\n`);
|
|
187
|
+
for (const finding of highRiskWarnings) {
|
|
188
|
+
const relative = path.relative(gitRoot, finding.filePath) || finding.filePath;
|
|
189
|
+
logWarn(` • ${finding.ruleId.toUpperCase()}`);
|
|
190
|
+
logWarn(` File: ${relative}:${finding.line}`);
|
|
191
|
+
logWarn(` Issue: ${finding.message}`);
|
|
192
|
+
// console.log(` Code: ${finding.snippet}`);
|
|
193
|
+
logWarn("");
|
|
194
|
+
}
|
|
183
195
|
}
|
|
196
|
+
// comment out low risk warnings
|
|
197
|
+
// const lowRiskWarnings = warnFindings.filter(f => f.confidence !== "high");
|
|
198
|
+
// if (lowRiskWarnings.length > 0) {
|
|
199
|
+
// logWarn(`Baseline warnings (${lowRiskWarnings.length}):`);
|
|
200
|
+
// for (const finding of lowRiskWarnings) {
|
|
201
|
+
// const relative = path.relative(gitRoot, finding.filePath) || finding.filePath;
|
|
202
|
+
// logWarn(
|
|
203
|
+
// `${finding.ruleId} [${finding.severity}/${finding.confidence}] ${relative}:${finding.line} ${finding.message}`
|
|
204
|
+
// );
|
|
205
|
+
// logWarn(` ${finding.snippet}`);
|
|
206
|
+
// }
|
|
207
|
+
// }
|
|
184
208
|
}
|
|
185
209
|
|
|
186
210
|
if (aiReviewed.length > 0) {
|
|
187
|
-
logWarn(
|
|
211
|
+
logWarn(`\n🤖 AI-REVIEWED FINDINGS (${aiReviewed.length}):\n`);
|
|
188
212
|
for (const entry of aiReviewed) {
|
|
189
213
|
const { finding, decision } = entry;
|
|
190
214
|
const relative = path.relative(gitRoot, finding.filePath) || finding.filePath;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
);
|
|
215
|
+
const verdict = decision.verdict === "block" ? "BLOCKED" : "WARNING";
|
|
216
|
+
logWarn(` • ${finding.ruleId.toUpperCase()} [${verdict}]`);
|
|
217
|
+
logWarn(` File: ${relative}:${finding.line}`);
|
|
218
|
+
logWarn(` Analysis: ${decision.explanation}`);
|
|
194
219
|
if (decision.suggestedFix) {
|
|
195
|
-
logWarn(`
|
|
220
|
+
logWarn(` Fix: ${decision.suggestedFix}`);
|
|
196
221
|
}
|
|
222
|
+
logWarn("");
|
|
197
223
|
}
|
|
198
224
|
}
|
|
199
225
|
|
package/core/safetyGuards.js
CHANGED
|
@@ -18,9 +18,13 @@ export function reportFeatureDisabled(name, verbose, logInfo) {
|
|
|
18
18
|
logInfo(`${name} disabled by feature flag.`);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function withFailOpenReporting(action, onError) {
|
|
21
|
+
export async function withFailOpenReporting(action, onError) {
|
|
22
22
|
try {
|
|
23
|
-
|
|
23
|
+
const result = action();
|
|
24
|
+
if (result && typeof result.then === 'function') {
|
|
25
|
+
return await result;
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
24
28
|
} catch {
|
|
25
29
|
if (onError) {
|
|
26
30
|
onError();
|
|
@@ -29,9 +33,12 @@ export function withFailOpenReporting(action, onError) {
|
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
export function withFailOpenIntegration(action) {
|
|
36
|
+
export async function withFailOpenIntegration(action) {
|
|
33
37
|
try {
|
|
34
|
-
action();
|
|
38
|
+
const result = action();
|
|
39
|
+
if (result && typeof result.then === 'function') {
|
|
40
|
+
await result;
|
|
41
|
+
}
|
|
35
42
|
} catch {
|
|
36
43
|
// Integration failures are ignored to avoid affecting commits.
|
|
37
44
|
}
|
package/hooks/preCommit.js
CHANGED
|
@@ -25,7 +25,7 @@ function getHookBlock() {
|
|
|
25
25
|
" exit 0",
|
|
26
26
|
" fi",
|
|
27
27
|
"fi",
|
|
28
|
-
"CODEPROOF_PRECOMMIT=1 codeproof run --precommit",
|
|
28
|
+
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
29
29
|
"RESULT=$?",
|
|
30
30
|
"if [ $RESULT -ne 0 ]; then",
|
|
31
31
|
" echo \"CodeProof checks failed. Commit blocked.\"",
|
|
@@ -62,7 +62,7 @@ export function installPreCommitHook(gitRoot) {
|
|
|
62
62
|
" exit 0",
|
|
63
63
|
" fi",
|
|
64
64
|
"fi",
|
|
65
|
-
"CODEPROOF_PRECOMMIT=1 codeproof run --precommit",
|
|
65
|
+
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
66
66
|
"RESULT=$?",
|
|
67
67
|
"if [ $RESULT -ne 0 ]; then",
|
|
68
68
|
" echo \"CodeProof checks failed. Commit blocked.\"",
|
package/package.json
CHANGED
package/utils/apiClient.js
CHANGED
|
@@ -4,11 +4,17 @@ import https from "https";
|
|
|
4
4
|
// Boundary: integration layer only. Must not import CLI, rule engine, or reporting.
|
|
5
5
|
// Network calls are fail-open to avoid impacting commits or developer flow.
|
|
6
6
|
|
|
7
|
-
const DEFAULT_ENDPOINT = "
|
|
7
|
+
const DEFAULT_ENDPOINT = "http://127.0.0.1:4000/api/reports";
|
|
8
8
|
|
|
9
|
-
export function sendReportToServer(report, options = {}) {
|
|
9
|
+
export async function sendReportToServer(report, options = {}) {
|
|
10
10
|
const enabled = Boolean(options.enabled);
|
|
11
|
+
|
|
12
|
+
// console.log("[API Client] sendReportToServer called");
|
|
13
|
+
// console.log("[API Client] enabled:", enabled);
|
|
14
|
+
// console.log("[API Client] options:", JSON.stringify(options, null, 2));
|
|
15
|
+
|
|
11
16
|
if (!enabled) {
|
|
17
|
+
// console.log("[API Client] Integration disabled, skipping");
|
|
12
18
|
return;
|
|
13
19
|
}
|
|
14
20
|
|
|
@@ -16,41 +22,75 @@ export function sendReportToServer(report, options = {}) {
|
|
|
16
22
|
? options.endpointUrl.trim()
|
|
17
23
|
: DEFAULT_ENDPOINT;
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
// console.log("[API Client] Target endpoint:", endpointUrl);
|
|
26
|
+
// console.log("[API Client] Report summary:", {
|
|
27
|
+
// projectId: report.projectId,
|
|
28
|
+
// clientId: report.clientId,
|
|
29
|
+
// findingsCount: report.findings?.length || 0
|
|
30
|
+
// });
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(endpointUrl);
|
|
35
|
+
const payload = JSON.stringify(report);
|
|
36
|
+
const transport = url.protocol === "http:" ? http : https;
|
|
37
|
+
|
|
38
|
+
// console.log("[API Client] URL parsed - protocol:", url.protocol, "hostname:", url.hostname, "port:", url.port, "pathname:", url.pathname);
|
|
39
|
+
// console.log("[API Client] Payload size:", Buffer.byteLength(payload), "bytes");
|
|
40
|
+
// console.log("[API Client] Payload preview:", payload.substring(0, 200) + "...");
|
|
41
|
+
|
|
42
|
+
const portNumber = url.port ? parseInt(url.port, 10) : (url.protocol === "http:" ? 80 : 443);
|
|
43
|
+
|
|
44
|
+
// console.log("[API Client] Sending POST to:", `${url.protocol}//${url.hostname}:${portNumber}${url.pathname}`);
|
|
45
|
+
|
|
46
|
+
const request = transport.request(
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
hostname: url.hostname,
|
|
50
|
+
port: portNumber,
|
|
51
|
+
path: `${url.pathname}${url.search}`,
|
|
52
|
+
headers: {
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
"Content-Length": Buffer.byteLength(payload)
|
|
55
|
+
},
|
|
56
|
+
timeout: 5000
|
|
33
57
|
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
(res) => {
|
|
59
|
+
// console.log("[API Client] Response received:", res.statusCode);
|
|
60
|
+
let body = "";
|
|
61
|
+
res.on("data", (chunk) => {
|
|
62
|
+
body += chunk;
|
|
63
|
+
});
|
|
64
|
+
res.on("end", () => {
|
|
65
|
+
// console.log("[API Client] Response body:", body);
|
|
66
|
+
if (res.statusCode === 201) {
|
|
67
|
+
// Report sent successfully
|
|
68
|
+
} else {
|
|
69
|
+
console.error("[API Client] Server returned status:", res.statusCode);
|
|
70
|
+
}
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
res.resume();
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
request.on("timeout", () => {
|
|
78
|
+
console.error("[API Client] Request timeout");
|
|
79
|
+
request.destroy();
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
request.on("error", (err) => {
|
|
84
|
+
console.error("[API Client] Request error:", err.message);
|
|
85
|
+
resolve();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
request.write(payload);
|
|
89
|
+
request.end();
|
|
90
|
+
// console.log("[API Client] Request sent");
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error("[API Client] Exception:", err.message);
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
56
96
|
}
|