scan-compromised 1.0.21 → 1.1.1
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 +14 -1
- package/package.json +2 -1
- package/scripts/index.js +9 -5
- package/scripts/logger.js +31 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
🔍 A CLI tool to detect known compromised npm packages in your project.
|
|
4
4
|
|
|
5
|
+
**No third-party dependencies:** This tool is fully self-contained and does not rely on any external npm packages or libraries. You can use it with confidence in sensitive or locked-down environments.
|
|
6
|
+
|
|
5
7
|
This scanner checks your `package.json`, `package-lock.json`, `yarn.lock`, and `pnpm-lock.yaml` files for any packages that were compromised in recent supply chain attacks — including the September 2025 Shai-Hulud incident.
|
|
6
8
|
|
|
7
9
|
It flags:
|
|
@@ -12,7 +14,7 @@ It flags:
|
|
|
12
14
|
|
|
13
15
|
## 🚀 Installation
|
|
14
16
|
|
|
15
|
-
### Run directly with `npx` (no install)
|
|
17
|
+
### Recommended: Run directly with `npx` (no install)
|
|
16
18
|
```bash
|
|
17
19
|
npx scan-compromised
|
|
18
20
|
```
|
|
@@ -22,10 +24,21 @@ npm install -g scan-compromised
|
|
|
22
24
|
scan-compromised
|
|
23
25
|
```
|
|
24
26
|
## 📦 Usage
|
|
27
|
+
|
|
25
28
|
### Basic scan
|
|
26
29
|
```bash
|
|
27
30
|
scan-compromised
|
|
28
31
|
```
|
|
32
|
+
|
|
33
|
+
### Hard gate: block installs with known advisories
|
|
34
|
+
Add this to your project's `preinstall` script in `package.json`:
|
|
35
|
+
```json
|
|
36
|
+
"scripts": {
|
|
37
|
+
"preinstall": "npx scan-compromised"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
This will prevent installation of any dependencies with known advisories, acting as a hard gate in your supply chain.
|
|
41
|
+
|
|
29
42
|
### JSON output (for CI integration)
|
|
30
43
|
```bash
|
|
31
44
|
scan-compromised --json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scan-compromised",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "A simple npm CLI tool (starter template)",
|
|
5
5
|
"main": "scripts/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"scripts/index.js",
|
|
16
|
+
"scripts/logger.js",
|
|
16
17
|
"data/threats.json",
|
|
17
18
|
"config/config.js"
|
|
18
19
|
],
|
package/scripts/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { join, isAbsolute, dirname } from "path";
|
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import util from "util";
|
|
6
6
|
import CONFIG from "../config/config.js";
|
|
7
|
+
import { logToRoot } from "./logger.js";
|
|
7
8
|
|
|
8
9
|
// Create a debug logger for the "threats" namespace
|
|
9
10
|
const debug = util.debuglog("threats");
|
|
@@ -272,15 +273,18 @@ function scanPnpmLock(content, bad) {
|
|
|
272
273
|
debug("Bad findings: %d, Warn findings: %d", badFindings.length, warnFindings.length);
|
|
273
274
|
|
|
274
275
|
warnFindings.forEach(f =>
|
|
275
|
-
|
|
276
|
+
logToRoot(`WARNING: ${f.pkg}@${f.version} in ${f.file} (${f.where}) — package had previous vulnerability, but version is not flagged with advisory.`)
|
|
276
277
|
);
|
|
277
278
|
|
|
278
|
-
badFindings.forEach(f =>
|
|
279
|
-
console.log(`❌ ALERT: ${f.pkg}@${f.version} in ${f.file} (${f.where}) — known
|
|
280
|
-
|
|
279
|
+
badFindings.forEach(f => {
|
|
280
|
+
console.log(`❌ ALERT: ${f.pkg}@${f.version} in ${f.file} (${f.where}) — known advisory`);
|
|
281
|
+
logToRoot(`ALERT: ${f.pkg}@${f.version} in ${f.file} (${f.where}) — known advisory`);
|
|
282
|
+
});
|
|
281
283
|
|
|
282
284
|
if (badFindings.length === 0) {
|
|
283
|
-
console.log("✅ No known
|
|
285
|
+
console.log("✅ No known advisories detected.");
|
|
286
|
+
logToRoot("No known advisories detected.");
|
|
287
|
+
console.log("Scan completed, results can be found in scan-compromised.log");
|
|
284
288
|
} else {
|
|
285
289
|
debug("Exiting with code 1 due to bad findings");
|
|
286
290
|
process.exit(1);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { appendFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname, join, parse } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Walk up from a starting directory until we find a package.json,
|
|
7
|
+
* then return that directory as the project root.
|
|
8
|
+
*/
|
|
9
|
+
function findProjectRoot(startDir = dirname(fileURLToPath(import.meta.url))) {
|
|
10
|
+
let dir = startDir;
|
|
11
|
+
while (dir !== parse(dir).root) {
|
|
12
|
+
if (existsSync(join(dir, 'package.json'))) {
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
dir = dirname(dir);
|
|
16
|
+
}
|
|
17
|
+
return process.cwd(); // fallback
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const projectRoot = findProjectRoot();
|
|
21
|
+
const logFilePath = resolve(projectRoot, 'scan-compromised.log');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Append a message to scan-compromised.log in the project root.
|
|
25
|
+
* @param {string} message - The text to log.
|
|
26
|
+
*/
|
|
27
|
+
export function logToRoot(message) {
|
|
28
|
+
const timestamp = new Date().toISOString();
|
|
29
|
+
const entry = `[${timestamp}] ${message}\n`;
|
|
30
|
+
appendFileSync(logFilePath, entry, { encoding: 'utf8' });
|
|
31
|
+
}
|