@lateos/npm-scan 0.12.0 → 0.12.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 +10 -0
- package/cli/cli.js +74 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,15 @@ npm-scan scan-lockfile --fail-on low
|
|
|
205
205
|
# Generate SARIF v2.1 output for GitHub Advanced Security / VS Code
|
|
206
206
|
npm-scan scan-lockfile --sarif results.sarif
|
|
207
207
|
|
|
208
|
+
# Watch for changes and auto-rescan (single lockfile)
|
|
209
|
+
npm-scan scan-lockfile --watch
|
|
210
|
+
|
|
211
|
+
# Watch with faster debounce (500ms) — great for dev workflows
|
|
212
|
+
npm-scan scan-lockfile --watch --debounce 500
|
|
213
|
+
|
|
214
|
+
# Watch monorepo (all package-lock.json files in workspace)
|
|
215
|
+
npm-scan scan-lockfile --watch --monorepo
|
|
216
|
+
|
|
208
217
|
# Output only risk score (0-10) for dashboards/thresholds
|
|
209
218
|
npm-scan scan-lockfile --score-only
|
|
210
219
|
```
|
|
@@ -621,6 +630,7 @@ See the [Docker quick-start section](#-run-lateosnpm-scan-anywhere-with-docker--
|
|
|
621
630
|
- GitHub Action
|
|
622
631
|
- Pre-commit hook (husky + lint-staged)
|
|
623
632
|
- Docker images + Compose pipeline
|
|
633
|
+
- Watch mode (--watch / --monorepo for auto-rescan)
|
|
624
634
|
|
|
625
635
|
### Premium (🔐 license key)
|
|
626
636
|
|
package/cli/cli.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import { watch } from 'fs';
|
|
5
|
+
import { statSync } from 'fs';
|
|
6
|
+
import { execSync } from 'child_process';
|
|
7
|
+
import { glob } from 'glob';
|
|
4
8
|
import { isFeatureEnabled, generateKey } from '../backend/license.js';
|
|
5
9
|
|
|
6
10
|
function requirePremium(feature, licenseKey) {
|
|
@@ -164,8 +168,76 @@ program
|
|
|
164
168
|
.option('--fail-on <level>', 'Exit with code 1 if findings >= level (low|medium|high|critical)', 'none')
|
|
165
169
|
.option('--csv [file]', 'Output CSV format to file or stdout')
|
|
166
170
|
.option('--sarif [file]', 'Output SARIF v2.1 format to file or stdout')
|
|
167
|
-
.
|
|
168
|
-
|
|
171
|
+
.option('--watch', 'Watch for changes and re-scan automatically')
|
|
172
|
+
.option('--debounce <ms>', 'Debounce delay in ms before rescanning (default: 1000)', '1000')
|
|
173
|
+
.option('--silent', 'Suppress stdout output (useful for piping)')
|
|
174
|
+
.option('--monorepo', 'Scan all package-lock.json files in workspace')
|
|
175
|
+
.action(async (options) => {
|
|
176
|
+
const silent = options.silent;
|
|
177
|
+
const debounce = parseInt(options.debounce, 10) || 1000;
|
|
178
|
+
const isWatch = options.watch;
|
|
179
|
+
const isMonorepo = options.monorepo;
|
|
180
|
+
|
|
181
|
+
if (isWatch) {
|
|
182
|
+
if (isMonorepo) {
|
|
183
|
+
const lockfiles = await glob('**/package-lock.json', { ignore: 'node_modules/**' });
|
|
184
|
+
|
|
185
|
+
if (!silent) {
|
|
186
|
+
console.log(`\x1b[32m✔\x1b[0m npm-scan watch mode (monorepo) — ${lockfiles.length} lockfiles`);
|
|
187
|
+
console.log(` Debounce: ${debounce}ms | Press Ctrl+C to stop\n`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let timers = {};
|
|
191
|
+
for (const lf of lockfiles) {
|
|
192
|
+
if (!silent) console.log(` Watching: ${lf}`);
|
|
193
|
+
const watcher = watch(lf, (eventType) => {
|
|
194
|
+
if (eventType !== 'change') return;
|
|
195
|
+
clearTimeout(timers[lf]);
|
|
196
|
+
timers[lf] = setTimeout(() => {
|
|
197
|
+
if (!silent) {
|
|
198
|
+
console.log(`\n\x1b[90m[${new Date().toLocaleTimeString()}]\x1b[0m ${lf} changed — scanning...`);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
execSync(`node cli/cli.js scan-lockfile -f "${lf}" --fail-on ${options.failOn || 'high'} --silent`, { stdio: silent ? 'ignore' : 'inherit' });
|
|
202
|
+
} catch (e) {}
|
|
203
|
+
}, debounce);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
process.on('SIGINT', () => {
|
|
208
|
+
if (!silent) console.log('\n\x1b[33m✖\x1b[0m Stopped.');
|
|
209
|
+
process.exit(0);
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
const lockfile = options.file;
|
|
213
|
+
let lastSize = 0;
|
|
214
|
+
try { lastSize = statSync(lockfile).size; } catch {}
|
|
215
|
+
|
|
216
|
+
if (!silent) {
|
|
217
|
+
console.log(`\x1b[32m✔\x1b[0m npm-scan watch mode — ${lockfile}`);
|
|
218
|
+
console.log(` Debounce: ${debounce}ms | Press Ctrl+C to stop\n`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const watcher = watch(lockfile, (eventType) => {
|
|
222
|
+
if (eventType !== 'change') return;
|
|
223
|
+
const size = statSync(lockfile).size;
|
|
224
|
+
if (size === lastSize) return;
|
|
225
|
+
lastSize = size;
|
|
226
|
+
if (!silent) console.log(`\n\x1b[90m[${new Date().toLocaleTimeString()}]\x1b[0m ${lockfile} changed — rescanning...`);
|
|
227
|
+
try {
|
|
228
|
+
execSync(`node cli/cli.js scan-lockfile --fail-on ${options.failOn || 'high'} --silent`, { stdio: silent ? 'ignore' : 'inherit' });
|
|
229
|
+
} catch (e) {}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
process.on('SIGINT', () => {
|
|
233
|
+
watcher.close();
|
|
234
|
+
if (!silent) console.log('\n\x1b[33m✖\x1b[0m Stopped.');
|
|
235
|
+
process.exit(0);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
console.log('Scanning lockfile:', options.file);
|
|
240
|
+
}
|
|
169
241
|
});
|
|
170
242
|
|
|
171
243
|
program
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lateos/npm-scan",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Modern npm supply chain security scanner — detects obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation. 11 attack types, SBOM, NIST/EU CRA compliance reporting.",
|
|
5
5
|
"main": "backend/index.js",
|
|
6
6
|
"bin": {
|