@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.
Files changed (3) hide show
  1. package/README.md +10 -0
  2. package/cli/cli.js +74 -2
  3. 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
- .action((options) => {
168
- console.log('Scanning lockfile:', options.file);
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.0",
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": {