codeprobe-scanner 1.0.13 ā 1.0.15
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/package.json +1 -1
- package/src/cli/commands/scan.ts +2 -2
- package/src/engine/index.ts +62 -1
- package/src/integrations/videodb.ts +34 -69
- package/src/shared/file-scanner.ts +50 -0
package/package.json
CHANGED
package/src/cli/commands/scan.ts
CHANGED
|
@@ -110,8 +110,8 @@ export async function scanCommand(args: string[]): Promise<void> {
|
|
|
110
110
|
try {
|
|
111
111
|
const engine = createEngine();
|
|
112
112
|
|
|
113
|
-
// Run
|
|
114
|
-
const report = await engine.
|
|
113
|
+
// Run recursive scan to find all package.json files
|
|
114
|
+
const report = await engine.scanRecursive(repoPath);
|
|
115
115
|
|
|
116
116
|
const duration = Date.now() - startTime;
|
|
117
117
|
|
package/src/engine/index.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { createSandbox } from "./sandbox";
|
|
|
4
4
|
import { createMatcher } from "./matcher";
|
|
5
5
|
import { createPatcher } from "./patcher";
|
|
6
6
|
import { createReportBuilder } from "./report";
|
|
7
|
-
import { Report } from "../shared/types";
|
|
7
|
+
import { Report, ScanCVE } from "../shared/types";
|
|
8
|
+
import { findAllPackageJsons } from "../shared/file-scanner";
|
|
8
9
|
|
|
9
10
|
export class CodeProbeEngine {
|
|
10
11
|
private parser = createParser();
|
|
@@ -18,6 +19,66 @@ export class CodeProbeEngine {
|
|
|
18
19
|
return this.sandbox.getVideoRecorder();
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
async scanRecursive(rootPath: string): Promise<Report> {
|
|
23
|
+
const startTime = Date.now();
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Find all package.json files
|
|
27
|
+
console.log("š Searching for package.json files...");
|
|
28
|
+
const packageJsonLocations = await findAllPackageJsons(rootPath);
|
|
29
|
+
console.log(` Found ${packageJsonLocations.length} package.json file(s)`);
|
|
30
|
+
|
|
31
|
+
if (packageJsonLocations.length === 0) {
|
|
32
|
+
throw new Error("No package.json files found in the repository");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Scan each location
|
|
36
|
+
const allCves: ScanCVE[] = [];
|
|
37
|
+
const allDependencies: number[] = [];
|
|
38
|
+
const scannedLocations: string[] = [];
|
|
39
|
+
|
|
40
|
+
for (const location of packageJsonLocations) {
|
|
41
|
+
console.log(`\nš Scanning: ${location.path}`);
|
|
42
|
+
try {
|
|
43
|
+
const report = await this.scan(location.path);
|
|
44
|
+
allCves.push(...report.scan.cves);
|
|
45
|
+
allDependencies.push(report.scan.total_dependencies);
|
|
46
|
+
scannedLocations.push(location.path);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn(
|
|
49
|
+
` ā ļø Skipped: ${error instanceof Error ? error.message : String(error)}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Aggregate results
|
|
55
|
+
const riskScore = this.matcher.calculateRiskScore(allCves);
|
|
56
|
+
const scanDuration = Date.now() - startTime;
|
|
57
|
+
const totalDependencies = allDependencies.reduce((a, b) => a + b, 0);
|
|
58
|
+
|
|
59
|
+
const report = await this.reportBuilder.buildReport(
|
|
60
|
+
rootPath,
|
|
61
|
+
allCves,
|
|
62
|
+
riskScore,
|
|
63
|
+
scanDuration,
|
|
64
|
+
totalDependencies
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Add metadata about scanned locations
|
|
68
|
+
(report as any).scanned_locations = scannedLocations;
|
|
69
|
+
|
|
70
|
+
await this.reportBuilder.saveReport(report);
|
|
71
|
+
|
|
72
|
+
return report;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Recursive scan failed: ${
|
|
76
|
+
error instanceof Error ? error.message : String(error)
|
|
77
|
+
}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
21
82
|
async scan(repoPath: string): Promise<Report> {
|
|
22
83
|
const startTime = Date.now();
|
|
23
84
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
1
4
|
interface ExploitVideoRecord {
|
|
2
5
|
cveId: string;
|
|
3
6
|
package: string;
|
|
@@ -9,29 +12,18 @@ interface ExploitVideoRecord {
|
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
export class VideoDBRecorder {
|
|
12
|
-
private videoDb: any = null;
|
|
13
|
-
private apiKey: string;
|
|
14
15
|
private recordedVideos: Map<string, ExploitVideoRecord> = new Map();
|
|
16
|
+
private proofsDir = ".proofs";
|
|
15
17
|
|
|
16
18
|
constructor() {
|
|
17
|
-
this.
|
|
18
|
-
this.initializeVideoDB();
|
|
19
|
+
this.ensureProofsDir();
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
private
|
|
22
|
-
if (!this.apiKey) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
22
|
+
private async ensureProofsDir(): Promise<void> {
|
|
26
23
|
try {
|
|
27
|
-
|
|
28
|
-
const Constructor = videodb.VideoDb || videodb.Connection || videodb.connect;
|
|
29
|
-
if (typeof Constructor === "function") {
|
|
30
|
-
this.videoDb = new Constructor({ apiKey: this.apiKey });
|
|
31
|
-
console.log("[VideoDB] ā Initialized - exploit recordings enabled");
|
|
32
|
-
}
|
|
24
|
+
await mkdir(this.proofsDir, { recursive: true });
|
|
33
25
|
} catch {
|
|
34
|
-
//
|
|
26
|
+
// Directory might already exist
|
|
35
27
|
}
|
|
36
28
|
}
|
|
37
29
|
|
|
@@ -42,88 +34,61 @@ export class VideoDBRecorder {
|
|
|
42
34
|
exploitOutput: string,
|
|
43
35
|
duration: number = 15
|
|
44
36
|
): Promise<ExploitVideoRecord | null> {
|
|
45
|
-
if (!this.videoDb) {
|
|
46
|
-
console.warn(`[VideoDB] Skipping recording for ${cveId} - not initialized`);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
37
|
try {
|
|
51
|
-
console.log(`[
|
|
52
|
-
|
|
53
|
-
// Create collection for this CVE
|
|
54
|
-
const collectionName = `codeprobe-${cveId.toLowerCase().replace("-", "_")}`;
|
|
55
|
-
|
|
56
|
-
// Create metadata for the video
|
|
57
|
-
const metadata = {
|
|
58
|
-
cve_id: cveId,
|
|
59
|
-
package: packageName,
|
|
60
|
-
version: version,
|
|
61
|
-
exploit_output: exploitOutput,
|
|
62
|
-
timestamp: new Date().toISOString(),
|
|
63
|
-
severity: "CRITICAL",
|
|
64
|
-
type: "rce-verification",
|
|
65
|
-
};
|
|
38
|
+
console.log(`[ProofRecorder] š„ Recording proof for ${cveId}...`);
|
|
66
39
|
|
|
67
|
-
|
|
68
|
-
// For now, we create a metadata entry with exploitOutput as the video description
|
|
69
|
-
const videoUrl = await this.uploadExploitRecord(
|
|
40
|
+
const proofPath = await this.saveProof(
|
|
70
41
|
cveId,
|
|
71
42
|
packageName,
|
|
72
43
|
version,
|
|
73
|
-
exploitOutput
|
|
74
|
-
collectionName,
|
|
75
|
-
metadata
|
|
44
|
+
exploitOutput
|
|
76
45
|
);
|
|
77
46
|
|
|
78
47
|
const record: ExploitVideoRecord = {
|
|
79
48
|
cveId,
|
|
80
49
|
package: packageName,
|
|
81
50
|
version,
|
|
82
|
-
videoUrl,
|
|
51
|
+
videoUrl: proofPath,
|
|
83
52
|
duration,
|
|
84
53
|
timestamp: new Date().toISOString(),
|
|
85
54
|
};
|
|
86
55
|
|
|
87
56
|
this.recordedVideos.set(cveId, record);
|
|
88
|
-
console.log(`[
|
|
57
|
+
console.log(`[ProofRecorder] ā Saved: ${proofPath}`);
|
|
89
58
|
|
|
90
59
|
return record;
|
|
91
60
|
} catch (error) {
|
|
92
61
|
console.warn(
|
|
93
|
-
`[
|
|
62
|
+
`[ProofRecorder] Failed to save proof for ${cveId}: ${error instanceof Error ? error.message : String(error)}`
|
|
94
63
|
);
|
|
95
64
|
return null;
|
|
96
65
|
}
|
|
97
66
|
}
|
|
98
67
|
|
|
99
|
-
private async
|
|
68
|
+
private async saveProof(
|
|
100
69
|
cveId: string,
|
|
101
70
|
packageName: string,
|
|
102
71
|
version: string,
|
|
103
|
-
exploitOutput: string
|
|
104
|
-
collectionName: string,
|
|
105
|
-
metadata: any
|
|
72
|
+
exploitOutput: string
|
|
106
73
|
): Promise<string> {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return videoUrl;
|
|
74
|
+
await this.ensureProofsDir();
|
|
75
|
+
|
|
76
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
77
|
+
const filename = `${cveId}_${timestamp}.json`;
|
|
78
|
+
const filePath = join(this.proofsDir, filename);
|
|
79
|
+
|
|
80
|
+
const proofData = {
|
|
81
|
+
cveId,
|
|
82
|
+
package: packageName,
|
|
83
|
+
version,
|
|
84
|
+
exploitOutput,
|
|
85
|
+
savedAt: new Date().toISOString(),
|
|
86
|
+
severity: "CRITICAL",
|
|
87
|
+
type: "rce-verification",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
await writeFile(filePath, JSON.stringify(proofData, null, 2));
|
|
91
|
+
return filePath;
|
|
127
92
|
}
|
|
128
93
|
|
|
129
94
|
getRecordedVideos(): ExploitVideoRecord[] {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readdir } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
|
|
4
|
+
export interface PackageJsonLocation {
|
|
5
|
+
path: string;
|
|
6
|
+
depth: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function findAllPackageJsons(
|
|
10
|
+
rootPath: string,
|
|
11
|
+
maxDepth: number = 3,
|
|
12
|
+
currentDepth: number = 0,
|
|
13
|
+
results: PackageJsonLocation[] = []
|
|
14
|
+
): Promise<PackageJsonLocation[]> {
|
|
15
|
+
if (currentDepth > maxDepth) {
|
|
16
|
+
return results;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const entries = await readdir(rootPath, { withFileTypes: true });
|
|
21
|
+
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
// Skip node_modules, .git, and other common folders
|
|
24
|
+
if (
|
|
25
|
+
entry.name.startsWith(".") ||
|
|
26
|
+
entry.name === "node_modules" ||
|
|
27
|
+
entry.name === "dist" ||
|
|
28
|
+
entry.name === "build" ||
|
|
29
|
+
entry.name === "coverage"
|
|
30
|
+
) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fullPath = join(rootPath, entry.name);
|
|
35
|
+
|
|
36
|
+
if (entry.isFile() && entry.name === "package.json") {
|
|
37
|
+
results.push({
|
|
38
|
+
path: rootPath,
|
|
39
|
+
depth: currentDepth,
|
|
40
|
+
});
|
|
41
|
+
} else if (entry.isDirectory()) {
|
|
42
|
+
await findAllPackageJsons(fullPath, maxDepth, currentDepth + 1, results);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// Permission denied or other errors - skip
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return results;
|
|
50
|
+
}
|