-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathscan-github-repo.js
More file actions
98 lines (87 loc) · 3.31 KB
/
Copy pathscan-github-repo.js
File metadata and controls
98 lines (87 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env node
// ─────────────────────────────────────────────────────────────
// scan-github-repo.js
// Scan any public GitHub repo programmatically using ai-scanner as a library
//
// Usage:
// node scan-github-repo.js https://github.com/user/repo
// ─────────────────────────────────────────────────────────────
const { execSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const os = require("os");
// Import ai-scanner as a library
const { Scanner } = require("../lib/scanner");
const repoUrl = process.argv[2];
if (!repoUrl) {
console.log("Usage: node scan-github-repo.js <github-repo-url>");
console.log("");
console.log("Example:");
console.log(
" node scan-github-repo.js https://github.com/langchain-ai/langchain"
);
process.exit(1);
}
// Extract repo name
const repoName = repoUrl.split("/").pop().replace(/\.git$/, "");
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ai-scanner-"));
const clonePath = path.join(tempDir, repoName);
try {
// Shallow clone
console.log(`📥 Cloning ${repoUrl} ...`);
execSync(`git clone --depth 1 --quiet "${repoUrl}" "${clonePath}"`, {
stdio: "inherit",
});
// Scan using the library API
console.log(`\n🔍 Scanning ${repoName} ...\n`);
const scanner = new Scanner({ rootDir: clonePath });
const result = scanner.scan();
// Print summary
console.log(`\n📊 Results for ${repoName}:`);
console.log(` Files scanned: ${result.stats.filesScanned}`);
console.log(` Total findings: ${result.stats.totalFindings}`);
console.log(` Critical: ${result.stats.criticalFindings}`);
console.log(` High: ${result.stats.highFindings}`);
console.log(` Info: ${result.stats.infoFindings}`);
// Group by type
const byType = {};
for (const f of result.findings) {
if (!byType[f.type]) byType[f.type] = [];
byType[f.type].push(f);
}
// Show unique detections per type
console.log("\n🤖 AI Stack Detected:");
if (byType.sdk) {
const unique = [...new Set(byType.sdk.map((f) => f.patternName))];
console.log(` SDKs: ${unique.join(", ")}`);
}
if (byType.framework) {
const unique = [...new Set(byType.framework.map((f) => f.patternName))];
console.log(` Frameworks: ${unique.join(", ")}`);
}
if (byType.token) {
const unique = [...new Set(byType.token.map((f) => f.patternName))];
console.log(` ⚠️ Tokens: ${unique.join(", ")}`);
}
// Save JSON report
const reportPath = path.join(process.cwd(), `${repoName}-report.json`);
const report = {
repo: repoUrl,
scanDate: new Date().toISOString(),
summary: result.stats,
findings: result.findings.map((f) => ({
severity: f.severity,
type: f.type,
name: f.patternName,
file: f.file,
line: f.line,
match: f.matchedText,
})),
};
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
console.log(`\n📄 Report saved to ${reportPath}`);
} finally {
// Cleanup
fs.rmSync(tempDir, { recursive: true, force: true });
console.log("🧹 Cleaned up temporary files.");
}