codeproof 1.0.4 → 1.1.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.
- package/bin/codeproof.js +62 -60
- package/commands/apply.js +32 -32
- package/commands/help.js +34 -0
- package/commands/ignore.js +32 -32
- package/commands/init.js +110 -106
- package/commands/moveSecret.js +254 -202
- package/commands/reportDashboard.js +65 -65
- package/commands/run.js +234 -234
- package/commands/whoami.js +19 -19
- package/core/boundaries.md +26 -26
- package/core/enforcement.js +51 -51
- package/core/featureFlags.js +25 -25
- package/core/identity.js +78 -78
- package/core/safetyGuards.js +58 -58
- package/engine/aiAnalyzer.js +143 -143
- package/engine/aiEscalation.js +6 -6
- package/engine/contextBuilder.js +65 -65
- package/engine/decisionMerger.js +30 -30
- package/engine/ruleEngine.js +52 -52
- package/hooks/preCommit.js +93 -93
- package/package.json +16 -16
- package/reporting/reportBuilder.js +112 -112
- package/reporting/reportReader.js +49 -49
- package/reporting/reportWriter.js +104 -91
- package/rules/dangerousUsageRule.js +11 -11
- package/rules/insecureConfigRule.js +11 -11
- package/rules/regexPatterns.js +53 -53
- package/rules/ruleUtils.js +58 -58
- package/rules/secretRule.js +21 -21
- package/ui/banner.js +36 -0
- package/ui/formatting.js +76 -0
- package/ui/welcomeScreen.js +42 -42
- package/utils/apiClient.js +96 -96
- package/utils/envManager.js +48 -48
- package/utils/fileRewriter.js +145 -145
- package/utils/fileScanner.js +40 -40
- package/utils/files.js +50 -50
- package/utils/git.js +63 -63
- package/utils/gitIgnore.js +55 -55
- package/utils/logger.js +25 -25
- package/utils/projectType.js +20 -20
- package/.env +0 -1
package/hooks/preCommit.js
CHANGED
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { logInfo, logWarn } from "../utils/logger.js";
|
|
4
|
-
|
|
5
|
-
const HOOK_MARKER = "# CodeProof pre-commit hook";
|
|
6
|
-
|
|
7
|
-
function getHookPath(gitRoot) {
|
|
8
|
-
return path.join(gitRoot, ".git", "hooks", "pre-commit");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function getHookBlock() {
|
|
12
|
-
return [
|
|
13
|
-
"",
|
|
14
|
-
HOOK_MARKER,
|
|
15
|
-
"GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)",
|
|
16
|
-
"if [ -n \"$GIT_ROOT\" ]; then",
|
|
17
|
-
" cd \"$GIT_ROOT\" || exit 1",
|
|
18
|
-
"fi",
|
|
19
|
-
"CONFIG_PATH=\"$GIT_ROOT/codeproof.config.json\"",
|
|
20
|
-
"if [ -f \"$CONFIG_PATH\" ]; then",
|
|
21
|
-
" ENFORCEMENT=$(node -e \"const fs=require('fs');const path=process.argv[1];try{const c=JSON.parse(fs.readFileSync(path,'utf8'));console.log((c.enforcement||'enabled').toLowerCase());}catch(e){console.log('enabled');}\" \"$CONFIG_PATH\")",
|
|
22
|
-
" if [ \"$ENFORCEMENT\" = \"disabled\" ]; then",
|
|
23
|
-
" echo \"CodeProof enforcement is temporarily disabled.\"",
|
|
24
|
-
" echo \"Commit allowed.\"",
|
|
25
|
-
" exit 0",
|
|
26
|
-
" fi",
|
|
27
|
-
"fi",
|
|
28
|
-
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
29
|
-
"RESULT=$?",
|
|
30
|
-
"if [ $RESULT -ne 0 ]; then",
|
|
31
|
-
" echo \"CodeProof checks failed. Commit blocked.\"",
|
|
32
|
-
" exit $RESULT",
|
|
33
|
-
"fi",
|
|
34
|
-
""
|
|
35
|
-
].join("\n");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function installPreCommitHook(gitRoot) {
|
|
39
|
-
const hookPath = getHookPath(gitRoot);
|
|
40
|
-
const hookDir = path.dirname(hookPath);
|
|
41
|
-
|
|
42
|
-
if (!fs.existsSync(hookDir)) {
|
|
43
|
-
fs.mkdirSync(hookDir, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!fs.existsSync(hookPath)) {
|
|
47
|
-
// Use a POSIX shell hook for cross-platform Git compatibility (Git Bash on Windows).
|
|
48
|
-
const content = [
|
|
49
|
-
"#!/bin/sh",
|
|
50
|
-
"",
|
|
51
|
-
"# Auto-generated by CodeProof",
|
|
52
|
-
"GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)",
|
|
53
|
-
"if [ -n \"$GIT_ROOT\" ]; then",
|
|
54
|
-
" cd \"$GIT_ROOT\" || exit 1",
|
|
55
|
-
"fi",
|
|
56
|
-
"CONFIG_PATH=\"$GIT_ROOT/codeproof.config.json\"",
|
|
57
|
-
"if [ -f \"$CONFIG_PATH\" ]; then",
|
|
58
|
-
" ENFORCEMENT=$(node -e \"const fs=require('fs');const path=process.argv[1];try{const c=JSON.parse(fs.readFileSync(path,'utf8'));console.log((c.enforcement||'enabled').toLowerCase());}catch(e){console.log('enabled');}\" \"$CONFIG_PATH\")",
|
|
59
|
-
" if [ \"$ENFORCEMENT\" = \"disabled\" ]; then",
|
|
60
|
-
" echo \"CodeProof enforcement is temporarily disabled.\"",
|
|
61
|
-
" echo \"Commit allowed.\"",
|
|
62
|
-
" exit 0",
|
|
63
|
-
" fi",
|
|
64
|
-
"fi",
|
|
65
|
-
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
66
|
-
"RESULT=$?",
|
|
67
|
-
"if [ $RESULT -ne 0 ]; then",
|
|
68
|
-
" echo \"CodeProof checks failed. Commit blocked.\"",
|
|
69
|
-
" exit $RESULT",
|
|
70
|
-
"fi",
|
|
71
|
-
""
|
|
72
|
-
].join("\n");
|
|
73
|
-
fs.writeFileSync(hookPath, content, "utf8");
|
|
74
|
-
logInfo("Created new pre-commit hook.");
|
|
75
|
-
} else {
|
|
76
|
-
const existing = fs.readFileSync(hookPath, "utf8");
|
|
77
|
-
if (existing.includes("codeproof run") || existing.includes(HOOK_MARKER)) {
|
|
78
|
-
logWarn("Pre-commit hook already references CodeProof. Skipping append.");
|
|
79
|
-
} else {
|
|
80
|
-
// Append instead of overwrite to avoid breaking existing hook logic.
|
|
81
|
-
fs.appendFileSync(hookPath, getHookBlock(), "utf8");
|
|
82
|
-
logInfo("Appended CodeProof to existing pre-commit hook.");
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
fs.chmodSync(hookPath, 0o755);
|
|
88
|
-
} catch {
|
|
89
|
-
// Best-effort: Windows may not honor chmod, but Git still runs hooks.
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { logInfo, logWarn } from "../utils/logger.js";
|
|
4
|
+
|
|
5
|
+
const HOOK_MARKER = "# CodeProof pre-commit hook";
|
|
6
|
+
|
|
7
|
+
function getHookPath(gitRoot) {
|
|
8
|
+
return path.join(gitRoot, ".git", "hooks", "pre-commit");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getHookBlock() {
|
|
12
|
+
return [
|
|
13
|
+
"",
|
|
14
|
+
HOOK_MARKER,
|
|
15
|
+
"GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)",
|
|
16
|
+
"if [ -n \"$GIT_ROOT\" ]; then",
|
|
17
|
+
" cd \"$GIT_ROOT\" || exit 1",
|
|
18
|
+
"fi",
|
|
19
|
+
"CONFIG_PATH=\"$GIT_ROOT/codeproof.config.json\"",
|
|
20
|
+
"if [ -f \"$CONFIG_PATH\" ]; then",
|
|
21
|
+
" ENFORCEMENT=$(node -e \"const fs=require('fs');const path=process.argv[1];try{const c=JSON.parse(fs.readFileSync(path,'utf8'));console.log((c.enforcement||'enabled').toLowerCase());}catch(e){console.log('enabled');}\" \"$CONFIG_PATH\")",
|
|
22
|
+
" if [ \"$ENFORCEMENT\" = \"disabled\" ]; then",
|
|
23
|
+
" echo \"CodeProof enforcement is temporarily disabled.\"",
|
|
24
|
+
" echo \"Commit allowed.\"",
|
|
25
|
+
" exit 0",
|
|
26
|
+
" fi",
|
|
27
|
+
"fi",
|
|
28
|
+
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
29
|
+
"RESULT=$?",
|
|
30
|
+
"if [ $RESULT -ne 0 ]; then",
|
|
31
|
+
" echo \"CodeProof checks failed. Commit blocked.\"",
|
|
32
|
+
" exit $RESULT",
|
|
33
|
+
"fi",
|
|
34
|
+
""
|
|
35
|
+
].join("\n");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function installPreCommitHook(gitRoot) {
|
|
39
|
+
const hookPath = getHookPath(gitRoot);
|
|
40
|
+
const hookDir = path.dirname(hookPath);
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(hookDir)) {
|
|
43
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(hookPath)) {
|
|
47
|
+
// Use a POSIX shell hook for cross-platform Git compatibility (Git Bash on Windows).
|
|
48
|
+
const content = [
|
|
49
|
+
"#!/bin/sh",
|
|
50
|
+
"",
|
|
51
|
+
"# Auto-generated by CodeProof",
|
|
52
|
+
"GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)",
|
|
53
|
+
"if [ -n \"$GIT_ROOT\" ]; then",
|
|
54
|
+
" cd \"$GIT_ROOT\" || exit 1",
|
|
55
|
+
"fi",
|
|
56
|
+
"CONFIG_PATH=\"$GIT_ROOT/codeproof.config.json\"",
|
|
57
|
+
"if [ -f \"$CONFIG_PATH\" ]; then",
|
|
58
|
+
" ENFORCEMENT=$(node -e \"const fs=require('fs');const path=process.argv[1];try{const c=JSON.parse(fs.readFileSync(path,'utf8'));console.log((c.enforcement||'enabled').toLowerCase());}catch(e){console.log('enabled');}\" \"$CONFIG_PATH\")",
|
|
59
|
+
" if [ \"$ENFORCEMENT\" = \"disabled\" ]; then",
|
|
60
|
+
" echo \"CodeProof enforcement is temporarily disabled.\"",
|
|
61
|
+
" echo \"Commit allowed.\"",
|
|
62
|
+
" exit 0",
|
|
63
|
+
" fi",
|
|
64
|
+
"fi",
|
|
65
|
+
"CODEPROOF_PRECOMMIT=1 npx codeproof run --precommit",
|
|
66
|
+
"RESULT=$?",
|
|
67
|
+
"if [ $RESULT -ne 0 ]; then",
|
|
68
|
+
" echo \"CodeProof checks failed. Commit blocked.\"",
|
|
69
|
+
" exit $RESULT",
|
|
70
|
+
"fi",
|
|
71
|
+
""
|
|
72
|
+
].join("\n");
|
|
73
|
+
fs.writeFileSync(hookPath, content, "utf8");
|
|
74
|
+
logInfo("Created new pre-commit hook.");
|
|
75
|
+
} else {
|
|
76
|
+
const existing = fs.readFileSync(hookPath, "utf8");
|
|
77
|
+
if (existing.includes("codeproof run") || existing.includes(HOOK_MARKER)) {
|
|
78
|
+
logWarn("Pre-commit hook already references CodeProof. Skipping append.");
|
|
79
|
+
} else {
|
|
80
|
+
// Append instead of overwrite to avoid breaking existing hook logic.
|
|
81
|
+
fs.appendFileSync(hookPath, getHookBlock(), "utf8");
|
|
82
|
+
logInfo("Appended CodeProof to existing pre-commit hook.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
fs.chmodSync(hookPath, 0o755);
|
|
88
|
+
} catch {
|
|
89
|
+
// Best-effort: Windows may not honor chmod, but Git still runs hooks.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "codeproof",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "CodeProof CLI",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"codeproof": "./bin/codeproof.js"
|
|
8
|
-
},
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=18"
|
|
11
|
-
},
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"uuid": "^13.0.0"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "codeproof",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "CodeProof CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"codeproof": "./bin/codeproof.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"uuid": "^13.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
|
|
3
|
-
// Boundary: reporting only. Must not import rule logic or AI logic.
|
|
4
|
-
// Reports are built from provided inputs to keep dependencies one-directional.
|
|
5
|
-
|
|
6
|
-
function toRelativePath(projectRoot, filePath) {
|
|
7
|
-
if (!filePath) {
|
|
8
|
-
return "";
|
|
9
|
-
}
|
|
10
|
-
const relative = path.relative(projectRoot, filePath);
|
|
11
|
-
return relative || filePath;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function trimSnippet(snippet, maxLength = 200) {
|
|
15
|
-
if (!snippet) {
|
|
16
|
-
return "";
|
|
17
|
-
}
|
|
18
|
-
const trimmed = String(snippet).trim();
|
|
19
|
-
if (trimmed.length <= maxLength) {
|
|
20
|
-
return trimmed;
|
|
21
|
-
}
|
|
22
|
-
return trimmed.slice(0, maxLength) + "...";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function redactSecretSnippet(ruleId, snippet) {
|
|
26
|
-
if (!snippet) {
|
|
27
|
-
return "";
|
|
28
|
-
}
|
|
29
|
-
if (String(ruleId || "").startsWith("secret.")) {
|
|
30
|
-
return "***";
|
|
31
|
-
}
|
|
32
|
-
return snippet;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function mapDecisionConfidence(value) {
|
|
36
|
-
if (typeof value !== "number") {
|
|
37
|
-
return "low";
|
|
38
|
-
}
|
|
39
|
-
return value >= 0.75 ? "high" : "low";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function normalizeSeverity(value) {
|
|
43
|
-
return value === "block" ? "block" : "warn";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function buildReport({
|
|
47
|
-
projectRoot,
|
|
48
|
-
projectId,
|
|
49
|
-
projectName,
|
|
50
|
-
repoIdentifier,
|
|
51
|
-
clientId,
|
|
52
|
-
reportId,
|
|
53
|
-
scanMode,
|
|
54
|
-
filesScannedCount,
|
|
55
|
-
baselineFindings,
|
|
56
|
-
aiReviewed,
|
|
57
|
-
timestamp
|
|
58
|
-
}) {
|
|
59
|
-
const aiById = new Map(aiReviewed.map((entry) => [entry.finding.findingId, entry.decision]));
|
|
60
|
-
|
|
61
|
-
const findings = baselineFindings.map((finding) => {
|
|
62
|
-
const decision = aiById.get(finding.findingId);
|
|
63
|
-
const severity = normalizeSeverity(decision ? decision.verdict : finding.severity);
|
|
64
|
-
const confidence = decision
|
|
65
|
-
? mapDecisionConfidence(decision.confidence)
|
|
66
|
-
: finding.confidence === "high"
|
|
67
|
-
? "high"
|
|
68
|
-
: "low";
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
ruleId: finding.ruleId,
|
|
72
|
-
severity,
|
|
73
|
-
confidence,
|
|
74
|
-
filePath: toRelativePath(projectRoot, finding.filePath),
|
|
75
|
-
lineNumber: finding.line || null,
|
|
76
|
-
codeSnippet: trimSnippet(redactSecretSnippet(finding.ruleId, finding.snippet)),
|
|
77
|
-
explanation: decision?.explanation || finding.message || ""
|
|
78
|
-
};
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const blocksCount = findings.filter((finding) => finding.severity === "block").length;
|
|
82
|
-
const warningsCount = findings.filter((finding) => finding.severity === "warn").length;
|
|
83
|
-
|
|
84
|
-
const finalVerdict = blocksCount > 0
|
|
85
|
-
? "blocked"
|
|
86
|
-
: warningsCount > 0
|
|
87
|
-
? "allowed_with_warnings"
|
|
88
|
-
: "allowed";
|
|
89
|
-
|
|
90
|
-
// Server-compatible format
|
|
91
|
-
return {
|
|
92
|
-
projectId,
|
|
93
|
-
clientId,
|
|
94
|
-
project: {
|
|
95
|
-
name: projectName || "Unknown Project",
|
|
96
|
-
repoIdentifier: repoIdentifier || projectRoot
|
|
97
|
-
},
|
|
98
|
-
report: {
|
|
99
|
-
timestamp,
|
|
100
|
-
scanMode,
|
|
101
|
-
summary: {
|
|
102
|
-
filesScanned: filesScannedCount,
|
|
103
|
-
findings: findings.length,
|
|
104
|
-
blocks: blocksCount,
|
|
105
|
-
warnings: warningsCount,
|
|
106
|
-
finalVerdict
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
findings,
|
|
110
|
-
finalVerdict
|
|
111
|
-
};
|
|
112
|
-
}
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
// Boundary: reporting only. Must not import rule logic or AI logic.
|
|
4
|
+
// Reports are built from provided inputs to keep dependencies one-directional.
|
|
5
|
+
|
|
6
|
+
function toRelativePath(projectRoot, filePath) {
|
|
7
|
+
if (!filePath) {
|
|
8
|
+
return "";
|
|
9
|
+
}
|
|
10
|
+
const relative = path.relative(projectRoot, filePath);
|
|
11
|
+
return relative || filePath;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function trimSnippet(snippet, maxLength = 200) {
|
|
15
|
+
if (!snippet) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
const trimmed = String(snippet).trim();
|
|
19
|
+
if (trimmed.length <= maxLength) {
|
|
20
|
+
return trimmed;
|
|
21
|
+
}
|
|
22
|
+
return trimmed.slice(0, maxLength) + "...";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function redactSecretSnippet(ruleId, snippet) {
|
|
26
|
+
if (!snippet) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
if (String(ruleId || "").startsWith("secret.")) {
|
|
30
|
+
return "***";
|
|
31
|
+
}
|
|
32
|
+
return snippet;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function mapDecisionConfidence(value) {
|
|
36
|
+
if (typeof value !== "number") {
|
|
37
|
+
return "low";
|
|
38
|
+
}
|
|
39
|
+
return value >= 0.75 ? "high" : "low";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeSeverity(value) {
|
|
43
|
+
return value === "block" ? "block" : "warn";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildReport({
|
|
47
|
+
projectRoot,
|
|
48
|
+
projectId,
|
|
49
|
+
projectName,
|
|
50
|
+
repoIdentifier,
|
|
51
|
+
clientId,
|
|
52
|
+
reportId,
|
|
53
|
+
scanMode,
|
|
54
|
+
filesScannedCount,
|
|
55
|
+
baselineFindings,
|
|
56
|
+
aiReviewed,
|
|
57
|
+
timestamp
|
|
58
|
+
}) {
|
|
59
|
+
const aiById = new Map(aiReviewed.map((entry) => [entry.finding.findingId, entry.decision]));
|
|
60
|
+
|
|
61
|
+
const findings = baselineFindings.map((finding) => {
|
|
62
|
+
const decision = aiById.get(finding.findingId);
|
|
63
|
+
const severity = normalizeSeverity(decision ? decision.verdict : finding.severity);
|
|
64
|
+
const confidence = decision
|
|
65
|
+
? mapDecisionConfidence(decision.confidence)
|
|
66
|
+
: finding.confidence === "high"
|
|
67
|
+
? "high"
|
|
68
|
+
: "low";
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
ruleId: finding.ruleId,
|
|
72
|
+
severity,
|
|
73
|
+
confidence,
|
|
74
|
+
filePath: toRelativePath(projectRoot, finding.filePath),
|
|
75
|
+
lineNumber: finding.line || null,
|
|
76
|
+
codeSnippet: trimSnippet(redactSecretSnippet(finding.ruleId, finding.snippet)),
|
|
77
|
+
explanation: decision?.explanation || finding.message || ""
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const blocksCount = findings.filter((finding) => finding.severity === "block").length;
|
|
82
|
+
const warningsCount = findings.filter((finding) => finding.severity === "warn").length;
|
|
83
|
+
|
|
84
|
+
const finalVerdict = blocksCount > 0
|
|
85
|
+
? "blocked"
|
|
86
|
+
: warningsCount > 0
|
|
87
|
+
? "allowed_with_warnings"
|
|
88
|
+
: "allowed";
|
|
89
|
+
|
|
90
|
+
// Server-compatible format
|
|
91
|
+
return {
|
|
92
|
+
projectId,
|
|
93
|
+
clientId,
|
|
94
|
+
project: {
|
|
95
|
+
name: projectName || "Unknown Project",
|
|
96
|
+
repoIdentifier: repoIdentifier || projectRoot
|
|
97
|
+
},
|
|
98
|
+
report: {
|
|
99
|
+
timestamp,
|
|
100
|
+
scanMode,
|
|
101
|
+
summary: {
|
|
102
|
+
filesScanned: filesScannedCount,
|
|
103
|
+
findings: findings.length,
|
|
104
|
+
blocks: blocksCount,
|
|
105
|
+
warnings: warningsCount,
|
|
106
|
+
finalVerdict
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
findings,
|
|
110
|
+
finalVerdict
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
const REPORT_DIR_NAME = "codeproof-reports";
|
|
5
|
-
const REPORT_PATTERN = /^report-(\d+)\.json$/;
|
|
6
|
-
|
|
7
|
-
function getReportDir(projectRoot) {
|
|
8
|
-
return path.join(projectRoot, REPORT_DIR_NAME);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function getReportNumbers(files) {
|
|
12
|
-
return files
|
|
13
|
-
.map((file) => {
|
|
14
|
-
const match = REPORT_PATTERN.exec(file);
|
|
15
|
-
return match ? Number.parseInt(match[1], 10) : null;
|
|
16
|
-
})
|
|
17
|
-
.filter((value) => Number.isInteger(value));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function readLatestReport(projectRoot) {
|
|
21
|
-
const reportDir = getReportDir(projectRoot);
|
|
22
|
-
if (!fs.existsSync(reportDir)) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let files = [];
|
|
27
|
-
try {
|
|
28
|
-
files = fs.readdirSync(reportDir);
|
|
29
|
-
} catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const numbers = getReportNumbers(files).sort((a, b) => b - a);
|
|
34
|
-
if (numbers.length === 0) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
for (const number of numbers) {
|
|
39
|
-
const reportPath = path.join(reportDir, `report-${number}.json`);
|
|
40
|
-
try {
|
|
41
|
-
const raw = fs.readFileSync(reportPath, "utf8");
|
|
42
|
-
return { report: JSON.parse(raw), reportPath };
|
|
43
|
-
} catch {
|
|
44
|
-
// Skip unreadable or invalid JSON reports instead of crashing.
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
const REPORT_DIR_NAME = "codeproof-reports";
|
|
5
|
+
const REPORT_PATTERN = /^report-(\d+)\.json$/;
|
|
6
|
+
|
|
7
|
+
function getReportDir(projectRoot) {
|
|
8
|
+
return path.join(projectRoot, REPORT_DIR_NAME);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getReportNumbers(files) {
|
|
12
|
+
return files
|
|
13
|
+
.map((file) => {
|
|
14
|
+
const match = REPORT_PATTERN.exec(file);
|
|
15
|
+
return match ? Number.parseInt(match[1], 10) : null;
|
|
16
|
+
})
|
|
17
|
+
.filter((value) => Number.isInteger(value));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function readLatestReport(projectRoot) {
|
|
21
|
+
const reportDir = getReportDir(projectRoot);
|
|
22
|
+
if (!fs.existsSync(reportDir)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let files = [];
|
|
27
|
+
try {
|
|
28
|
+
files = fs.readdirSync(reportDir);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const numbers = getReportNumbers(files).sort((a, b) => b - a);
|
|
34
|
+
if (numbers.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const number of numbers) {
|
|
39
|
+
const reportPath = path.join(reportDir, `report-${number}.json`);
|
|
40
|
+
try {
|
|
41
|
+
const raw = fs.readFileSync(reportPath, "utf8");
|
|
42
|
+
return { report: JSON.parse(raw), reportPath };
|
|
43
|
+
} catch {
|
|
44
|
+
// Skip unreadable or invalid JSON reports instead of crashing.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return null;
|
|
49
|
+
}
|