Skip to main content
Certified BuildRequires the advanced logging package.

SIEM

Security Information and Event Management (SIEM) systems collect logs from many devices into one store.This store can be queried for incidents, policy gaps, and inventory.

NVM for Windows Advanced Logging writes Node.js activity in an SIEM-friendly format, in a dedicated NVM for Windows/Operational channel. Send that channel to your SIEM to see which Node.js versions run in the organization, who installed or removed them, when a firewall or malware rule blocked a package, and much more.

Structured Logs

Advanced logging produces structured logs with metadata fields. These are explicitly designed for querying.

Without this, SIEM collectors would require custom parsers against plaintext messages, with no syntax guarantees. The plaintext logging available without advanced logging is designed for individual user readability, not SIEM or analytics. Plaintext logs will change over time with community feedback. Advanced Logging exists to provide a reliable log source for critical auditing capabilities.

This guide shows how to collect the channel, then gives starter queries.

Setup the collector​

ItemValue
ChannelNVM for Windows/Operational
ProviderNVM for Windows
Provider GUID{4c0f8d8e-2d6b-4f93-9f0f-3f0d7b4e2d11}

Point your collector at the NVM for Windows/Operational channel. Do not collect only the Windows Application log. Application warnings, such as NVM4101, are a separate stream.

Most EventName / Payload events need the Advanced Logging add-on. Without that license, classic events still include Code. They do not include EventName or Payload.

Exception: nodejs.executed (Event ID 120) is written whenever log_executions is on. Community Builds and Certified Builds. No Advanced Logging license. Certified Builds also write package_manager.executed the same way.

How to query​

There is a full list of Error Codes (and explanations) for use in SIEM pipelines. When querying, drop the NVM prefix. For example, NVM4312 is EventData.Code == 4312.

You wantFilter
Structured info, warning, or errorEvent ID 120, 121, or 122
An NVM codeCode (number only)
A named lifecycle or audit eventEventName, then parse Payload as JSON
Each node.exe launchEvent ID 120 nodejs.executed when log_executions is on. Event 200 unused.
FieldDetail
PayloadJSON string - Do not look for the NVM number inside this field.
CodeSeparate UInt32 - Code is 0 when the event is not an NVM#### condition.

Collectors flatten EventData in different ways. Use the names your platform created if a sample field is missing.

Microsoft SentinelSplunkElasticEvent Viewer
ChannelEventLogChannelwinlog.channellog name
Event IDEventIDEventCodewinlog.event_idEventID
Event nameEventData.EventNameEventData.EventNamewinlog.event_data.EventNameData[@Name='EventName']
PayloadEventData.PayloadEventData.Payloadwinlog.event_data.PayloadData[@Name='Payload']
CodeEventData.CodeEventData.Codewinlog.event_data.CodeData[@Name='Code']
ComputerComputerComputerwinlog.computer_name—

Example Queries​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4312

Node.js versions in use​

Event ID 120 nodejs.executed records each shim launch when log_executions is on. Payload.NodeVersion is the version. Event ID 200 is unused.

Event
| where EventLog == "NVM for Windows/Operational"
| 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

Host count for the same event:

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nodejs.executed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Launches = dcount(Computer) by tostring(EventData.EventName)

Who installed a Node.js version​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.system.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| where Payload.Action == "install"
| where Payload.Outcome == "Succeeded"
| summarize Installs = count() by tostring(Payload.NodeVersion), tostring(Payload.User), tostring(Payload.hostname)

Who installed Node.js 20​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| extend Payload = parse_json(tostring(EventData.Payload))
| where EventData.EventName == "nvm.system.changed"
| where Payload.Action == "install"
| where Payload.Outcome == "Succeeded"
| where tostring(Payload.NodeVersion) startswith "20."
| summarize People = dcount(tostring(Payload.User))

Change 20. to another major version, or to an EOL line your policy tracks. The event does not mark EOL for you.

Who uninstalled a Node.js version​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.system.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| where Payload.Action == "uninstall"
| where Payload.Outcome == "Succeeded"
| summarize Uninstalls = count() by tostring(Payload.NodeVersion), tostring(Payload.User), tostring(Payload.hostname)

Which version became the default​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.version.activated"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Activations = count() by tostring(Payload.version), tostring(Payload.user), tostring(Payload.hostname)

Package-manager installs allowed or blocked​

Certified Builds write package_manager.install when the module firewall allows or denies an install. NVM4408 is an allow. NVM4403 is a deny. A malware block does not write this event. Use NVM4602 instead.

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID in (120, 122)
| where EventData.EventName == "package_manager.install"
| summarize Events = count() by Computer, EventData.Code

Malware blocks​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.Code == 4602
| where EventData.EventName == "malware.package_blocked"
| summarize Blocks = count() by Computer

Inspect Payload for the package name, version when known, command, source, and reason.

Integrity verification failures​

Authenticode / trust failures are NVM4301. A swapped node.exe whose FileVersion does not match the folder is NVM4314.

Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code in (4301, 4314)
| summarize Failures = count() by Computer, EventID, EventData.EventName, EventData.Code

Version firewall blocks​

Governance VersionAllowList / VersionBlockList denies are NVM4411.

Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4411
| where EventData.EventName == "node.security.version_firewall_blocked"
| summarize Blocks = count() by Computer

Lifecycle scripts blocked​

HKLM BlockPackageManagerLifecycleScripts writes NVM4412 (package_manager.lifecycle_scripts_blocked) on Certified Builds with Advanced Logging when the proxy rewrites an install-class command (install, ci, add, update, rebuild, and similar; npx with a package; bare yarn). npm --version and npm run do not write it. The lock still applies to those commands. This is audit, not a failed install. Community Builds do not write it. Inspect Payload.command, Payload.verb, and Payload.action.

Event
| where EventLog == "NVM for Windows/Operational"
| where EventData.Code == 4412
| where EventData.EventName == "package_manager.lifecycle_scripts_blocked"
| summarize Locks = count() by Computer, tostring(EventData.EventName)

Configuration changes​

Event
| where EventLog == "NVM for Windows/Operational"
| where EventID == 120
| where EventData.EventName == "nvm.configuration.changed"
| extend Payload = parse_json(tostring(EventData.Payload))
| summarize Changes = count() by tostring(Payload.Configuration), tostring(Payload.User), tostring(Payload.hostname)

Event ID templates​

The Windows Event ID is the template. It is not the NVM number.

Event IDWriterFields
100nvm.exe infoSource, Message, Code
101nvm.exe warningSource, Message, Code
102nvm.exe errorSource, Message, Code
120structured infoSource, EventName, Payload, Code
121structured warningSource, EventName, Payload, Code
122structured errorSource, EventName, Payload, Code
200unusedManifest leftover. Launch logging is Event ID 120 nodejs.executed.
210shim infoSource, Message, Code
211shim warningSource, Message, Code
212shim errorSource, Message, Code

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.

Event names with Code 0​

Query these by EventName.

EventNameWhat it records
nvm.system.changedInstall, uninstall, and similar lifecycle results. Payload: Action, NodeVersion, Outcome, User, sid, hostname, correlation_id.
nvm.configuration.changedA setting change. Payload: Configuration, Value, Old, Outcome.
nvm.version.activatedThe active Node.js version changed. Payload: version, previous_version, user, hostname.
nodejs.executedA node launch when LogExecutions is 1. Source node-shim. When permission allow flags were supplied, applied, or removed, the payload includes permission_allow.supplied, permission_allow.applied, and permission_allow.removed.
package_manager.executedAn npm, yarn, pnpm, or similar launch on a Certified Build when LogExecutions is 1. Source proxy.

Event names that carry an NVM code​

Match Code to the number in Error Codes.

EventNameCodeSource
verify_key.open_failed4312nvm.exe on a Certified Build. A Community Build writes Event ID 102 with the same code.
node.security.verification_failed4301node-shim or proxy
node.security.activation_blocked4302 or 4305nvm.exe uses 4302. The proxy uses 4305.
node.security.cache_state_changed4303node-shim or proxy
node.security.full_verification_recovered4304node-shim or proxy
node.security.version_mismatch4314node-shim or proxy. PE FileVersion does not match the install folder.
node.security.version_firewall_blocked4411node-shim or proxy. Governance VersionAllowList / VersionBlockList denied the version.
package_manager.lifecycle_scripts_blocked4412proxy. Advanced Logging only. HKLM lifecycle-script lock on an install-class command.
network.deadline_exceeded4501nvm.exe or sync.exe
malware.definitions_updated4601sync.exe. Advanced Logging only.
malware.package_blocked4602Shim. Advanced Logging only.
firewall.elevation_required4404nvm.exe
firewall.invalid_rule4405nvm.exe
firewall.untrusted_module_changed4406nvm.exe or proxy
firewall.policy_mutated4410nvm.exe
firewall.policy_mutate_failed4410nvm.exe
package_manager.install4403 or 4408proxy on a Certified Build. 4403 is a deny. 4408 is an allow.