codeprobe-scanner 1.0.8 → 1.0.10
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 +159 -6
- package/package.json +1 -1
- package/src/cli/commands/scan.ts +26 -18
- package/src/engine/index.ts +2 -2
- package/src/engine/scraper.ts +118 -90
package/README.md
CHANGED
|
@@ -1,15 +1,168 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CodeProbe
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Automated vulnerability scanner for Node.js projects. Scans your `package.json` dependencies against live security databases, verifies exploits in isolated sandboxes, generates AI patches, and shows you what's trending in npm security right now.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx codeprobe-scanner scan .
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
1. **Scans your dependencies** — reads `package.json` and checks every package against [OSV.dev](https://osv.dev) (the same database behind `npm audit`)
|
|
14
|
+
2. **Verifies exploits** — spins up isolated [Daytona](https://daytona.io) sandboxes to confirm whether a CVE is actually exploitable in your version, not just theoretical
|
|
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
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g codeprobe-scanner
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or run without installing:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx codeprobe-scanner scan .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Requires [Bun](https://bun.sh) — installed automatically if not present.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Scan a project
|
|
4
39
|
|
|
5
40
|
```bash
|
|
6
|
-
|
|
41
|
+
codeprobe scan .
|
|
42
|
+
codeprobe scan ./my-app
|
|
7
43
|
```
|
|
8
44
|
|
|
9
|
-
|
|
45
|
+
Output:
|
|
46
|
+
|
|
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...
|
|
54
|
+
|
|
55
|
+
SCAN COMPLETE
|
|
56
|
+
Risk Score: 6.4/10 (MEDIUM)
|
|
57
|
+
|
|
58
|
+
CVE Details:
|
|
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
|
+
```
|
|
68
|
+
|
|
69
|
+
### Scan and auto-fix
|
|
10
70
|
|
|
11
71
|
```bash
|
|
12
|
-
|
|
72
|
+
codeprobe scan . --fix
|
|
13
73
|
```
|
|
14
74
|
|
|
15
|
-
|
|
75
|
+
Walks you through each vulnerability interactively. For each one you approve, it:
|
|
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
|
|
80
|
+
|
|
81
|
+
### Other commands
|
|
82
|
+
|
|
83
|
+
```bash
|
|
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
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
No API keys are required for basic scanning — OSV.dev and the GitHub Advisory Database are free public APIs.
|
|
95
|
+
|
|
96
|
+
For exploit verification and AI patch generation, configure optional keys once:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
codeprobe config set daytona_api_key <key> # sandbox exploit verification
|
|
100
|
+
codeprobe config set kimi_api_key <key> # AI patch generation
|
|
101
|
+
codeprobe config set nosana_api_key <key> # backup LLM for patches
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Keys are stored encrypted at `~/.codeprobe/config.json`. You can also pass them as environment variables:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
DAYTONA_API_KEY=xxx KIMI_API_KEY=xxx codeprobe scan .
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Key | Where to get it | What it enables |
|
|
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 |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## CI / GitHub Actions
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
- name: Security scan
|
|
122
|
+
run: npx codeprobe-scanner scan . --json > security-report.json
|
|
123
|
+
|
|
124
|
+
- name: Fail on exploitable CVEs
|
|
125
|
+
run: npx codeprobe-scanner scan .
|
|
126
|
+
# exits with code 1 if exploitable CVEs found
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Exit codes
|
|
132
|
+
|
|
133
|
+
| Code | Meaning |
|
|
134
|
+
|------|---------|
|
|
135
|
+
| `0` | No vulnerabilities found |
|
|
136
|
+
| `1` | Vulnerabilities found |
|
|
137
|
+
| `2` | Scan error |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## How it works
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
package.json
|
|
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)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
No source code is sent to any external service. Only package names and versions are used for lookups.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
package/package.json
CHANGED
package/src/cli/commands/scan.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { handleError, CodeProbeError } from '../errors.js';
|
|
|
8
8
|
import { generateScanId, formatRiskScore, msToHuman } from '../../shared/utils.js';
|
|
9
9
|
import { Report } from '../../shared/types.js';
|
|
10
10
|
import { createEngine } from '../../engine/index.js';
|
|
11
|
-
import {
|
|
11
|
+
import { createScraper } from '../../engine/scraper.js';
|
|
12
12
|
|
|
13
13
|
interface ScanOptions {
|
|
14
14
|
fix: boolean;
|
|
@@ -105,23 +105,6 @@ export async function scanCommand(args: string[]): Promise<void> {
|
|
|
105
105
|
|
|
106
106
|
logger.printHeader();
|
|
107
107
|
|
|
108
|
-
// Check for required API key
|
|
109
|
-
const brightDataKey = process.env.BRIGHT_DATA_API_KEY || await getConfig("bright_data_api_key");
|
|
110
|
-
if (!brightDataKey) {
|
|
111
|
-
console.log(chalk.yellow('\n⚠️ No Bright Data API key configured.'));
|
|
112
|
-
console.log(chalk.dim(' CVE lookup requires a Bright Data account (free tier available).'));
|
|
113
|
-
console.log(chalk.dim(' Get a key at: https://brightdata.com'));
|
|
114
|
-
console.log('');
|
|
115
|
-
console.log(chalk.cyan(' To configure:'));
|
|
116
|
-
console.log(chalk.white(' codeprobe config set bright_data_api_key <your-key>'));
|
|
117
|
-
console.log('');
|
|
118
|
-
console.log(chalk.dim(' Optional keys for full functionality:'));
|
|
119
|
-
console.log(chalk.dim(' codeprobe config set daytona_api_key <key> # exploit verification'));
|
|
120
|
-
console.log(chalk.dim(' codeprobe config set kimi_api_key <key> # AI patch generation'));
|
|
121
|
-
console.log('');
|
|
122
|
-
process.exit(EXIT_CODES.SCAN_FAILED);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
108
|
const startTime = Date.now();
|
|
126
109
|
|
|
127
110
|
try {
|
|
@@ -139,6 +122,31 @@ export async function scanCommand(args: string[]): Promise<void> {
|
|
|
139
122
|
// Display results
|
|
140
123
|
displayReport(report, options.json, duration);
|
|
141
124
|
|
|
125
|
+
// Show recent npm threats from GitHub Advisory Database
|
|
126
|
+
if (!options.json) {
|
|
127
|
+
try {
|
|
128
|
+
const scraper = createScraper();
|
|
129
|
+
const threats = await scraper.fetchRecentThreats();
|
|
130
|
+
if (threats.length > 0) {
|
|
131
|
+
console.log(chalk.bold('\n🌐 Recent npm Security Threats (GitHub Advisory Database):'));
|
|
132
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
133
|
+
for (const t of threats.slice(0, 5)) {
|
|
134
|
+
const sev = t.severity === 'CRITICAL' ? chalk.red(t.severity)
|
|
135
|
+
: t.severity === 'HIGH' ? chalk.yellow(t.severity)
|
|
136
|
+
: chalk.blue(t.severity);
|
|
137
|
+
console.log(` ${sev} ${chalk.bold(t.title)}`);
|
|
138
|
+
if (t.packages.length > 0) {
|
|
139
|
+
console.log(chalk.dim(` Packages: ${t.packages.join(', ')}`));
|
|
140
|
+
}
|
|
141
|
+
console.log(chalk.dim(` ${t.published?.slice(0, 10)} · ${t.url}`));
|
|
142
|
+
}
|
|
143
|
+
console.log('');
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
// Non-fatal — threats feed is best-effort
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
142
150
|
// If --fix, handle that next
|
|
143
151
|
if (options.fix) {
|
|
144
152
|
const { scanWithFixCommand } = await import('./scan-with-fix.js');
|
package/src/engine/index.ts
CHANGED
|
@@ -27,8 +27,8 @@ export class CodeProbeEngine {
|
|
|
27
27
|
const dependencies = await this.parser.parseDependencies(repoPath);
|
|
28
28
|
console.log(` Found ${dependencies.length} dependencies`);
|
|
29
29
|
|
|
30
|
-
// Step 2: Scrape CVEs
|
|
31
|
-
console.log("
|
|
30
|
+
// Step 2: Scrape CVEs from OSV.dev + npm audit
|
|
31
|
+
console.log("🔍 Checking OSV.dev + npm advisory database...");
|
|
32
32
|
const cves = await this.scraper.scrapeAll(dependencies);
|
|
33
33
|
console.log(` Found ${cves.length} CVEs`);
|
|
34
34
|
|
package/src/engine/scraper.ts
CHANGED
|
@@ -1,34 +1,111 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import { CVE
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { CVE } from "../shared/types";
|
|
3
|
+
import { DEMO_CVE } from "../shared/constants";
|
|
4
|
+
|
|
5
|
+
const OSV_API = "https://api.osv.dev/v1";
|
|
6
|
+
const GITHUB_ADVISORY_API = "https://api.github.com/advisories";
|
|
7
|
+
|
|
8
|
+
export interface ThreatIntel {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
severity: string;
|
|
12
|
+
packages: string[];
|
|
13
|
+
published: string;
|
|
14
|
+
url: string;
|
|
15
|
+
summary: string;
|
|
16
|
+
}
|
|
5
17
|
|
|
6
18
|
export class CVEScraper {
|
|
7
|
-
|
|
8
|
-
private
|
|
19
|
+
// Query OSV.dev — exact package+version match, no false positives
|
|
20
|
+
private async queryOSV(packageName: string, version: string): Promise<CVE[]> {
|
|
21
|
+
try {
|
|
22
|
+
const response = await axios.post(
|
|
23
|
+
`${OSV_API}/query`,
|
|
24
|
+
{
|
|
25
|
+
package: { name: packageName, ecosystem: "npm" },
|
|
26
|
+
version,
|
|
27
|
+
},
|
|
28
|
+
{ timeout: 10000, headers: { "Content-Type": "application/json" } }
|
|
29
|
+
);
|
|
9
30
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
31
|
+
const vulns = response.data?.vulns;
|
|
32
|
+
if (!Array.isArray(vulns)) return [];
|
|
33
|
+
|
|
34
|
+
return vulns.map((v: any) => {
|
|
35
|
+
const severity = v.severity?.[0]?.score || v.database_specific?.severity || "MEDIUM";
|
|
36
|
+
const cvss = typeof severity === "number" ? severity : this.severityToScore(severity);
|
|
37
|
+
const aliases: string[] = v.aliases || [];
|
|
38
|
+
const cveId = aliases.find((a: string) => a.startsWith("CVE-")) || v.id;
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
id: cveId,
|
|
42
|
+
package: packageName,
|
|
43
|
+
affected_versions: [version],
|
|
44
|
+
fixed_version: this.extractFixedVersion(v, packageName),
|
|
45
|
+
severity: this.normalizeSeverity(severity),
|
|
46
|
+
cvss,
|
|
47
|
+
description: v.details || v.summary || "",
|
|
48
|
+
cwe: v.database_specific?.cwe_ids?.[0] || "",
|
|
49
|
+
exploit_url: `https://osv.dev/vulnerability/${v.id}`,
|
|
50
|
+
} as CVE;
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
14
55
|
}
|
|
15
56
|
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
// Fetch recent npm security threats from GitHub Advisory Database
|
|
58
|
+
async fetchRecentThreats(): Promise<ThreatIntel[]> {
|
|
59
|
+
try {
|
|
60
|
+
const response = await axios.get(GITHUB_ADVISORY_API, {
|
|
61
|
+
params: {
|
|
62
|
+
ecosystem: "npm",
|
|
63
|
+
per_page: 10,
|
|
64
|
+
sort: "published",
|
|
65
|
+
direction: "desc",
|
|
66
|
+
},
|
|
67
|
+
timeout: 10000,
|
|
68
|
+
headers: { Accept: "application/vnd.github+json" },
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return (response.data || []).map((a: any) => ({
|
|
72
|
+
id: a.ghsa_id,
|
|
73
|
+
title: a.summary || a.ghsa_id,
|
|
74
|
+
severity: a.severity?.toUpperCase() || "UNKNOWN",
|
|
75
|
+
packages: (a.vulnerabilities || []).map((v: any) => v.package?.name).filter(Boolean),
|
|
76
|
+
published: a.published_at,
|
|
77
|
+
url: a.html_url || `https://github.com/advisories/${a.ghsa_id}`,
|
|
78
|
+
summary: a.description?.substring(0, 200) || "",
|
|
79
|
+
}));
|
|
80
|
+
} catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
18
84
|
|
|
85
|
+
async scrapeForCVEs(packageName: string, version: string): Promise<CVE[]> {
|
|
19
86
|
if (packageName === DEMO_CVE.package) {
|
|
20
87
|
return [this.buildDemoCVE()];
|
|
21
88
|
}
|
|
89
|
+
return await this.queryOSV(packageName, version);
|
|
90
|
+
}
|
|
22
91
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
92
|
+
async scrapeAll(
|
|
93
|
+
dependencies: Array<{ name: string; version: string }>
|
|
94
|
+
): Promise<CVE[]> {
|
|
95
|
+
const results = await Promise.all(
|
|
96
|
+
dependencies.map(dep => this.scrapeForCVEs(dep.name, dep.version))
|
|
97
|
+
);
|
|
98
|
+
const seen = new Set<string>();
|
|
99
|
+
const allCVEs: CVE[] = [];
|
|
100
|
+
for (const cves of results) {
|
|
101
|
+
for (const cve of cves) {
|
|
102
|
+
if (!seen.has(cve.id)) {
|
|
103
|
+
seen.add(cve.id);
|
|
104
|
+
allCVEs.push(cve);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
31
107
|
}
|
|
108
|
+
return allCVEs;
|
|
32
109
|
}
|
|
33
110
|
|
|
34
111
|
private buildDemoCVE(): CVE {
|
|
@@ -41,87 +118,38 @@ export class CVEScraper {
|
|
|
41
118
|
cvss: DEMO_CVE.cvss,
|
|
42
119
|
description: DEMO_CVE.description,
|
|
43
120
|
cwe: "CWE-94",
|
|
44
|
-
exploit_url:
|
|
121
|
+
exploit_url: `https://nvd.nist.gov/vuln/detail/${DEMO_CVE.id}`,
|
|
45
122
|
};
|
|
46
123
|
}
|
|
47
124
|
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
{
|
|
57
|
-
headers: {
|
|
58
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
59
|
-
"Accept": "application/json",
|
|
60
|
-
},
|
|
61
|
-
timeout: TIMEOUTS.BRIGHT_DATA_SCRAPE,
|
|
125
|
+
private extractFixedVersion(vuln: any, packageName: string): string {
|
|
126
|
+
const affected = vuln.affected || [];
|
|
127
|
+
for (const a of affected) {
|
|
128
|
+
if (a.package?.name === packageName) {
|
|
129
|
+
for (const range of a.ranges || []) {
|
|
130
|
+
for (const event of range.events || []) {
|
|
131
|
+
if (event.fixed) return event.fixed;
|
|
132
|
+
}
|
|
62
133
|
}
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
if (response.data?.vulnerabilities && Array.isArray(response.data.vulnerabilities)) {
|
|
66
|
-
return response.data.vulnerabilities
|
|
67
|
-
.filter((vuln: any) => {
|
|
68
|
-
const affectedProducts = vuln.cve?.configurations?.[0]?.nodes?.flatMap((n: any) =>
|
|
69
|
-
n.cpeMatch?.map((m: any) => m.criteria) || []
|
|
70
|
-
) || [];
|
|
71
|
-
return affectedProducts.some((p: string) => p?.includes(packageName));
|
|
72
|
-
})
|
|
73
|
-
.map((vuln: any) => {
|
|
74
|
-
const descriptions = vuln.cve?.descriptions || [];
|
|
75
|
-
return {
|
|
76
|
-
id: vuln.id,
|
|
77
|
-
package: packageName,
|
|
78
|
-
affected_versions: [version],
|
|
79
|
-
fixed_version: "",
|
|
80
|
-
severity: vuln.impact?.baseSeverity || "MEDIUM",
|
|
81
|
-
cvss: vuln.impact?.baseScore || 5.0,
|
|
82
|
-
description: descriptions[0]?.value || "",
|
|
83
|
-
cwe: vuln.cve?.weaknesses?.[0]?.source || "",
|
|
84
|
-
exploit_url: `https://nvd.nist.gov/vuln/detail/${vuln.id}`,
|
|
85
|
-
};
|
|
86
|
-
})
|
|
87
|
-
.slice(0, 5); // Limit to top 5 results
|
|
88
134
|
}
|
|
89
|
-
|
|
90
|
-
return [];
|
|
91
|
-
} catch (error) {
|
|
92
|
-
// Bright Data failed, will fall back to cache
|
|
93
|
-
console.warn(`[Bright Data] Scraping failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
-
throw new Error(`Bright Data API call failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
95
135
|
}
|
|
136
|
+
return "";
|
|
96
137
|
}
|
|
97
138
|
|
|
98
|
-
private
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const content = await cacheFile.text();
|
|
105
|
-
const cacheData = JSON.parse(content);
|
|
106
|
-
return cacheData.cves || [];
|
|
107
|
-
}
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.warn("Failed to load CVE cache:", error);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Return at least the demo CVE
|
|
113
|
-
return [this.buildDemoCVE()];
|
|
139
|
+
private normalizeSeverity(s: string): "CRITICAL" | "HIGH" | "MEDIUM" | "LOW" {
|
|
140
|
+
const upper = (s || "").toUpperCase();
|
|
141
|
+
if (upper === "CRITICAL") return "CRITICAL";
|
|
142
|
+
if (upper === "HIGH") return "HIGH";
|
|
143
|
+
if (upper === "LOW") return "LOW";
|
|
144
|
+
return "MEDIUM";
|
|
114
145
|
}
|
|
115
146
|
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return allCVEs;
|
|
147
|
+
private severityToScore(s: string): number {
|
|
148
|
+
const upper = (s || "").toUpperCase();
|
|
149
|
+
if (upper === "CRITICAL") return 9.0;
|
|
150
|
+
if (upper === "HIGH") return 7.5;
|
|
151
|
+
if (upper === "LOW") return 3.0;
|
|
152
|
+
return 5.0;
|
|
125
153
|
}
|
|
126
154
|
}
|
|
127
155
|
|