codeprobe-scanner 1.0.10 → 1.0.12
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/README.md +153 -112
- package/bin/codeprobe.cjs +1 -1
- package/package.json +2 -2
- package/src/cli/commands/scan-with-fix.ts +100 -74
- package/src/engine/index.ts +5 -2
package/README.md
CHANGED
|
@@ -1,167 +1,208 @@
|
|
|
1
1
|
# CodeProbe
|
|
2
2
|
|
|
3
|
-
Automated vulnerability scanner for Node.js projects. Scans
|
|
3
|
+
Automated vulnerability scanner for Node.js / npm projects. Scans dependencies against OSV.dev and the GitHub Advisory Database, verifies exploits in isolated sandboxes, and optionally auto-patches vulnerable packages.
|
|
4
4
|
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install -g codeprobe-scanner
|
|
5
9
|
```
|
|
6
|
-
|
|
10
|
+
|
|
11
|
+
Or run without installing:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx codeprobe-scanner scan
|
|
7
15
|
```
|
|
8
16
|
|
|
9
|
-
|
|
17
|
+
## Quick Start
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
```sh
|
|
20
|
+
# Scan the current directory
|
|
21
|
+
codeprobe scan
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
3. **Generates patches** — uses Kimi AI to produce a version-bump diff for exploitable vulnerabilities
|
|
16
|
-
4. **Shows recent threats** — pulls the latest npm security advisories from GitHub's Advisory Database so you can see what attacks are trending
|
|
23
|
+
# Scan a specific project
|
|
24
|
+
codeprobe scan ./my-app
|
|
17
25
|
|
|
18
|
-
|
|
26
|
+
# Scan and auto-fix vulnerabilities
|
|
27
|
+
codeprobe scan --fix
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
# Output results as JSON
|
|
30
|
+
codeprobe scan --json > report.json
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
# Show the last scan report
|
|
33
|
+
codeprobe report
|
|
24
34
|
```
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
## Commands
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
npx codeprobe-scanner scan .
|
|
30
|
-
```
|
|
38
|
+
### `codeprobe scan [path]`
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
Scans a repository for known CVEs in its npm dependencies.
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
| Flag | Description |
|
|
43
|
+
|------|-------------|
|
|
44
|
+
| `--fix` | Auto-fix: upgrades vulnerable packages, creates a git branch and commit |
|
|
45
|
+
| `--json` | Print results as JSON (pipe-friendly) |
|
|
46
|
+
| `--verbose` | Show detailed phase-by-phase logs |
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
**What it does:**
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
1. Parses `package.json` / `package-lock.json` / `bun.lock` for installed packages and versions
|
|
51
|
+
2. Queries **OSV.dev** for CVEs matching each package+version
|
|
52
|
+
3. Cross-references the **GitHub Advisory Database** for additional intelligence
|
|
53
|
+
4. Runs exploit verification in an isolated **Daytona sandbox** (when configured)
|
|
54
|
+
5. Saves the report to `~/.codeprobe/scans/<scan-id>.json`
|
|
55
|
+
6. Displays a risk score, CVE table, and recent npm threat feed
|
|
39
56
|
|
|
40
|
-
|
|
41
|
-
codeprobe scan .
|
|
42
|
-
codeprobe scan ./my-app
|
|
43
|
-
```
|
|
57
|
+
**Exit codes:**
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
| Code | Meaning |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| `0` | No vulnerabilities found |
|
|
62
|
+
| `1` | Vulnerabilities found |
|
|
63
|
+
| `2` | Scan failed |
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
⚡ CodeProbe v1.0.0
|
|
49
|
-
📦 Parsing dependencies...
|
|
50
|
-
Found 11 dependencies
|
|
51
|
-
🔍 Checking OSV.dev + npm advisory database...
|
|
52
|
-
Found 3 CVEs
|
|
53
|
-
🎯 Matching dependencies to CVEs...
|
|
65
|
+
### `codeprobe report`
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
Risk Score: 6.4/10 (MEDIUM)
|
|
67
|
+
Displays the most recent scan results from `~/.codeprobe/scans/latest.json`.
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
CVE-2024-39338: axios 1.6.5 [HIGH] ~ Theoretical Risk
|
|
60
|
-
CVE-2023-45133: babel 7.22.0 [CRITICAL] ✓ CONFIRMED EXPLOITABLE
|
|
61
|
-
...
|
|
62
|
-
|
|
63
|
-
🌐 Recent npm Security Threats:
|
|
64
|
-
HIGH esbuild: Missing binary integrity verification enables RCE
|
|
65
|
-
HIGH Budibase: Auth bypass on webhook schema endpoint
|
|
66
|
-
...
|
|
67
|
-
```
|
|
69
|
+
### `codeprobe config set <key> <value>`
|
|
68
70
|
|
|
69
|
-
|
|
71
|
+
Saves a configuration value to `~/.codeprobe/config.json`.
|
|
70
72
|
|
|
71
|
-
```
|
|
72
|
-
codeprobe
|
|
73
|
+
```sh
|
|
74
|
+
codeprobe config set bright_data_api_key YOUR_KEY
|
|
75
|
+
codeprobe config set daytona_api_key YOUR_KEY
|
|
73
76
|
```
|
|
74
77
|
|
|
75
|
-
|
|
76
|
-
- Updates the version in `package.json`
|
|
77
|
-
- Creates a git branch (`codeprobe-security-fixes-<timestamp>`)
|
|
78
|
-
- Commits and pushes
|
|
79
|
-
- Opens a pull request via GitHub CLI
|
|
78
|
+
## Configuration
|
|
80
79
|
|
|
81
|
-
|
|
80
|
+
CodeProbe works out of the box with zero configuration using public APIs. Optional integrations unlock deeper exploit verification.
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
codeprobe report # show last scan results again
|
|
85
|
-
codeprobe scan . --json # JSON output (for CI pipelines)
|
|
86
|
-
codeprobe scan . --verbose # detailed logs
|
|
87
|
-
codeprobe config get # show stored config
|
|
88
|
-
```
|
|
82
|
+
### Environment Variables
|
|
89
83
|
|
|
90
|
-
|
|
84
|
+
| Variable | Description |
|
|
85
|
+
|----------|-------------|
|
|
86
|
+
| `BRIGHT_DATA_API_KEY` | Bright Data scraping proxy (optional) |
|
|
87
|
+
| `DAYTONA_API_KEY` | Daytona sandbox for exploit verification (optional) |
|
|
88
|
+
| `NOSANA_API_KEY` | Nosana LLM for AI-assisted analysis (optional) |
|
|
89
|
+
| `GITHUB_TOKEN` | GitHub token for higher Advisory API rate limits |
|
|
90
|
+
| `PORT` | API server port (default: `3000`) |
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
No API keys are required for basic scanning — OSV.dev and the GitHub Advisory Database are free public APIs.
|
|
92
|
+
Create a `.env` file in your project root or export variables in your shell.
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
## How It Works
|
|
97
95
|
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
```
|
|
97
|
+
package.json / lockfile
|
|
98
|
+
↓
|
|
99
|
+
Dependency Parser
|
|
100
|
+
↓
|
|
101
|
+
OSV.dev + GitHub Advisory DB ──→ CVE list
|
|
102
|
+
↓
|
|
103
|
+
Sandbox Exploit Verification ──→ Confirmed / Theoretical
|
|
104
|
+
↓
|
|
105
|
+
Risk Score + Report
|
|
102
106
|
```
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
- **Risk score** = (confirmed exploitable × 10) + (theoretical × 3)
|
|
109
|
+
- Scans are saved locally at `~/.codeprobe/scans/`
|
|
110
|
+
- `latest.json` always points to the most recent scan
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
DAYTONA_API_KEY=xxx KIMI_API_KEY=xxx codeprobe scan .
|
|
108
|
-
```
|
|
112
|
+
## Auto-Fix (`--fix`)
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
|-----|----------------|-----------------|
|
|
112
|
-
| `DAYTONA_API_KEY` | [daytona.io](https://daytona.io) | Confirms if CVEs are truly exploitable |
|
|
113
|
-
| `KIMI_API_KEY` | [aimlapi.com](https://aimlapi.com) | AI-generated patch diffs |
|
|
114
|
-
| `NOSANA_API_KEY` | [nosana.io](https://nosana.io) | Backup LLM for patches |
|
|
114
|
+
When `--fix` is passed, CodeProbe:
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
1. Identifies vulnerable packages that have a patched version available
|
|
117
|
+
2. Updates `package.json` to the safe version
|
|
118
|
+
3. Runs `bun install` (or `npm install`) to apply the change
|
|
119
|
+
4. Creates a git commit on a new branch: `codeprobe-fix/<scan-id>`
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
Review the branch and open a PR — no changes are pushed automatically.
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
## API Server
|
|
124
|
+
|
|
125
|
+
Start the REST API server:
|
|
123
126
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# exits with code 1 if exploitable CVEs found
|
|
127
|
+
```sh
|
|
128
|
+
bun run src/api/server.ts
|
|
127
129
|
```
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
| Endpoint | Method | Description |
|
|
132
|
+
|----------|--------|-------------|
|
|
133
|
+
| `POST /api/scan` | POST | Trigger a scan (`{ "repoPath": "./path" }`) |
|
|
134
|
+
| `GET /api/scans` | GET | List all past scans (requires auth) |
|
|
135
|
+
| `GET /api/scans/:id` | GET | Get a specific scan (requires auth) |
|
|
136
|
+
| `GET /api/auth/github` | GET | GitHub OAuth callback |
|
|
137
|
+
| `GET /api/auth/logout` | GET | Logout |
|
|
130
138
|
|
|
131
|
-
|
|
139
|
+
Authentication: pass a `Bearer <token>` header. In development mode any non-empty token is accepted.
|
|
132
140
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
## Docker
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
docker build -t codeprobe .
|
|
145
|
+
docker run -e PORT=8080 -p 8080:8080 codeprobe
|
|
146
|
+
```
|
|
138
147
|
|
|
139
|
-
|
|
148
|
+
## GitHub Actions
|
|
140
149
|
|
|
141
|
-
|
|
150
|
+
Add CodeProbe to your CI pipeline:
|
|
142
151
|
|
|
152
|
+
```yaml
|
|
153
|
+
# .github/workflows/codeprobe-scan.yml
|
|
154
|
+
name: Security Scan
|
|
155
|
+
on: [push, pull_request]
|
|
156
|
+
jobs:
|
|
157
|
+
scan:
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v3
|
|
161
|
+
- run: npx codeprobe-scanner scan --json > report.json
|
|
162
|
+
- uses: actions/upload-artifact@v3
|
|
163
|
+
with:
|
|
164
|
+
name: security-report
|
|
165
|
+
path: report.json
|
|
143
166
|
```
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Parse deps (reads your dependencies + exact versions)
|
|
148
|
-
│
|
|
149
|
-
▼
|
|
150
|
-
OSV.dev query (exact version match — no false positives)
|
|
151
|
-
│
|
|
152
|
-
▼
|
|
153
|
-
Daytona sandbox (runs the exploit in isolation to confirm)
|
|
154
|
-
│
|
|
155
|
-
▼
|
|
156
|
-
Kimi / Nosana (generates a patch diff via AI)
|
|
157
|
-
│
|
|
158
|
-
▼
|
|
159
|
-
Report + PR (shows results, optionally opens a fix PR)
|
|
167
|
+
|
|
168
|
+
## Output Example
|
|
169
|
+
|
|
160
170
|
```
|
|
171
|
+
╔══════════════════════════════════════════╗
|
|
172
|
+
║ CodeProbe Scanner ║
|
|
173
|
+
╚══════════════════════════════════════════╝
|
|
161
174
|
|
|
162
|
-
|
|
175
|
+
SCAN COMPLETE
|
|
176
|
+
Risk Score: 🔴 HIGH (43)
|
|
177
|
+
Confirmed Exploitable: 2 | Theoretical Risk: 5
|
|
178
|
+
Patches Available: 3
|
|
179
|
+
Duration: 8.3s
|
|
163
180
|
|
|
164
|
-
|
|
181
|
+
CVE Details:
|
|
182
|
+
CVE-2022-29078: ejs 3.1.6 [CRITICAL] ✓ CONFIRMED EXPLOITABLE
|
|
183
|
+
→ Patch available: 3.1.7
|
|
184
|
+
CVE-2023-44487: http2-server 1.0.0 [HIGH] ✓ CONFIRMED EXPLOITABLE
|
|
185
|
+
→ Patch available: 1.0.1
|
|
186
|
+
|
|
187
|
+
🌐 Recent npm Security Threats (GitHub Advisory Database):
|
|
188
|
+
CRITICAL lodash - Prototype Pollution
|
|
189
|
+
HIGH axios - SSRF via redirect
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Project Structure
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
src/
|
|
196
|
+
├── cli/ # CLI entry point and commands (scan, report, config)
|
|
197
|
+
├── engine/ # Core scanner: parser, scraper, sandbox, patcher
|
|
198
|
+
├── api/ # REST API server
|
|
199
|
+
├── shared/ # Types, constants, utilities
|
|
200
|
+
├── integrations/ # VideoDB, Daytona, Nosana integrations
|
|
201
|
+
├── bot/ # Bot server
|
|
202
|
+
└── mcp/ # MCP server
|
|
203
|
+
bin/
|
|
204
|
+
└── codeprobe.cjs # CLI binary entry point
|
|
205
|
+
```
|
|
165
206
|
|
|
166
207
|
## License
|
|
167
208
|
|
package/bin/codeprobe.cjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeprobe-scanner",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Automated vulnerability scanner with exploit verification and video evidence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@daytona/sdk": "^0.187.0",
|
|
17
|
-
"axios": "^1.
|
|
17
|
+
"axios": "^1.16.0",
|
|
18
18
|
"chalk": "^5.3.0",
|
|
19
19
|
"cli-table3": "^0.6.3",
|
|
20
20
|
"dayjs": "^1.11.10",
|
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import path from 'path';
|
|
3
5
|
import dayjs from 'dayjs';
|
|
4
6
|
import { Report } from '../../shared/types.js';
|
|
5
7
|
import { ProgressLogger } from '../progress.js';
|
|
6
8
|
import { GitError, handleError } from '../errors.js';
|
|
7
9
|
import { EXIT_CODES } from '../../shared/constants.js';
|
|
8
10
|
|
|
9
|
-
function getGitStatus(): string {
|
|
10
|
-
try {
|
|
11
|
-
return execSync('git status --porcelain', { encoding: 'utf-8' });
|
|
12
|
-
} catch {
|
|
13
|
-
throw new GitError('Not a git repository');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
function checkGitRepo(): boolean {
|
|
18
12
|
try {
|
|
19
|
-
execSync('git rev-parse --git-dir', { encoding: 'utf-8' });
|
|
13
|
+
execSync('git rev-parse --git-dir', { encoding: 'utf-8', stdio: 'pipe' });
|
|
20
14
|
return true;
|
|
21
15
|
} catch {
|
|
22
16
|
return false;
|
|
@@ -24,25 +18,48 @@ function checkGitRepo(): boolean {
|
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
function isGitDirty(): boolean {
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
try {
|
|
22
|
+
const status = execSync('git status --porcelain', { encoding: 'utf-8', stdio: 'pipe' });
|
|
23
|
+
return status.trim().length > 0;
|
|
24
|
+
} catch {
|
|
25
|
+
throw new GitError('Not a git repository');
|
|
26
|
+
}
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
function createBranch(name: string): void {
|
|
32
30
|
try {
|
|
33
|
-
execSync(`git checkout -b ${name}`, { encoding: 'utf-8' });
|
|
34
|
-
} catch
|
|
31
|
+
execSync(`git checkout -b ${name}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
32
|
+
} catch {
|
|
35
33
|
throw new GitError(`Failed to create branch: ${name}`);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
function
|
|
37
|
+
function commitAndPush(message: string, branchName: string): void {
|
|
38
|
+
execSync('git add package.json', { encoding: 'utf-8', stdio: 'pipe' });
|
|
39
|
+
execSync(`git -c commit.gpgsign=false commit -m "${message}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
40
40
|
try {
|
|
41
|
-
execSync(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
execSync(`git push -u origin ${branchName}`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
42
|
+
} catch {
|
|
43
|
+
// Push failed — not fatal, user can push manually
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function applyVersionBumps(
|
|
48
|
+
repoPath: string,
|
|
49
|
+
bumps: Array<{ package: string; from: string; to: string }>
|
|
50
|
+
): void {
|
|
51
|
+
const pkgPath = path.join(repoPath, 'package.json');
|
|
52
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
53
|
+
|
|
54
|
+
for (const bump of bumps) {
|
|
55
|
+
if (pkg.dependencies?.[bump.package]) {
|
|
56
|
+
pkg.dependencies[bump.package] = `^${bump.to}`;
|
|
57
|
+
} else if (pkg.devDependencies?.[bump.package]) {
|
|
58
|
+
pkg.devDependencies[bump.package] = `^${bump.to}`;
|
|
59
|
+
}
|
|
45
60
|
}
|
|
61
|
+
|
|
62
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
46
63
|
}
|
|
47
64
|
|
|
48
65
|
export async function scanWithFixCommand(
|
|
@@ -52,72 +69,81 @@ export async function scanWithFixCommand(
|
|
|
52
69
|
): Promise<void> {
|
|
53
70
|
console.log('');
|
|
54
71
|
logger.printSeparator();
|
|
55
|
-
logger.logPhaseStart('git', 'Preparing to apply patches');
|
|
56
72
|
|
|
57
|
-
|
|
58
|
-
// Check git repo exists
|
|
59
|
-
if (!checkGitRepo()) {
|
|
60
|
-
throw new GitError('Not a git repository. Run: git init');
|
|
61
|
-
}
|
|
73
|
+
const repoPath = path.resolve(args[0] || '.');
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
75
|
+
// Collect CVEs that have a known fixed version
|
|
76
|
+
const fixable = report.scan.cves.filter(
|
|
77
|
+
(cve) => cve.version_fixed && cve.version_fixed.trim() !== ''
|
|
78
|
+
);
|
|
68
79
|
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
if (fixable.length === 0) {
|
|
81
|
+
console.log(chalk.yellow('⚠️ No CVEs with known fixes found.'));
|
|
82
|
+
console.log(chalk.dim(' All vulnerabilities are either unpatched or already on the latest version.'));
|
|
83
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
84
|
+
}
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
// Deduplicate: one bump per package (use highest fixed version)
|
|
87
|
+
const bumpMap = new Map<string, { from: string; to: string; cves: string[] }>();
|
|
88
|
+
for (const cve of fixable) {
|
|
89
|
+
const existing = bumpMap.get(cve.package);
|
|
90
|
+
if (!existing) {
|
|
91
|
+
bumpMap.set(cve.package, {
|
|
92
|
+
from: cve.version_vulnerable,
|
|
93
|
+
to: cve.version_fixed!,
|
|
94
|
+
cves: [cve.id],
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
existing.cves.push(cve.id);
|
|
75
98
|
}
|
|
99
|
+
}
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
const bumps = Array.from(bumpMap.entries()).map(([pkg, info]) => ({
|
|
102
|
+
package: pkg,
|
|
103
|
+
...info,
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
// Show what will be changed
|
|
107
|
+
console.log(chalk.bold(`\n📦 ${bumps.length} package(s) can be updated:\n`));
|
|
108
|
+
for (const b of bumps) {
|
|
109
|
+
console.log(
|
|
110
|
+
` ${chalk.cyan(b.package)}: ${chalk.red(b.from)} → ${chalk.green(b.to)}`
|
|
111
|
+
);
|
|
112
|
+
console.log(chalk.dim(` Fixes: ${b.cves.slice(0, 3).join(', ')}${b.cves.length > 3 ? ` +${b.cves.length - 3} more` : ''}`));
|
|
113
|
+
}
|
|
80
114
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
// Check git repo
|
|
116
|
+
if (!checkGitRepo()) {
|
|
117
|
+
console.log(chalk.yellow('\n⚠️ Not a git repository — applying fixes without committing.'));
|
|
118
|
+
applyVersionBumps(repoPath, bumps);
|
|
119
|
+
console.log(chalk.green('\n✓ package.json updated. Run your package manager to install.'));
|
|
120
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
121
|
+
}
|
|
84
122
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
123
|
+
if (isGitDirty()) {
|
|
124
|
+
console.log(chalk.yellow('\n⚠️ Uncommitted changes detected — applying fixes without committing.'));
|
|
125
|
+
applyVersionBumps(repoPath, bumps);
|
|
126
|
+
console.log(chalk.green('\n✓ package.json updated. Commit when ready.'));
|
|
127
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
128
|
+
}
|
|
88
129
|
|
|
89
|
-
|
|
130
|
+
// Create branch and apply fixes
|
|
131
|
+
const branchName = `codeprobe-fix-${dayjs().format('YYYY-MM-DD-HHmmss')}`;
|
|
132
|
+
console.log(chalk.dim(`\nCreating branch: ${branchName}`));
|
|
133
|
+
createBranch(branchName);
|
|
90
134
|
|
|
91
|
-
|
|
92
|
-
|
|
135
|
+
applyVersionBumps(repoPath, bumps);
|
|
136
|
+
console.log(chalk.green('\n✓ package.json updated'));
|
|
93
137
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
logger.logPhaseComplete('patch', `Patched ${cve.package}: ${cve.version_vulnerable} → ${cve.patch_version}`);
|
|
97
|
-
}
|
|
138
|
+
const commitMsg = `security: bump ${bumps.length} vulnerable package(s) via codeprobe\\n\\n${bumps.map(b => `- ${b.package}: ${b.from} -> ${b.to}`).join('\\n')}`;
|
|
139
|
+
commitAndPush(commitMsg, branchName);
|
|
98
140
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
commitChanges(commitMsg);
|
|
108
|
-
logger.logPhaseComplete('git', 'Changes committed');
|
|
109
|
-
|
|
110
|
-
// Show what to do next
|
|
111
|
-
console.log('');
|
|
112
|
-
console.log(chalk.green('✓ Patches applied successfully!'));
|
|
113
|
-
console.log(chalk.cyan(`\nNext steps:`));
|
|
114
|
-
console.log(chalk.cyan(` 1. Review changes: git diff main`));
|
|
115
|
-
console.log(chalk.cyan(` 2. Push branch: git push -u origin ${branchName}`));
|
|
116
|
-
console.log(chalk.cyan(` 3. Create PR on GitHub`));
|
|
117
|
-
|
|
118
|
-
logger.printSeparator();
|
|
119
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
handleError(error, logger, true);
|
|
122
|
-
}
|
|
141
|
+
console.log(chalk.bold.green(`\n✨ Done! Branch '${branchName}' created and pushed.\n`));
|
|
142
|
+
console.log(chalk.cyan('Next steps:'));
|
|
143
|
+
console.log(` 1. Run your package manager: ${chalk.white('bun install')} or ${chalk.white('npm install')}`);
|
|
144
|
+
console.log(` 2. Open a PR from branch: ${chalk.white(branchName)}`);
|
|
145
|
+
console.log('');
|
|
146
|
+
|
|
147
|
+
logger.printSeparator();
|
|
148
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
123
149
|
}
|
package/src/engine/index.ts
CHANGED
|
@@ -61,10 +61,13 @@ export class CodeProbeEngine {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// Step 7: Generate patches
|
|
64
|
+
// Step 7: Generate patches for exploitable CVEs + any HIGH/CRITICAL with a known fix version
|
|
65
65
|
console.log("\x1b[33m[Nosana]\x1b[0m 🔧 Generating patches with LLM...");
|
|
66
66
|
await this.patcher.loadPrebakedPatches();
|
|
67
|
-
const
|
|
67
|
+
const patchCandidates = matchedCves.filter((c) =>
|
|
68
|
+
c.exploitable || ((c.severity === "CRITICAL" || c.severity === "HIGH") && c.version_fixed)
|
|
69
|
+
);
|
|
70
|
+
const patches = await this.patcher.generateAllPatches(patchCandidates);
|
|
68
71
|
for (const cve of matchedCves) {
|
|
69
72
|
if (patches.has(cve.id)) {
|
|
70
73
|
cve.patch_diff = patches.get(cve.id);
|