@lateos/npm-scan 0.14.0 → 0.15.0

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 CHANGED
@@ -3,8 +3,8 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@lateos/npm-scan?style=flat-square)](https://www.npmjs.com/package/@lateos/npm-scan)
4
4
  [![License](https://img.shields.io/badge/license-Apache%202.0%20%2B%20Commons%20Clause-blue?style=flat-square)](LICENSING.md)
5
5
  [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](package.json)
6
- [![Tests](https://img.shields.io/badge/tests-222%20passing-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
7
- [![Coverage](https://img.shields.io/badge/coverage-85%25-yellowgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
6
+ [![Tests](https://img.shields.io/badge/tests-324%20passing-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
7
+ [![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
8
8
  [![Docker](https://img.shields.io/badge/docker-lateos%2Fnpm--scan-2496ED?style=flat-square&logo=docker)](https://hub.docker.com/r/lateos/npm-scan)
9
9
  [![Sigstore](https://img.shields.io/static/v1?label=Sigstore&message=Provenance&color=green&style=flat-square&logo=sigstore)](https://github.com/lateos-ai/npm-scan/actions/workflows/publish.yml)
10
10
 
@@ -71,6 +71,7 @@ Attackers have moved past simple typosquatting. They now ship **obfuscated prein
71
71
  | 🛡️ | **Zero telemetry** | No data leaves your machine. No cloud. No callbacks. |
72
72
  | 💾 | **Local scan history** | SQLite-backed persistence, zero external dependencies |
73
73
  | 🪝 | **Pre-commit hook** | Block threats before commit — one-liner install, scans `package-lock.json` changes |
74
+ | 📎 | **Yarn + pnpm support** | `scan-lockfile` parses `yarn.lock` and `pnpm-lock.yaml` alongside `package-lock.json` |
74
75
 
75
76
  ---
76
77
 
@@ -190,12 +191,16 @@ npm-scan scan --file path/to/malicious-package.tgz
190
191
  ### Scan a lockfile
191
192
 
192
193
  ```bash
193
- # Scan the current project's dependencies
194
+ # Scan the current project's dependencies (auto-detects npm/yarn/pnpm)
194
195
  npm-scan scan-lockfile
195
196
 
196
197
  # Scan a specific lockfile
197
198
  npm-scan scan-lockfile -f ./path/to/package-lock.json
198
199
 
200
+ # Scan yarn.lock or pnpm-lock.yaml
201
+ npm-scan scan-lockfile -f ./yarn.lock --yarn
202
+ npm-scan scan-lockfile -f ./pnpm-lock.yaml --pnpm
203
+
199
204
  # Fail CI/CD on high or critical findings (exit code 1)
200
205
  npm-scan scan-lockfile --fail-on high
201
206
 
@@ -211,7 +216,7 @@ npm-scan scan-lockfile --watch
211
216
  # Watch with faster debounce (500ms) — great for dev workflows
212
217
  npm-scan scan-lockfile --watch --debounce 500
213
218
 
214
- # Watch monorepo (all package-lock.json files in workspace)
219
+ # Watch monorepo (all lockfiles npm/yarn/pnpm — in workspace)
215
220
  npm-scan scan-lockfile --watch --monorepo
216
221
 
217
222
  # Output only risk score (0-10) for dashboards/thresholds
@@ -686,8 +691,12 @@ node --test test/detectors-corpus.test.js
686
691
  - `test/detectors-corpus.test.js` — 33 malicious + 50 clean tarball integration (offline)
687
692
  - `test/fetch.test.js` — tarball extraction, temp directory cleanup
688
693
  - `test/policy-edge-cases.test.js` — edge cases in suppress, override, load validation
694
+ - `test/policy.test.js` — policy YAML/JSON load, apply, suppress, severity override tests
689
695
  - `test/report-snapshots.test.js` — HTML/text/CRA/PDF format assertions
696
+ - `test/report.test.js` — SARIF, CSV, STIG, risk score format tests
697
+ - `test/lockfile.test.js` — npm/yarn/pnpm parser, auto-detect, ATK-007/011 lockfile tests
690
698
  - `test/cli.test.js` — commander integration tests (help, version, scan, report, error handling)
699
+ - `test/cli-lockfile.test.js` — scan-lockfile CLI options, yarn/pnpm/monorepo/watch tests
691
700
 
692
701
  ### Need help?
693
702
 
@@ -78,6 +78,10 @@ export function validateLicense(key, feature = '*') {
78
78
 
79
79
  export function isFeatureEnabled(feature, licenseKey = process.env.NPM_SCAN_LICENSE_KEY) {
80
80
  try {
81
+ if (!licenseKey) {
82
+ const unlocked = feature === 'scan' || ALLOWED_UNLOCKED.includes(feature);
83
+ if (unlocked) return true;
84
+ }
81
85
  validateLicense(licenseKey, feature);
82
86
  return true;
83
87
  } catch {
@@ -126,10 +126,10 @@ function parseYarnLockfile(content, filePath) {
126
126
  if (bodyTrim.startsWith('version ')) {
127
127
  const vMatch = bodyTrim.match(/^version ['"]([^'"]+)['"]/);
128
128
  if (vMatch) version = vMatch[1];
129
- } else if (bodyTrim.startsWith('resolved ')) {
130
- const rMatch = bodyTrim.match(/^resolved ['"]([^'"]+)['"]/);
129
+ } else if (bodyTrim.match(/^\s*resolved\s+(.+)/)) {
130
+ const rMatch = bodyTrim.match(/^\s*resolved\s+(.+)/);
131
131
  if (rMatch) {
132
- resolved = rMatch[1];
132
+ resolved = rMatch[1].trim().replace(/^['"]|['"]$/g, '');
133
133
  if (resolved.startsWith('https://registry.yarnpkg.com/')) {
134
134
  resolved = resolved.replace('https://registry.yarnpkg.com/', 'https://registry.npmjs.org/');
135
135
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lateos/npm-scan",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
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": {
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "test-project",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "packages": {
6
+ "": {
7
+ "name": "test-project",
8
+ "version": "1.0.0",
9
+ "dependencies": {
10
+ "lodash": "^4.17.21",
11
+ "axios": "^1.6.0"
12
+ },
13
+ "devDependencies": {
14
+ "@babel/core": "^7.23.0"
15
+ }
16
+ },
17
+ "node_modules/lodash": {
18
+ "name": "lodash",
19
+ "version": "4.17.21",
20
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
21
+ "integrity": "sha512-v2kDEeDAnj4p1hhL6Ogrgu4BSWwg8cD2fRIouDAiqwu+iNl1IvyMex9jG9j8OpNp1zntnv/headququbit",
22
+ "dependencies": {}
23
+ },
24
+ "node_modules/axios": {
25
+ "name": "axios",
26
+ "version": "1.6.8",
27
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
28
+ "integrity": "sha512-j2xvyqwsdd456789abcdef",
29
+ "dependencies": {
30
+ "form-data": "4.0.0",
31
+ "proxy-from-env": "1.1.0"
32
+ }
33
+ },
34
+ "node_modules/axios/node_modules/form-data": {
35
+ "version": "4.0.0",
36
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
37
+ "integrity": "sha512-444567890123456"
38
+ },
39
+ "node_modules/@babel/core": {
40
+ "name": "@babel/core",
41
+ "version": "7.23.9",
42
+ "resolved": "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz",
43
+ "integrity": "sha512-5q+M1iEJCOrGJs9NxzG3p3z7w2cJK/QuoRoI2pOJhtcNQjl9y7w6w4At5ZQHZdwqd+5N5G1lULu7I6pXVBw==",
44
+ "dev": true,
45
+ "dependencies": {
46
+ "@babel/generator": "^7.23.6",
47
+ "@babel/parser": "^7.23.9"
48
+ }
49
+ },
50
+ "node_modules/reakt": {
51
+ "name": "reakt",
52
+ "version": "18.2.0",
53
+ "resolved": "https://registry.yarnpkg.com/reakt/-/reakt-18.2.0.tgz",
54
+ "integrity": "sha-abcdabcd1234defghi",
55
+ "optional": true,
56
+ "dependencies": {}
57
+ },
58
+ "node_modules/express": {
59
+ "name": "express",
60
+ "version": "4.18.2",
61
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
62
+ "integrity": "sha512-abcdabcd1234abcdefghi",
63
+ "dependencies": {
64
+ "accepts": "~1.3.8",
65
+ "body-parser": "1.20.2"
66
+ }
67
+ }
68
+ }
69
+ }
@@ -1,15 +1,15 @@
1
1
  lodash@^4.17.21:
2
2
  version "4.17.21"
3
- resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
3
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz"
4
4
  integrity sha512-Vythumb
5
5
  dependencies: {}
6
6
  dev false
7
- optional false
7
+ optional true
8
8
 
9
9
  axios@^1.6.0:
10
10
  version "1.6.8"
11
- resolved "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz"
12
- integrity sha512-j2xvyqwsdd456789abcdef
11
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz"
12
+ integrity sha-j2xvyqwsdd456789abcdef
13
13
  dependencies:
14
14
  form-data "4.0.0"
15
15
  proxy-from-env "1.1.0"
@@ -30,6 +30,7 @@ axios@^1.6.0:
30
30
  gensync "^1.0.0-beta.2"
31
31
  json5 "^2.2.3"
32
32
  semver "^6.3.1"
33
+ rimraf "^3.0.2"
33
34
  dev true
34
35
  optional false
35
36
 
@@ -45,7 +46,7 @@ axios@^1.6.0:
45
46
  dev false
46
47
  optional false
47
48
 
48
- reakt@^18.2.0, reakt@^18.2.0::version=18.2.0:
49
+ reakt@^18.2.0:
49
50
  version "18.2.0"
50
51
  resolved "https://registry.yarnpkg.com/reakt/-/reakt-18.2.0.tgz"
51
52
  integrity sha512-abcdabcd1234defghi
@@ -53,7 +54,7 @@ reakt@^18.2.0, reakt@^18.2.0::version=18.2.0:
53
54
  dev false
54
55
  optional true
55
56
 
56
- "express@npm:expres@^4.18.2", expres@^4.18.2::version=4.18.2:
57
+ express@npm:expres@^4.18.2:
57
58
  version "4.18.2"
58
59
  resolved "https://registry.npmjs.org/expres-4.18.2.tgz"
59
60
  integrity sha512-abcdabcd1234abcdefghi
@@ -92,7 +93,7 @@ reakt@^18.2.0, reakt@^18.2.0::version=18.2.0:
92
93
  dev false
93
94
  optional false
94
95
 
95
- "my-scope-plugin@npm:my-scope-plugin@^1.0.0", my-scope-plugin@^1.0.0::version=1.0.0:
96
+ "my-scope-plugin@npm:my-scope-plugin@^1.0.0":
96
97
  version "1.0.0"
97
98
  resolved "https://registry.npmjs.org/my-scope-plugin-1.0.0.tgz"
98
99
  integrity sha512-abcdefghijk123456789abcdef