You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit eb4e2b0
Browse filesBrowse the repository at this point in the historyBrowse files
`node2shell-check` compares the Node.js version a runtime or website reports with the official upstream fix for each supported branch. Assess a version you already know, collect metadata inside a deployed service, or inspect a website's ordinary public response. It sends **no exploit payloads**, never decodes Brotli, and reports uncertainty honestly: a hidden, vendor-built, or conflicting version is `unknown`, never "safe".
21
+
`node2shell-check` compares the Node.js version a runtime or website reports with the official upstream fix for each supported branch. You can assess a version you already know, collect metadata inside a deployed service, or inspect a website's ordinary public response.
22
+
23
+
It sends **no exploit payloads**, never decodes Brotli, and reports uncertainty honestly: a hidden, vendor-built, or conflicting version is `unknown`, never "safe".
24
+
25
+
> [!IMPORTANT]
26
+
> **Potential exposure is not confirmed exploitability.** Every result keeps exploitability `not_tested`. The vulnerability involves decoding attacker-controlled Brotli data, and a version alone cannot show whether an application reaches that code path. `fixed_version_reported` is not verification of a deployed patch. Treat every result as a prompt for an administrator to check.
27
+
28
+
Check only targets you are authorized to assess, and keep real target lists, inventories, and reports out of version control.
29
+
30
+
## Contents
31
+
32
+
-[Requirements](#requirements)
33
+
-[Quick start](#quick-start)
34
+
-[Input modes](#input-modes)
35
+
-[Understanding results](#understanding-results)
36
+
-[Reports and output](#reports-and-output)
37
+
-[Exit codes](#exit-codes)
38
+
-[Scope and limitations](#scope-and-limitations)
39
+
-[Optional tooling](#optional-tooling)
40
+
-[Documentation](#documentation)
41
+
-[Development and contributing](#development-and-contributing)
42
+
-[License](#license)
43
+
44
+
## Requirements
45
+
46
+
- Python 3.10 or later on Windows, macOS, or Linux. There is nothing to install; use `python3` where `python` is not Python 3.
47
+
- Node.js only if you run the optional collector, `collect-runtime.cjs`.
python node2shell_check.py --node-version 24.22.0 # offline, no network calls
28
-
node collect-runtime.cjs > runtime.json # run inside the deployed service
55
+
# Compare a known version. Offline, no network calls.
56
+
python node2shell_check.py --node-version 24.22.0
57
+
58
+
# Collect metadata where the service runs (same Node.js binary), then assess it offline.
59
+
node collect-runtime.cjs > runtime.json
29
60
python node2shell_check.py --inventory runtime.json --format html --output report.html
61
+
62
+
# Check authorized websites listed one per line (start from websites.example.txt).
63
+
cp websites.example.txt websites.txt # then replace the entries with your own authorized targets
30
64
python node2shell_check.py --input websites.txt --format html --output websites-report.html
31
65
```
32
66
33
-
Python 3.10+ on Windows, macOS, or Linux. Nothing to install; use `python3` where `python` is not Python 3. Node.js is needed only to run the optional collector.
67
+
With no `--format`, results are written as CSV to stdout. Run `python node2shell_check.py --help` for the complete option list, or see the [full reference](docs/REFERENCE.md).
68
+
69
+
## Input modes
34
70
35
-
## The report
71
+
All three modes can be combined in one run. Each input becomes its own observation with its own evidence; a supplied runtime version is never assumed to belong to a supplied URL.
36
72
37
-
`--format html` produces a single offline, script-free file with a logo, summary tiles, every result with its evidence and next step, and the upstream fix table. It follows the reader's light or dark theme and prints cleanly.
73
+
| Mode | Option | Network | What it does |
74
+
| --- | --- | --- | --- |
75
+
| Known version |`--node-version VERSION` (repeatable) | None | Compares a version you obtained elsewhere. |
76
+
| Runtime inventory |`--inventory FILE` (repeatable) | None | Reads collector-compatible JSON (schema `1`) produced by `collect-runtime.cjs`. |
77
+
| Website metadata | URLs or domains as arguments, or `--input FILE`| Yes | Reads public response metadata from each supplied target. |
78
+
79
+
**Runtime inventory.**`collect-runtime.cjs` records version and build metadata from the Node.js binary that runs it. It never loads application code, reads environment variables, decompresses data, or opens a connection. It cannot attest vendor patch provenance, and its `build_source` defaults to `unknown`. Run it in the actual container or runtime you intend to assess; a host installation does not describe every container, function, or worker.
80
+
81
+
**Website metadata.** For each target the checker reads `robots.txt` and, if policy allows, makes an ordinary `GET` request to the supplied page. It uses verified TLS, an honest User-Agent, bounded responses, and request pacing. Redirects stay on the same host or its `www` counterpart, and HTTPS downgrades stop. It fetches nothing else and sends no compressed bodies, credentials, or crafted requests. Express or Next.js hints never become Node.js version evidence, and Brotli response compression does not prove that an application decodes request bodies. See [Requests made in website mode](docs/REFERENCE.md#requests-made-in-website-mode) for the full rules.
82
+
83
+
## Understanding results
84
+
85
+
Each observation receives one of four assessment statuses.
86
+
87
+
| Status | Meaning |
88
+
| --- | --- |
89
+
|`potentially_affected`| A plain Node.js version below its branch's fix is reported. Verify the deployed build and prioritize remediation. |
90
+
|`unsupported_runtime`| The version is on an older, unsupported upstream line. Verify vendor support and move to a supported, patched runtime. |
91
+
|`fixed_version_reported`| The reported version meets the fix. This is not independent verification of installed patches. |
92
+
|`unknown`| The version is missing, malformed, conflicting, vendor-qualified, custom-built, pre-release, possibly backported, or outside this rule snapshot. |
93
+
94
+
Upstream fixes in the embedded rule snapshot:
95
+
96
+
| Node.js branch | 22.x | 24.x | 26.x |
97
+
| --- | --- | --- | --- |
98
+
|**Fixed in**| 22.23.4 | 24.22.1 | 26.12.1 |
99
+
100
+
Thresholds come from the [Node.js October 2026 security releases](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) and the release notes for [22.23.4](https://nodejs.org/en/blog/release/v22.23.4), [24.22.1](https://nodejs.org/en/blog/release/v24.22.1), and [26.12.1](https://nodejs.org/en/blog/release/v26.12.1), released 2026-10-21. The embedded rule snapshot is dated **2026-10-23**. The checker does not download or refresh rules, so rerunning an old copy does not update them.
101
+
102
+
Keep these distinctions in mind when reading a report:
103
+
104
+
-**Unknown is not an error.** A successful request may legitimately reveal no runtime version. Conversely, a `fixed_version_reported` observation elsewhere in a report does not erase an unsuccessful website check. Inspect each result's fetch outcome and the run's completeness as well as its assessment.
105
+
-**Reported is not verified.** A version copied from a Dockerfile, deployment label, or public header may differ from the process serving traffic. Headers can be stale, spoofed, or proxy-generated.
106
+
-**Vendor builds stay uncertain.** Vendor-qualified, custom-built, and pre-release versions remain `unknown`. Brotli metadata from the collector is context, not a patch verdict.
107
+
108
+
The [assessment methodology](docs/NODE2SHELL.md) documents each classification rule and the checks an administrator should perform. Result and evidence fields are listed in the [reference](docs/REFERENCE.md#result-fields).
109
+
110
+
## Reports and output
111
+
112
+
### HTML report
113
+
114
+
`--format html` produces a single offline, script-free file with a logo, summary tiles, every result with its evidence and next step, the upstream fix table, and run-completeness information. It follows the reader's light or dark theme and prints cleanly.
38
115
39
116
<palign="center">
40
117
<picture>
@@ -45,49 +122,69 @@ Python 3.10+ on Windows, macOS, or Linux. Nothing to install; use `python3` wher
45
122
46
123
<palign="center"><sub>Synthetic example built from reserved <code>example.*</code> domains and invented runtimes. <ahref="docs/example-report.html">View the HTML</a>.</sub></p>
47
124
48
-
Use `--logo path/to/logo.png` and `--brand "Your Company"` to brand it for your own organization. CSV and JSON remain available for spreadsheets and pipelines. `--fail-on-finding` exits 1 when any result is `potentially_affected` or `unsupported_runtime`; operational errors exit 2.
125
+
Use `--logo path/to/logo.png` and `--brand "Your Company"` to brand the report for your own organization.
49
126
50
-
##What the results mean
127
+
### CSV and JSON
51
128
52
-
| Status | Meaning |
53
-
| --- | --- |
54
-
| 🔴 `potentially_affected`| A plain Node.js version below its branch's fix is reported. Verify the deployed build and prioritize remediation. |
55
-
| 🟠 `unsupported_runtime`| The version is on an older, unsupported upstream line. Verify vendor support and move to a supported, patched runtime. |
56
-
| 🟢 `fixed_version_reported`| The reported version meets the fix. Not independent verification of installed patches. |
57
-
| ⚪ `unknown`| Version missing, malformed, conflicting, vendor-qualified, custom-built, pre-release, possibly backported, or outside this rule snapshot. |
129
+
CSV (the default) and JSON suit spreadsheets and pipelines. Progress and diagnostics go to stderr, leaving stdout available for data.
58
130
59
-
| Node.js branch | 22.x | 24.x | 26.x |
60
-
| --- | --- | --- | --- |
61
-
|**Fixed in**| 22.23.4 | 24.22.1 | 26.12.1 |
131
+
Report schema `1.1` records the checker's executable SHA-256 (`tool_sha256`, not a publisher signature) and explicit run completeness in JSON, CSV, and HTML. Existing observation columns and inventory schema `1` remain compatible. An empty CSV report contains one summary row rather than a fabricated target. The JSON report is an object with top-level `tool`, `tool_version`, `tool_sha256`, `report_schema_version`, `rules_as_of`, `generated_at`, `complete`, and `results` fields. `complete: true` means the input loop finished, not that every request succeeded or every version was identified. See [CSV](docs/REFERENCE.md#csv), [JSON](docs/REFERENCE.md#json), and [result fields](docs/REFERENCE.md#result-fields) for details.
62
132
63
-
Thresholds come from the [Node.js October 2026 security releases](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases) and the release notes for [22.23.4](https://nodejs.org/en/blog/release/v22.23.4), [24.22.1](https://nodejs.org/en/blog/release/v24.22.1), and [26.12.1](https://nodejs.org/en/blog/release/v26.12.1), released 2026-10-21. The embedded rule snapshot is dated **2026-10-23**.
133
+
### Writing reports safely
64
134
65
-
> [!IMPORTANT]
66
-
> **Potential exposure is not confirmed exploitability.** Every result keeps exploitability `not_tested`. The vulnerability involves decoding attacker-controlled Brotli data, and a version alone cannot show whether an application reaches that code path. Treat every result as a prompt for an administrator to check.
135
+
- Existing output files are refused unless you pass `--force`. Input and logo files can never be the output destination, even with `--force`.
136
+
- The output directory must already exist. Files are prepared in a temporary file and published atomically; the default mode needs a filesystem that supports hard links and fails rather than weakening overwrite protection.
137
+
- The report is written after the input loop finishes or a keyboard interruption is handled. An interruption keeps completed observations and marks the report incomplete (`complete: false`). An interruption after a complete report has been published preserves that report. Abrupt termination, a crash, or power loss before the write can prevent report creation; there are no incremental checkpoints or automatic resume.
138
+
- CSV neutralizes spreadsheet formulas with a leading apostrophe and rejects NUL characters; a rejected write is an output error (exit 2).
139
+
- Reports can contain target URLs, response metadata, and operator-supplied inventory. Handle them as assessment records. There is no telemetry or automatic upload.
140
+
141
+
## Exit codes
142
+
143
+
| Code | Meaning |
144
+
| --- | --- |
145
+
|`0`| The run completed without an operational error. Findings do not change this unless `--fail-on-finding` is set. |
146
+
|`1`|`--fail-on-finding` was set and at least one result is `potentially_affected` or `unsupported_runtime`. |
147
+
|`2`| An argument, input, or output error, or an operational failure, prevented one or more requested checks. This takes precedence over `1`. |
148
+
|`130`| The run was interrupted. Completed observations are retained where output can be written. |
149
+
150
+
Operational outcomes (for example timeouts or blocked requests) are separate from assessment results. Review both.
151
+
152
+
## Scope and limitations
67
153
68
-
## How it works
154
+
-**Version triage only.** The checker never triggers the Brotli decoder, sends compressed request bodies, tests a crash, or attempts code execution. It does not review whether an application decompresses untrusted Brotli input through `node:zlib`.
155
+
-**Node.js only.** Thresholds are not applied to Bun, browser runtimes, standalone Brotli libraries, or other packages with similar version numbers. It is not a general vulnerability scanner.
156
+
-**Conservative by design.** Missing, malformed, conflicting, vendor-qualified, custom-build, pre-release, and uncovered-future versions stay `unknown`. `unsupported_runtime` is an upgrade-and-review result, not a claim that the vulnerability was reproduced on that release.
157
+
-**Fixed version reported is not a patched deployment.** Backports and vendor patches are not detected, and a missing header or successful scan does not show that a service is patched.
158
+
-**Local CLI, not a service.** Private and loopback addresses are supported for authorized internal assessment. The program is not designed to accept untrusted URLs from the internet.
159
+
-**Website checks stay narrow.** There is no crawling or endpoint enumeration, no automatic retries or access-control bypasses, no HTTP fallback after a TLS error, and no option to disable TLS verification or skip robots policy. A permissive `robots.txt` does not by itself grant permission to assess a website.
69
160
70
-
-**Known version:**`--node-version` (repeatable) compares a version you obtained elsewhere. No network calls.
71
-
-**Runtime inventory:**`collect-runtime.cjs` records version and build metadata from the Node.js binary that runs the service. It never loads application code, reads environment variables, decompresses data, or opens a connection, and it cannot attest vendor patch provenance.
72
-
-**Website metadata:** reads `robots.txt`, then makes an ordinary `GET` request to the page if allowed, with verified TLS, an honest User-Agent, bounded responses, and pacing. Redirects stay on the same host or its `www` counterpart, and HTTPS downgrades stop. It fetches nothing else and sends no compressed bodies, credentials, or crafted requests.
161
+
## Optional tooling
73
162
74
-
Input modes can be combined in one report; each observation keeps its own evidence. Express or Next.js hints never become Node.js version evidence, and Brotli response compression does not prove request decoding.
163
+
None of these changes the standalone checker, its embedded offline rule snapshot, or the Node2Shell inventory schema.
75
164
76
-
Check only targets you are authorized to assess, and keep real target lists, inventories, and results out of version control.
165
+
-**Signed-rule verification:**[offline TUF bundle verification](tools/signed_rules/README.md) uses separate optional dependencies and an explicitly provisioned public trust root. It never applies a rule update automatically.
166
+
-**Authenticated rule publication:** the [keyless publication channel](docs/KEYLESS_RULES.md) uses pinned GitHub/Sigstore publisher identity, expiring artifacts, and durable rollback checks. Updates produce a separate review artifact.
167
+
-**Family metadata tools:**[Node capability and build-context collection](docs/NODE_METADATA.md) produces separate formats that do not infer application exposure from available APIs, lockfile declarations, or image tags.
77
168
78
169
## Documentation
79
170
80
171
-[Full reference](docs/REFERENCE.md): every CLI option, inventory field, output field, and request rule
81
172
-[Assessment methodology](docs/NODE2SHELL.md): how each version is classified, with sources
173
+
-[Node advisory matrix](docs/NODE_ADVISORY_MATRIX.md): named Node.js advisories and their checker coverage
82
174
-[Exploit-specific checker repositories](docs/CHECKER_FAMILY.md): roadmap for one independent repository per exploit
83
-
-[Checker delivery status](docs/CHECKER_STATUS.md): dated merge/CI receipts and remaining roadmap scope
-[Checker delivery status](docs/CHECKER_STATUS.md): dated merge and CI receipts and remaining roadmap scope
176
+
-[Changelog](CHANGELOG.md)
177
+
178
+
## Development and contributing
179
+
180
+
Issues and pull requests are welcome; start with [CONTRIBUTING.md](CONTRIBUTING.md). Report a security flaw in this checker as described in [SECURITY.md](SECURITY.md).
85
181
86
182
```sh
87
-
python -m unittest -v # local-only tests; no external websites are contacted
183
+
python -m unittest -v # local-only tests; no external websites are contacted
184
+
python node2shell_check.py --help
88
185
```
89
186
90
-
For agents contributing to this repository, [AGENTS.md](AGENTS.md) contains the shared development instructions; [CLAUDE.md](CLAUDE.md) points Claude to them.
187
+
Hosted CI runs the suite on Linux, Windows, and macOS with Python 3.10 and 3.14. For agents contributing to this repository, [AGENTS.md](AGENTS.md) contains the shared development instructions; [CLAUDE.md](CLAUDE.md) points Claude to them.
91
188
92
189
## License
93
190
@@ -97,22 +194,3 @@ Dual licensed: [AGPL-3.0](LICENSE), including commercial use that meets its term
0 commit comments