Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions docs/_data/siemQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,21 @@ export const siemQueries = {
wevtutilListsOnly: true,
sentinel: `Event
| where EventLog == "${CHANNEL}"
| where EventID == 200
| summarize Launches = count() by tostring(EventData.NodeVersion), Computer
| where EventID == 120
| where EventData.EventName == "nodejs.executed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Launches = count() by tostring(Payload.NodeVersion), Computer
| order by Launches desc`,
splunk: `index=wineventlog Channel="${CHANNEL}" EventCode=200
| stats count as Launches by EventData.NodeVersion, Computer
splunk: `index=wineventlog Channel="${CHANNEL}" EventCode=120 EventData.EventName="nodejs.executed"
| eval Payload=json_decode(EventData.Payload)
| stats count as Launches by Payload.NodeVersion, Computer
| sort -Launches`,
elastic: `FROM logs-*
| WHERE winlog.channel == "${CHANNEL}" AND winlog.event_id == 200
| STATS Launches = COUNT(*) BY version = winlog.event_data.NodeVersion, host = winlog.computer_name
| WHERE winlog.channel == "${CHANNEL}" AND winlog.event_id == 120 AND winlog.event_data.EventName == "nodejs.executed"
| EVAL Payload = TO_JSON(winlog.event_data.Payload)
| STATS Launches = COUNT(*) BY version = Payload.NodeVersion, host = winlog.computer_name
| SORT Launches DESC`,
wevtutil: `wevtutil qe "${CHANNEL}" /q:"*[System[(EventID=200)]]" /f:text /c:20`,
wevtutil: `wevtutil qe "${CHANNEL}" /q:"*[System[(EventID=120)] and EventData[Data[@Name='EventName']='nodejs.executed']]" /f:text /c:20`,
},
nodejsExecuted: {
wevtutilListsOnly: true,
Expand Down
2 changes: 1 addition & 1 deletion docs/cfg/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Use `nvm config docs --json` for machine-readable metadata.
| Option | Default | Values | Description |
|--------|---------|--------|-------------|
| `mode` | `shim` | `shim`, `link` | Specifies how Node.js commands and versions are managed, either through shim-based routing or direct junction/symlink linking. Same as `nvm use shim`/`nvm use link`. See [Operating Modes](../features/modes). |
| `root` | `%LOCALAPPDATA%\Author Software\nvm\installs` | Directory path | Root directory where Node.js versions are installed. Changing this does not move existing installs; migrate files yourself or reinstall versions. Windows, Program Files, profile folders, the NVM program folder, and parents of those paths are refused (**NVM4313**). |
| `root` | `%LOCALAPPDATA%\Author Software\nvm\installs` | Directory path | Root directory where Node.js versions are installed. Default path is writable by the installing user. Other users cannot write it (**NVM4305**). Set `InstallRoot` to a locked directory when versions must be immutable. Changing this does not move existing installs; migrate files yourself or reinstall versions. Windows, Program Files, profile folders, the NVM program folder, and parents of those paths are refused (**NVM4313**). |

## Downloads and mirrors

Expand Down
8 changes: 4 additions & 4 deletions docs/cfg/enterprise/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ See [Administrative Templates](ad) for GPO and Intune deployment. Download ADMX/
|**Disable version management**|`Enabled`|**Overridable** (`nvm on`/`nvm off`). Forces version management on or off. When off, NVM does not manage or redirect Node.js commands.<br /><br />- `0` = off (`nvm off`)<br />- `1` = on<br /><br />Default: `1`<br /><br />`REG_DWORD`|
|**Enforce permission model**|`EnforcePermissionModel`|**Overridable** (`enforce_permission_model`). Node shim prepends Node permission-model flag on `node` launches (shim-only). When `1`, the package-manager proxy also merges the same flag and `DefaultPermissionGrants` into `npm_config_node_options` for npm/npx/pnpm lifecycle scripts. npm-cli.js itself is not launched under `--permission`. Yarn, bun, corepack, and vlt are not covered. Does not set `NODE_OPTIONS`. Default-deny FS/network unless the process passes `--allow-*` at runtime. NVM does not inject `--allow-*` grants except `DefaultPermissionGrants`. NVM never injects `--permission-audit` (Node 25.8+ report-only). There is no policy that forces `--permission-audit`.<br /><br />- Node 23+: `--permission`<br />- Node 20–22: `--experimental-permission`<br />- Node &lt;20: no flag (unsupported)<br /><br />- HKLM Policies `1`: strip caller `--permission-audit` (and `--permission-audit` already in `npm_config_node_options`) and inject `--permission` / `--experimental-permission`. Filter `--allow-*` even if the caller already passed `--permission`.<br />- Preference-only `1`: caller `--permission-audit` is a debug exception for `node` (no inject). Proxy still merges `--permission` into `npm_config_node_options` without stripping existing tokens.<br /><br />- `0` = off (no env write)<br />- `1` = on<br /><br />Default: `0`<br /><br />`REG_DWORD`|
|**Freeze V8 global objects**|`FreezeV8GlobalObjects`|**Overridable** (`freeze_v8_global_objects`). Node shim prepends `--frozen-intrinsics` so built-in prototypes cannot be patched (shim-only; Node.js 12+). When `1`, the package-manager proxy also merges this flag into `npm_config_node_options` for npm/npx/pnpm lifecycle scripts. Yarn, bun, corepack, and vlt are not covered. Does not set `NODE_OPTIONS`. Adds measurable startup cost.<br /><br />- `0` = off (no env write)<br />- `1` = on<br /><br />Default: `0`<br /><br />`REG_DWORD`|
|**Install root**|`InstallRoot`|**Overridable** (`root`). Directory where Node.js versions are stored. Overrides machine and user preferences.<br /><br />Default: `%LOCALAPPDATA%\Author Software\nvm\installs`<br /><br />`REG_SZ`|
|**Install root**|`InstallRoot`|**Overridable** (`root`). Directory where Node.js versions are stored. Overrides machine and user preferences.<br /><br />Default: `%LOCALAPPDATA%\Author Software\nvm\installs`. That directory is writable by the installing user. Other users cannot write it (**NVM4305**). Set this key to a locked directory when versions must be immutable (Governance).<br /><br />`REG_SZ`|
|**Local install source**|`LocalInstallDir`|**Overridable** (`local_dir`). Alternate local directory for Node.js archives (air-gapped mirrors). Overrides cache.<br /><br />`REG_SZ`|
|**Local install only**|`LocalInstallOnly`|**Overridable** (`local_install_only`). Restrict installs to `LocalInstallDir` only.<br /><br />- `0` = off<br />- `1` = on<br /><br />Default: `0`<br /><br />`REG_DWORD`|
|**Audit logging**|`LogExecutions`|**Overridable** (`log_executions`). Log every Node.js invocation to the Windows Event Log (shim-only). Most useful on **Audit+** (structured ETW).<br /><br />- `0` = off<br />- `1` = on<br /><br />Default: `0`<br /><br />`REG_DWORD`|
Expand All @@ -77,9 +77,9 @@ These keys are part of the **Governance** feature set. They appear in the Govern
|**Verbose mirror license metadata**|`ApplyVerboseLicenseMetadata`|When on, Author mirror license JWTs include identity claims (`idp_username`, `idp_machine_name`, `idp_machine_id`). Does not set `AccessToken`/`AccessKey`.<br /><br />- `0` = omit claims<br />- `1` = include claims<br /><br />Default: `0`<br /><br />`REG_DWORD`|
|**Verbose firewall policy metadata**|`ApplyVerboseFirewallMetadata`|When on, HTTPS firewall policy JWTs include verbose identity claims (`idp_*`) plus npm/pnpm/yarn profile/config when available.<br /><br />- `0` = omit extra claims<br />- `1` = include claims<br /><br />Default: `0`<br /><br />`REG_DWORD`|
|**Skip lockfile for module firewall**|`FirewallSkipLockfile`|When on, module firewall uses `package.json` only (ignores lockfiles for local matching and HTTPS POST bodies).<br /><br />- `0` = use lockfile when present (default)<br />- `1` = skip lockfile<br /><br />`REG_DWORD`|
|**npm module minimum age**|`NpmModuleMinimumAge`|Minimum package publish age (cooldown), in minutes, for package manager installs (shim mode). Auto-converts for npm/pnpm/yarn.<br /><br />Default: Community `0` (disabled). Certified Builds `2880` (2 days) when the key is unset. `nvm config` and CLI `--min-release-age` still override the Certified default. When HKLM Policies sets this key, the value is forced and the shim strips `--min-release-age`, `--minimum-release-age`, `--config.min-release-age`, `--no-time-gate`, and `--bypass-age-policy` (arguments after `--` stay). DWORD `0` forces the gate off.<br /><br />`REG_DWORD`|
|**Allowed Node.js versions**|`VersionAllowList`|Allow list for `nvm install`, `nvm use`, and the Certified shim. Community ACL is a no-op at install and at use/shim. Invalid entries fail enforcement. Allow wins over block. Also feeds Author-mirror JWT version claims (magic tokens such as `EOL`, `ALPHA`, `MAINTENANCE`, `ALL`).<br /><br />Supports exact semver, wildcards (e.g. `20.x`), aliases, and `NOT`/`!` negation (one rule per line).<br /><br />`REG_SZ`|
|**Blocked Node.js versions**|`VersionBlockList`|Block list for `nvm install`, `nvm use`, and the Certified shim. Same rule formats as `VersionAllowList`.<br /><br />`REG_SZ`|
|**npm module minimum age**|`NpmModuleMinimumAge`|Minimum package publish age (cooldown), in minutes, for package manager installs (shim mode). Auto-converts for npm/npx/pnpm/yarn. bun/bunx/corepack/vlt are not covered.<br /><br />Default: Community `0` (disabled) — same as stock npm/npx with no version manager. Certified Builds `2880` (2 days) when the key is unset, including npx. `nvm config` and CLI `--min-release-age` still override the Certified default. When HKLM Policies sets this key, the value is forced and the shim strips `--min-release-age`, `--minimum-release-age`, `--config.min-release-age`, `--no-time-gate`, and `--bypass-age-policy` (arguments after `--` stay). DWORD `0` forces the gate off.<br /><br />`REG_DWORD`|
|**Allowed Node.js versions**|`VersionAllowList`|Allow list for `nvm install`, `nvm use`, and the Certified shim. Community ACL is a no-op at install and at use/shim. Invalid entries fail enforcement. Allow wins over block when both lists come from the same source. The Certified shim loads allow and block as a pair from the first source that has either key (HKLM Policies, then remaining sources). A user HKCU allow list cannot override an HKLM Policies block list. CLI `nvm install` / `nvm use` on Certified Builds read **HKLM Policies only**. Unmanaged Certified still honors `nvm config` lists at shim launch when no GPO lists exist. Also feeds Author-mirror JWT version claims (magic tokens such as `EOL`, `ALPHA`, `MAINTENANCE`, `ALL`). Those magic tokens are ignored locally at install, use, and shim launch.<br /><br />Supports exact semver, wildcards (e.g. `20.x`), aliases, and `NOT`/`!` negation (one rule per line).<br /><br />`REG_SZ`|
|**Blocked Node.js versions**|`VersionBlockList`|Block list for `nvm install`, `nvm use`, and the Certified shim. Same rule formats and same-source pairing as `VersionAllowList`.<br /><br />`REG_SZ`|

:::info[License secrets]
`AccessToken`, `AccessKey`, and `JwksCose` are **not** ADMX policies. Deploy with portal scripts (`Set-NvmWindowsAccessToken.ps1` on stock certified build; `Set-NvmWindowsLicensing.ps1` for governance builds, which also sets `AccessKey` for Author mirrors) or `nvm license`.
Expand Down
2 changes: 1 addition & 1 deletion docs/command/package-manager-shims.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Global CLIs installed with those managers (for example `tsc`) use the same `prox
4. For `bun` / `bunx`, find the installed bun binary, run malware checks on package-manager commands, spawn bun, and stop. For other names, resolve Node.js (preference / detection / `--nvm-use`) and optionally auto-install.
5. Check the version directory is safe (not a reparse point, not cross-user writable) — failure → **NVM4305**.
6. Locate the delegated command under the Node install (`.exe`, then `.cmd`, then `.bat`).
7. Enforce package-manager constraints when policy applies (npm / npx / pnpm / yarn). HKLM `BlockPackageManagerLifecycleScripts` forces `--ignore-scripts` or `YARN_ENABLE_SCRIPTS=false`. Certified Builds with Advanced Logging write **NVM4412** for install-class commands only.
7. Enforce package-manager constraints when policy applies (npm / npx / pnpm / yarn). `NpmModuleMinimumAge` maps to each manager's native cooldown, including npx (`npm_config_min_release_age`). Community default is off (`0`). Certified default is 2880 minutes (2 days) when the key is unset. HKLM `BlockPackageManagerLifecycleScripts` forces `--ignore-scripts` or `YARN_ENABLE_SCRIPTS=false`. Certified Builds with Advanced Logging write **NVM4412** for install-class commands only.
8. When `EnforcePermissionModel`, `FreezeV8GlobalObjects`, or `DisableEvalAndStringExecution` is on, merge those Node flags into `npm_config_node_options` for npm/npx/pnpm so lifecycle scripts inherit them. Default off: no env write. Does not set `NODE_OPTIONS`. Yarn, bun, corepack, and vlt are skipped. npm-cli.js itself is not launched under `--permission`.
9. Verify trust of `node.exe` and the delegated target (below).
10. Spawn the real tool (for npm/npx/corepack: often `node.exe` + `*-cli.js`; otherwise the `.exe` / `.cmd`).
Expand Down
4 changes: 2 additions & 2 deletions docs/features/author-mirror.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ There are two (combinable) ways to enforce dynamic policies on the Author mirror
| Policy entry | Local client | Author mirror (`mirror.author.io`) | Public mirror (`nodejs.org`) |
|:-|:-|:-|:-|
| `16.x` on block list | Denied before download | Not reached | Not reached |
| `EOL` on block list | Allowed locally (alias ignored) | Denied if version is end-of-life | No Author JWT — not enforced |
| `EOL` on block list | Allowed locally (alias ignored at install, `nvm use`, and shim launch) | Denied if version is end-of-life | No Author JWT — not enforced |
| Allow `20.x` only (no magic) | Non-20 denied locally | — | — |


Expand All @@ -80,7 +80,7 @@ VersionAllowList=20.x

What this does:

1. **Locally:** `20.x` / `22.x` match the allow list. `EOL` is not expanded, so an end-of-life release is **not** stopped on the client by that alias alone. Because a dynamic alias is present, the allow list is also **not** treated as exclusive: other versions can still pass the local check.
1. **Locally:** `20.x` / `22.x` match the allow list. `EOL` is not expanded, so an end-of-life release is **not** stopped on the client by that alias alone — including `nvm use` and Certified shim launch of an already-installed copy. Because a dynamic alias is present, the allow list is also **not** treated as exclusive: other versions can still pass the local check.
1. **On `mirror.author.io`:** the client sends a license claim derived from these lists (including `NOT EOL`). The mirror rejects end-of-life archives.
1. **On `nodejs.org` (or any non-Author mirror):** no Author JWT — `EOL` does nothing. Put Author first in [`node_mirror`](../cfg/core#downloads-and-mirrors), or add static blocks (for example `16.x`) if you must enforce without the Author mirror.

Expand Down
2 changes: 1 addition & 1 deletion docs/features/modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Shim mode features:
- Lockdown Node.js/V8 permissions. (Optional) _Requires Certified Builds with the Governance add-on._
- Publisher trust: Verifies node.exe publisher to prevent untrusted node.exe swaps.
- Native event logging.
- Unified/configurable cooldown periods across all major package managers (npm/yarn/pnpm). _Requires the Governance add-on, available September 2026._
- Unified/configurable cooldown periods across all major package managers (npm/npx/yarn/pnpm). _Requires the Governance add-on, available September 2026._

:::tip[Certified Builds]
Shim **V8 / permission lockdown** (`EnforcePermissionModel`, `FreezeV8GlobalObjects`, `DisableEvalAndStringExecution`) and package-manager **cooldown** are Governance features. See [registry policy](../cfg/registry) and [Choosing an Edition](../guide/builds/).
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/builds/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Additional capabilties are available as add-ons:
|:-|:-|
|**Advanced&nbsp;Logging**|Structured logging for SIEM integration.|
|**Trust&nbsp;Artifacts**|SBOM, provenance, and VEX reports.|
|**Governance**|Central policy management (ADMX/ADML + GPO/Entra scripts), advanced proxy support (IWA, WPAD/PAC), Node.js mirror (mirror.author.io), version firewall, shim-enforced Node.js permissions, unified package manager cooldown (npm/pnpm/yarn), and air-gap/local installations.|
|**Governance**|Central policy management (ADMX/ADML + GPO/Entra scripts), advanced proxy support (IWA, WPAD/PAC), Node.js mirror (mirror.author.io), version firewall, shim-enforced Node.js permissions, unified package manager cooldown (npm/npx/pnpm/yarn), and air-gap/local installations.|

### Code Signing Authority

Expand Down Expand Up @@ -70,7 +70,7 @@ The Governance package for certified builds introduce policy management. With th

- Utilize corporate proxies (IWA, PAC/WPAD).
- Block unapproved Node.js Versions (i.e. EOL, vulnerable, or unstable versions).
- Require custom npm/yarn/pnpm cooldown periods.
- Require custom npm/npx/yarn/pnpm cooldown periods.
- Enforce Node.js permission model (`--permission` / `--experimental-permission`) so shimmed `node` starts default-deny; npm/npx/pnpm lifecycle scripts inherit the same flags via `npm_config_node_options` when the policy is on. Apps must pass `--allow-*` at runtime.
- Freeze V8 intrinsics (`--frozen-intrinsics`) to prevent prototype pollution of built-ins (also applied to npm/npx/pnpm scripts when the policy is on).
- Disallow `eval()` / `new Function()` (`--disallow-code-generation-from-strings`) (also applied to npm/npx/pnpm scripts when the policy is on).
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ sequenceDiagram

## 3. Package managers and modules

Through `proxy.exe`, NVM can apply **Governance** package-manager controls such as [`NpmModuleMinimumAge`](../cfg/registry#governance-keys) (cooldown / minimum publish age for npm, pnpm, and yarn). Community defaults to off. Certified Builds default to 2 days (2880 minutes) when the key is unset. HKLM Policies forces the value and strips CLI age-override flags.
Through `proxy.exe`, NVM can apply **Governance** package-manager controls such as [`NpmModuleMinimumAge`](../cfg/registry#governance-keys) (cooldown / minimum publish age for npm, npx, pnpm, and yarn). Community defaults to off (`0`), matching stock npm/npx with no version manager. Certified Builds default to 2 days (2880 minutes) when the key is unset, including npx. HKLM Policies forces the value and strips CLI age-override flags.

Certified Builds also enforce the **[NVM Firewall](firewall)** module allow lists (`ApprovedModules` / `ApprovedGlobalModules`) on shimmed installs, and both editions support the **trust firewall** (`TrustedModules`) so approved self-updating global CLIs can auto-reshim after they rewrite their entrypoints.

Expand Down
11 changes: 6 additions & 5 deletions docs/guide/siem.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ There is a full list of [Error Codes](../troubleshooting/error-codes) (and expla
| Structured info, warning, or error | Event ID **120**, **121**, or **122** |
| An NVM code | `Code` (number only) |
| A named lifecycle or audit event | `EventName`, then parse `Payload` as JSON |
| Each `node.exe` launch | Event ID **120** `nodejs.executed` when `log_executions` is on. Event **200** unused. |

|Field|Detail|
|:-|:-|
Expand All @@ -65,12 +66,12 @@ Collectors flatten EventData in different ways. Use the names your platform crea

### Node.js versions in use

Event ID **200** records each shim launch when `log_executions` is on.
This Event ID does not need Advanced Logging.
Event ID **120** `nodejs.executed` records each shim launch when `log_executions` is on.
`Payload.NodeVersion` is the version. Event ID **200** is unused.

<SiemQuery id="versionsInUse" />

With Advanced Logging, the structured form is `nodejs.executed` on Event ID **120**.
Host count for the same event:

<SiemQuery id="nodejsExecuted" />

Expand Down Expand Up @@ -143,12 +144,12 @@ It is not the NVM number.
| 120 | structured info | `Source`, `EventName`, `Payload`, `Code` |
| 121 | structured warning | `Source`, `EventName`, `Payload`, `Code` |
| 122 | structured error | `Source`, `EventName`, `Payload`, `Code` |
| 200 | shim execution | `RequestedCommand`, `ResolvedPath`, `NodeVersion`, `Arguments`, `WorkingDirectory` |
| 200 | unused | Manifest leftover. Launch logging is Event ID **120** `nodejs.executed`. |
| 210 | shim info | `Source`, `Message`, `Code` |
| 211 | shim warning | `Source`, `Message`, `Code` |
| 212 | shim error | `Source`, `Message`, `Code` |

Event ID **200** has no `Code` field.
Event ID **200** is unused. Launch logging uses Event ID **120**.

Security events from the shims include `user`, `sid`, `hostname`, `parent_process`, `parent_pid`, `project_name`, and `project_path` when the process can resolve them.
An empty value is `unknown`.
Expand Down
Loading
Loading