From 54c87dee93e2ff9b94db4590092a3e81b8789116 Mon Sep 17 00:00:00 2001 From: Shubham Chauhan Date: Thu, 25 Jun 2026 11:29:46 +0530 Subject: [PATCH] feat: add NX workspace fixtures and documentation - Add examples/nx-integrated fixture for NX integrated repos - Add examples/nx-package-based fixture for NX package-based repos - Add examples/nx-mixed fixture demonstrating mixed scenario gap - Add website/docs/nx-workspaces.md with NX scanning guide - Update examples/readme.md with NX fixtures Scope: Fixtures and docs only. No code changes for mixed scenario handling (out of scope - will be handled separately using these fixtures for testing). --- examples/nx-integrated/package-lock.json | 13 +++ examples/nx-integrated/package.json | 9 ++ examples/nx-mixed/README.md | 13 +++ .../nx-mixed/apps/my-app/package-lock.json | 13 +++ examples/nx-mixed/apps/my-app/package.json | 8 ++ examples/nx-mixed/package-lock.json | 13 +++ examples/nx-mixed/package.json | 8 ++ .../apps/my-app/package-lock.json | 13 +++ .../nx-package-based/apps/my-app/package.json | 8 ++ .../libs/my-lib/package-lock.json | 13 +++ .../nx-package-based/libs/my-lib/package.json | 8 ++ examples/nx-package-based/package.json | 8 ++ examples/readme.md | 6 + website/docs/nx-workspaces.md | 110 ++++++++++++++++++ 14 files changed, 243 insertions(+) create mode 100644 examples/nx-integrated/package-lock.json create mode 100644 examples/nx-integrated/package.json create mode 100644 examples/nx-mixed/README.md create mode 100644 examples/nx-mixed/apps/my-app/package-lock.json create mode 100644 examples/nx-mixed/apps/my-app/package.json create mode 100644 examples/nx-mixed/package-lock.json create mode 100644 examples/nx-mixed/package.json create mode 100644 examples/nx-package-based/apps/my-app/package-lock.json create mode 100644 examples/nx-package-based/apps/my-app/package.json create mode 100644 examples/nx-package-based/libs/my-lib/package-lock.json create mode 100644 examples/nx-package-based/libs/my-lib/package.json create mode 100644 examples/nx-package-based/package.json create mode 100644 website/docs/nx-workspaces.md diff --git a/examples/nx-integrated/package-lock.json b/examples/nx-integrated/package-lock.json new file mode 100644 index 0000000..9d21f72 --- /dev/null +++ b/examples/nx-integrated/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "nx-integrated", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1il3Oy4gbwF2v7Sbm6I5XkqXSfYWTDq5kCCeXd4jG7u3CImPc9i/Eo+VZ7R1VvC5RRRvnoRxv9vQ+v82l4Fg==" + } + } +} diff --git a/examples/nx-integrated/package.json b/examples/nx-integrated/package.json new file mode 100644 index 0000000..c4e6d3b --- /dev/null +++ b/examples/nx-integrated/package.json @@ -0,0 +1,9 @@ +{ + "name": "nx-integrated", + "version": "1.0.0", + "private": true, + "devDependencies": { + "nx": "20.0.0", + "axios": "0.19.0" + } +} diff --git a/examples/nx-mixed/README.md b/examples/nx-mixed/README.md new file mode 100644 index 0000000..21e02c5 --- /dev/null +++ b/examples/nx-mixed/README.md @@ -0,0 +1,13 @@ +# NX Mixed Scenario Fixture + +This fixture demonstrates a mixed NX workspace scenario where both a root lockfile AND nested lockfiles exist (e.g., root dev tools + per-project dependencies). + +**Known gap:** Currently, CVE Lite CLI only scans the root lockfile in mixed scenarios. Nested lockfiles under `apps/` and `libs/` are not discovered when a root lockfile exists. + +**Workaround:** Run scans separately for each project: +```bash +cve-lite apps/my-app +cve-lite libs/my-lib +``` + +**Future fix:** We plan to add support for scanning both root and nested lockfiles in mixed scenarios. diff --git a/examples/nx-mixed/apps/my-app/package-lock.json b/examples/nx-mixed/apps/my-app/package-lock.json new file mode 100644 index 0000000..561c970 --- /dev/null +++ b/examples/nx-mixed/apps/my-app/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "my-app", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1il3Oy4gbwF2v7Sbm6I5XkqXSfYWTDq5kCCeXd4jG7u3CImPc9i/Eo+VZ7R1VvC5RRRvnoRxv9vQ+v82l4Fg==" + } + } +} diff --git a/examples/nx-mixed/apps/my-app/package.json b/examples/nx-mixed/apps/my-app/package.json new file mode 100644 index 0000000..c0c82c7 --- /dev/null +++ b/examples/nx-mixed/apps/my-app/package.json @@ -0,0 +1,8 @@ +{ + "name": "my-app", + "version": "1.0.0", + "private": true, + "dependencies": { + "axios": "0.19.0" + } +} diff --git a/examples/nx-mixed/package-lock.json b/examples/nx-mixed/package-lock.json new file mode 100644 index 0000000..fe93804 --- /dev/null +++ b/examples/nx-mixed/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "nx-mixed", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "nx": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/nx/-/nx-20.0.0.tgz", + "integrity": "sha512-test" + } + } +} diff --git a/examples/nx-mixed/package.json b/examples/nx-mixed/package.json new file mode 100644 index 0000000..11c8a5a --- /dev/null +++ b/examples/nx-mixed/package.json @@ -0,0 +1,8 @@ +{ + "name": "nx-mixed", + "version": "1.0.0", + "private": true, + "devDependencies": { + "nx": "20.0.0" + } +} diff --git a/examples/nx-package-based/apps/my-app/package-lock.json b/examples/nx-package-based/apps/my-app/package-lock.json new file mode 100644 index 0000000..561c970 --- /dev/null +++ b/examples/nx-package-based/apps/my-app/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "my-app", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1il3Oy4gbwF2v7Sbm6I5XkqXSfYWTDq5kCCeXd4jG7u3CImPc9i/Eo+VZ7R1VvC5RRRvnoRxv9vQ+v82l4Fg==" + } + } +} diff --git a/examples/nx-package-based/apps/my-app/package.json b/examples/nx-package-based/apps/my-app/package.json new file mode 100644 index 0000000..c0c82c7 --- /dev/null +++ b/examples/nx-package-based/apps/my-app/package.json @@ -0,0 +1,8 @@ +{ + "name": "my-app", + "version": "1.0.0", + "private": true, + "dependencies": { + "axios": "0.19.0" + } +} diff --git a/examples/nx-package-based/libs/my-lib/package-lock.json b/examples/nx-package-based/libs/my-lib/package-lock.json new file mode 100644 index 0000000..f976020 --- /dev/null +++ b/examples/nx-package-based/libs/my-lib/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "my-lib", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cP582QI9xL1H3vU45r1YdCjMt5QPBhA4hGfH0/IS7Vh7j+3iR+u93zbn+1Y8q1qReZhsC6sC/yjVhD++8AlpA==" + } + } +} diff --git a/examples/nx-package-based/libs/my-lib/package.json b/examples/nx-package-based/libs/my-lib/package.json new file mode 100644 index 0000000..a244bd1 --- /dev/null +++ b/examples/nx-package-based/libs/my-lib/package.json @@ -0,0 +1,8 @@ +{ + "name": "my-lib", + "version": "1.0.0", + "private": true, + "dependencies": { + "lodash": "4.17.11" + } +} diff --git a/examples/nx-package-based/package.json b/examples/nx-package-based/package.json new file mode 100644 index 0000000..5e99e03 --- /dev/null +++ b/examples/nx-package-based/package.json @@ -0,0 +1,8 @@ +{ + "name": "nx-package-based", + "version": "1.0.0", + "private": true, + "devDependencies": { + "nx": "20.0.0" + } +} diff --git a/examples/readme.md b/examples/readme.md index c95f01b..9601f43 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -24,6 +24,9 @@ Small curated projects committed to the repository. Clone the repo and scan imme | `bun-simple` | Bun | Minimal Bun lockfile with a direct and transitive vulnerability. | | `bun-within-range` | Bun | Transitive follow-redirects fix within axios range — suggests `bun update follow-redirects`. | | `bun-workspace` | Bun (workspace) | Bun workspace monorepo with workspace-scoped fix commands. | +| `nx-integrated` | npm | NX integrated monorepo with a single root lockfile. | +| `nx-package-based` | npm | NX package-based monorepo with per-project lockfiles under apps/ and libs/ directories. | +| `nx-mixed` | npm | NX mixed scenario with both root lockfile and nested lockfiles (demonstrates known gap). | | `pnpm-simple` | pnpm | Minimal pnpm v9 lockfile with a single direct vulnerability. | | `pnpm-dual-document` | pnpm | Dual-document pnpm v9 lockfile (bootstrap + project sections) - regression for #669. Uses intentionally vulnerable `lodash@4.17.20` for e2e scan coverage. | | `pnpm-within-range` | pnpm | Transitive `qs` via `body-parser` where the parent range already covers the fix — expects `pnpm update qs`, not a parent bump. | @@ -180,6 +183,9 @@ node dist/index.js examples/yarn-within-range --verbose node dist/index.js examples/bun-simple --verbose node dist/index.js examples/bun-within-range --verbose node dist/index.js examples/bun-workspace --verbose +node dist/index.js examples/nx-integrated --verbose +node dist/index.js examples/nx-package-based --verbose +node dist/index.js examples/nx-mixed --verbose node dist/index.js examples/pnpm-simple --verbose node dist/index.js examples/pnpm-dual-document --verbose node dist/index.js examples/pnpm-within-range --verbose diff --git a/website/docs/nx-workspaces.md b/website/docs/nx-workspaces.md new file mode 100644 index 0000000..a390254 --- /dev/null +++ b/website/docs/nx-workspaces.md @@ -0,0 +1,110 @@ +--- +sidebar_label: NX Workspaces +--- + +# NX Workspaces + +CVE Lite CLI supports both NX integrated and package-based workspace configurations. + +## NX Integrated Repos + +NX integrated repos use a single root lockfile (package-lock.json, pnpm-lock.yaml, or yarn.lock) with all dependencies managed at the root. CVE Lite CLI scans the root lockfile by default. + +**Scan command:** +```bash +cve-lite . +``` + +**Examples:** +- [Strapi](./case-studies/strapi.md) - Yarn Berry NX monorepo with 2,887 packages +- [Twenty](./case-studies/twenty.md) - Yarn Berry NX monorepo with 5,451 packages + +## NX Package-Based Repos + +NX package-based repos have individual lockfiles for each project under `apps/` and `libs/` directories. CVE Lite CLI automatically discovers and scans all nested lockfiles. + +**Scan command:** +```bash +cve-lite . --search-depth 4 +``` + +The default `--search-depth` of 4 is sufficient for most NX workspace structures. CVE Lite CLI will: +- Discover all lockfiles under `apps/` and `libs/` directories +- Scan each lockfile independently +- Label findings by workspace project (e.g., `apps/my-app/`, `libs/my-lib/`) +- Provide project-specific fix commands with correct working directories + +**Example output:** +``` +📁 apps/my-app/ +──────────────────────────────── +📦 Vulnerabilities found +──────────────────────────────── +HIGH axios@0.19.0 + Direct dependency + Fix: upgrade to 0.32.0 +──────────────────────────────── +🛠 Copy And Run These Fix Commands +──────────────────────────────── +> cd apps/my-app && npm install axios@0.32.0 + +📁 libs/my-lib/ +──────────────────────────────── +📦 Vulnerabilities found +──────────────────────────────── +CRITICAL lodash@4.17.11 + Direct dependency + Fix: upgrade to 4.18.0 +──────────────────────────────── +🛠 Copy And Run These Fix Commands +──────────────────────────────── +> cd libs/my-lib && npm install lodash@4.18.0 + +Scanned 2 folders: apps/my-app/, libs/my-lib/ +2 total findings across all folders +``` + +## Mixed Scenarios + +Some NX workspaces have both a root lockfile AND nested lockfiles (e.g., root dev tools + per-project dependencies). + +**Known gap:** Currently, CVE Lite CLI only scans the root lockfile in mixed scenarios. Nested lockfiles under `apps/` and `libs/` are not discovered when a root lockfile exists. + +**Workaround:** Run scans separately for each project: +```bash +cve-lite apps/my-app +cve-lite libs/my-lib +``` + +**Future fix:** We plan to add support for scanning both root and nested lockfiles in mixed scenarios. See the `examples/nx-mixed` fixture for a test case. + +## Deeply Nested Structures + +For unusually deep NX workspace structures (e.g., `apps/platform/frontend/app/`), increase the search depth: + +```bash +cve-lite . --search-depth 6 +``` + +## CI/CD Integration + +For CI/CD pipelines, use the standard workflow integration patterns: + +```yaml +- name: Scan for vulnerabilities + run: cve-lite . --fail-on high --json > results.json +``` + +See [Workflow Integration](./workflow-integration.md) for full CI/CD patterns. + +## Limitations + +- **--fix mode**: Not yet supported in multi-folder mode. Run `cve-lite .` from each subfolder individually for auto-fix. +- **--sarif and --cdx**: Not yet supported in multi-folder mode. +- **Mixed scenarios**: Root lockfile + nested lockfiles only scans the root lockfile (see above for workaround). +- **Workspace boundaries**: Monorepo workspace boundaries are only partially modeled in this version. Findings are labeled by folder path but not by NX project graph. + +## References + +- [NX Integrated vs Package-Based](https://nx.dev/concepts/integrated-vs-package-based) +- [CLI Reference](./cli-reference.md)