From acc34c208c81334d6d2debac7c06229530714bb6 Mon Sep 17 00:00:00 2001 From: Timothy Wayne Gregg Date: Mon, 26 Oct 2026 01:01:41 -0400 Subject: [PATCH 1/2] docs: reorganize and polish repository README --- README.md | 196 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 179 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 318d20e..7b82246 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,60 @@ # tinypool-options-rce-check -Offline evidence checker for **CVE-2026-104849 — Tinypool worker-options prototype-pollution gadget**. Python 3.10+, standard library only. The executable can be copied alone. It never contacts websites, imports application code, runs a package manager, or executes exploit payloads. +Offline evidence checker for **CVE-2026-104849 — Tinypool worker-options prototype-pollution gadget** (alias GHSA-85c8-ppgw-ccpr). + +It compares operator-reported `tinypool` versions with the reviewed advisory rules and writes CSV, JSON or HTML reports. It is version-evidence triage only: it never contacts websites, imports application code, runs a package manager, or executes exploit payloads, and exploitability is always reported as `not_tested`. + +## Contents + +- [Scope](#scope) +- [Requirements](#requirements) +- [Quick start](#quick-start) +- [Synthetic report preview](#synthetic-report-preview) +- [Reviewed rules](#reviewed-rules) +- [Interpreting results](#interpreting-results) +- [Preconditions](#preconditions) +- [Providing evidence](#providing-evidence) +- [Options](#options) +- [Reports and output](#reports-and-output) +- [Exit status and interruption](#exit-status-and-interruption) +- [Limitations](#limitations) +- [Sources and provenance](#sources-and-provenance) +- [Project documents](#project-documents) +- [Contributing and security](#contributing-and-security) +- [License and brand assets](#license-and-brand-assets) + +## Scope + +This repository covers only CVE-2026-104849. The checker does not scan deployed systems, fetch package inventories, analyze source code, or test exploitability. It reads versions and optional observations that you supply and reports what those claims imply under the reviewed rules. + +A public URL cannot provide installed package inventory. Obtain each deployed package instance's exact version and provenance; declarations and local workstation versions are not deployment evidence. Use it only with inventories you are authorized to assess. + +## Requirements + +- Python 3.10 or newer, standard library only. No runtime dependencies and no network access. +- `tinypool_options_rce_check.py` can be copied and run on its own; no companion repository is needed at runtime. +- The CI workflow (`.github/workflows/tests.yml`) runs the local-only tests and `--help` on Python 3.10 and 3.14 on Ubuntu and Windows. + +## Quick start ```sh +# Assess one reported version; the default format is csv python tinypool_options_rce_check.py --version 2.1.2 --format json + +# Assess a JSON inventory and save an HTML report to a new file python tinypool_options_rce_check.py --inventory examples/inventory.example.json --format html --output report.html + +# Assess a CSV list and exit 1 if any entry is potentially affected python tinypool_options_rce_check.py --csv-list inventory.csv --fail-on-finding + +# Show all options, then run the local tests python tinypool_options_rce_check.py --help python -m unittest -v ``` +`examples/inventory.example.json` is a synthetic entry (`2.1.2`, label `example-package-instance`, all observations `null`). Without `--output`, the report goes to stdout. + ## Synthetic report preview @@ -32,7 +76,7 @@ python -m unittest -v ## Reviewed rules -Primary advisory and reviewed database retrieved 2026-10-24. A public URL cannot provide installed package inventory. Obtain each deployed package instance's exact version and provenance; declarations and local workstation versions are not deployment evidence. +The primary advisory and reviewed database were retrieved on 2026-10-24 (`rules_as_of` in every report). | Example reported version | Result | | --- | --- | @@ -44,12 +88,43 @@ Primary advisory and reviewed database retrieved 2026-10-24. A public URL cannot | 2.2.0 | `unknown` | | 3.0.0 | `unknown` | -pool.run(task) alone is not the documented trigger. Surface 2.1.1 source conflict and do not report clean. Maintainer severity High differs from database Critical; retain provenance. +- **Affected interval:** `0.0.0` through `2.1.0` inclusive. +- **Fixed line:** `2.1.x`, fixed in `2.1.2`. Future lines remain unknown rather than extrapolating a patch claim. +- **Disputed boundary:** `2.1.1`. The maintainer range (`<=2.1.0`) and the GitHub Reviewed range (`<2.1.2`) disagree there, so it is reported as `unknown` and never as clean. Obtain maintainer evidence. +- **Not the trigger:** `pool.run(task)` alone is not the documented trigger. +- **Severity:** the maintainer rates it High; the database rates it Critical. Retain provenance when reporting. + +Only plain stable `MAJOR.MINOR.PATCH` versions are compared. A single leading `v` and surrounding ASCII whitespace are accepted. + +## Interpreting results + +| Status | Meaning | +| --- | --- | +| `potentially_affected` | The reported version is in the documented affected interval. This is not confirmed exploitability: preconditions and exploitability were not established. | +| `fixed_version_reported` | The reported version is at or above the fix within the reviewed `2.1.x` line. This describes a version claim only; it never attests an installed patch or safety. | +| `unknown` | Evidence is insufficient to classify. This is neither a clean result nor a finding. | + +Every result retains `exploitability: not_tested`. `evidence_type` is `operator_reported_version`; `evidence_confidence` is `insufficient` for `unknown` and `low` otherwise. -The reviewed fixed line is `2.1.x`; future lines remain unknown rather than extrapolating a patch claim. Versions outside the exact affected interval remain unknown, including older releases that might have other flaws. Missing, conflicting, malformed, prerelease, qualified, custom/vendor builds remain unknown. `fixed_version_reported` only describes a version claim; it never attests installed patches or safety. `potentially_affected` is not confirmed exploitability. Every row retains `exploitability: not_tested`. +Each result also carries a more specific `version_status`: + +| `version_status` | Status | Cause | +| --- | --- | --- | +| `in_documented_affected_range` | `potentially_affected` | Version is within `0.0.0`–`2.1.0`. | +| `at_or_above_fixed_version` | `fixed_version_reported` | Version is `2.1.2` or later within `2.1.x`. | +| `source_discrepancy` | `unknown` | Version is `2.1.1`, where the sources disagree. | +| `outside_reviewed_range` | `unknown` | Version is outside the affected interval and the reviewed fixed line; no safety or other-vulnerability conclusion is made. | +| `vendor_build_unverified` | `unknown` | `build_source` is a custom or vendor build, which needs independent patch evidence. | +| `qualified_version_unverified` | `unknown` | Prerelease, nightly/rc or suffixed versions need vendor verification. | +| `unrecognized_or_partial_version` | `unknown` | Version text is malformed or partial. | +| `version_not_disclosed` | `unknown` | No version was reported. Missing metadata does not establish that the runtime is unaffected. | + +Operational failures (bad arguments, unreadable or invalid input, output errors) are not assessment results. They exit with status 2 and are never reported as `unknown` or clean. ## Preconditions +The advisory describes conditions that must hold for the gadget to matter. You can record your own observations of them: + | Optional operator observation | Meaning | | --- | --- | | `caller_supplies_pool_run_options_object` | caller supplies pool.run options object | @@ -57,27 +132,114 @@ The reviewed fixed line is `2.1.x`; future lines remain unknown rather than extr | `separate_prototype_pollution_primitive` | separate prototype-pollution primitive | | `attacker_controlled_worker_module_available` | attacker-controlled worker module available | -Each observation is boolean or null, is unverified, and never changes the package-version result. No source search, taint analysis, reachability test, or runtime evaluation is performed. Absent observations do not establish mitigation. +Each observation is `true`, `false` or unset (`null` or blank). Reports show them as `reported_true`, `reported_false` or `not_reported`. They are unverified, and never change the package-version result. No source search, taint analysis, reachability test, or runtime evaluation is performed. Absent observations do not establish mitigation. + +## Providing evidence + +Provide at least one of `--version`, `--inventory` or `--csv-list`; they can be combined and repeated in one run. + +**Command line.** `--version` (alias `--package-version`) is repeatable. Each value is a separate instance, not evidence reconciliation for a single instance. `--build-source` and the observation flags (the key with underscores turned into hyphens, such as `--separate-prototype-pollution-primitive true`) apply to every `--version` input. + +**JSON inventory.** One object or an array of objects: + +| Field | Required | Notes | +| --- | --- | --- | +| `schema_version` | yes | Integer `1`. | +| `package_version` | yes | String. | +| `package` | no | Must be `"tinypool"` if present. | +| `label` | no | Display name; defaults to `:`. | +| `build_source` | no | Provenance of the build. | +| observation keys | no | `true`, `false` or `null`. | + +**CSV list.** A header row is required and must include `package_version`. Optional columns are `label`, `build_source` and the observation keys (`true`, `false` or blank). `schema_version` and `package` are not CSV columns. + +**Build provenance.** `build_source` values `unknown` (the default), blank, `upstream`, `official` and `official_upstream` are compared as upstream builds. Any other value, such as a vendor name, yields `vendor_build_unverified` and `unknown`, because vendor and custom builds may backport fixes or differ from upstream. + +**Rejected input.** Unknown or duplicate keys, wrong types, non-finite numbers and malformed JSON are rejected. CSV with duplicate or undocumented columns, rows with a different number of fields than the header, quotes inside unquoted cells, text after a closing quote, unterminated quotes, or NUL characters is rejected as well. + +**Bounds.** Only regular UTF-8 files are accepted. Limits: 4 MiB per file, 100 files per run, 10,000 entries per run (including `--version` values) and 512 characters per text field. Work from stable copies; these checks are not a filesystem sandbox. + +## Options + +| Option | Description | +| --- | --- | +| `--version`, `--package-version` `VERSION` | Assess an explicitly supplied tinypool version offline; repeatable. | +| `--build-source SOURCE` | Provenance of the `--version` builds (default `unknown`, compared as upstream). | +| `--` `true\|false` | Observation applied to `--version` inputs; one flag per [precondition](#preconditions) key. | +| `--inventory FILE` | JSON object or array of entries; repeatable. | +| `--csv-list FILE` | CSV list with a `package_version` column; repeatable. | +| `--format csv\|json\|html` | Output format (default `csv`). | +| `--output PATH` | Save the report to a new file instead of stdout. | +| `--force` | Replace an existing output file explicitly. | +| `--brand NAME` | Organization name shown in the HTML report (default `CompleteTech LLC`, at most 512 characters). It has no other effect. | +| `--fail-on-finding` | Exit 1 when any result is `potentially_affected`. | +| `--tool-version` | Print the checker name and version, then exit. | + +The detailed contract is in [docs/REFERENCE.md](docs/REFERENCE.md); `--help` shows the current options. + +## Reports and output + +JSON, formula-neutralized CSV and escaped, script-free HTML share the same assessments. + +- **JSON:** an object with `tool`, `tool_version`, `generated_at`, `report_schema_version` (`"1.0"`), `cve`, `rules_as_of`, `sources`, `complete` and a `results` array. +- **CSV:** one `record_type=result` row per asset observation, with the result fields above, a `precondition_` column for each observation, `report_schema_version`, `report_complete` and the run identity. Cells that could start a spreadsheet formula are prefixed with `'`, and all cells are quoted. NUL characters are rejected before a report is written. A zero-result report has one `record_type=summary` row carrying completeness, schema, rule date, CVE and `not_tested`; it is not an asset finding. +- **HTML:** a self-contained page with a restrictive content security policy, sorted with potentially affected results first. It shows the same generation time and checker identity, the fixed release, precondition observations, and notes. An interrupted run is flagged with a notice. + +All formats preserve the same run identity: `tool`, `tool_version`, and ISO 8601 UTC `generated_at`. CSV repeats these fields on every result and empty summary. The CLI captures generation time once before assessment and retains it when interrupted. `rules_as_of` remains the advisory verification date; generation time is not evidence acquisition time or deployed-patch attestation. + +**Safe publication.** The report is rendered fully in memory before anything is written. With `--output`, the directory must already exist and the file is staged then published atomically. An existing file is not overwritten unless you pass `--force`, and the output must differ from every input file even with `--force`. Without `--force`, publication uses a hard link; on a filesystem without hard links the checker reports an error and asks you to choose another filesystem or use `--force`. + +**Privacy.** Reports contain operator data. Keep real targets, inventories and reports out of commits. + +## Exit status and interruption + +| Exit | Meaning | +| --- | --- | +| `0` | Run completed. | +| `1` | At least one result is `potentially_affected` and `--fail-on-finding` was given. | +| `2` | Argument, input or output error; nothing was assessed or the report was not written. | +| `130` | Interrupted. Completed observations are preserved when possible. | + +`unknown` results do not trigger `--fail-on-finding` and are not a clean result. + +On interruption (`130`): + +- Before the output path is validated, nothing is written. +- If nothing has been published or started yet, completed results are written with `complete: false` (JSON), `report_complete=false` (CSV) or an interrupted-run notice (HTML). An empty interrupted CSV retains a summary row. +- If the complete report was already published, it is preserved, and the checker does not retry. +- If publication is left indeterminate, the checker does not retry. The diagnostic does not establish whether publication succeeded, so inspect the output before retrying. +- If interrupted while writing to stdout, the document can be truncated and no second report is appended. + +## Limitations -## Evidence and reports +- Results rest on operator-reported versions, not on observed deployments. `fixed_version_reported` is a reported fixed version, not a verified deployed patch. +- Backports, vendor builds, prereleases and custom builds cannot be resolved offline and stay `unknown`. +- Precondition observations are unverified and never change the version result. +- Versions outside the reviewed interval and fixed line are `unknown`; no conclusion about other flaws is made. +- The tool does not establish exploitability, reachability or production exposure. -JSON accepts one object or an array of objects. Required: integer `schema_version: 1`, string `package_version`. Optional: `package: "tinypool"`, `label`, `build_source`, and the observation keys above. Unknown or duplicate keys, wrong types, non-finite numbers and malformed inputs are rejected. CSV requires `package_version`; optional columns are label, build_source, and observation keys (`true`, `false`, blank). CLI `--version` is repeatable; values are separate instances, not evidence reconciliation for a single instance. `--build-source` and observation flags (underscore becomes hyphen) apply to CLI versions. +## Sources and provenance -Only regular UTF-8 input files are accepted. Limits: 4 MiB/file, 100 files, 10,000 entries total, 512 characters/text field. Work from stable copies; checks are not a filesystem sandbox. JSON, formula-neutralized CSV and escaped script-free HTML share assessments. Output files publish atomically and do not overwrite existing files without `--force`; inputs remain protected even with force. +- +- -Exit 0: completed run; 1: potentially affected with `--fail-on-finding`; 2: argument/input/output error; 130: interrupted, preserving completed observations when possible. Interruption during stdout can leave a truncated document and never appends a second report. Unknown does not count as a finding-gate failure or as a clean result. Reports contain operator data; keep real targets, inventories and reports out of commits. +Bounded input and reporting helpers are bundled from the reviewed Node.js checker at `74aa57ba5df05c34d17ae67912cd26ec2335201b`, with package-specific range comparison and the mixed-whitespace CSV fix. -## Sources and delivery +## Project documents -- https://github.com/tinylibs/tinypool/security/advisories/GHSA-85c8-ppgw-ccpr -- https://github.com/advisories/GHSA-85c8-ppgw-ccpr +- [Evidence and CLI reference](docs/REFERENCE.md) +- [Implementation status](docs/IMPLEMENTATION.md) +- [Synthetic example report](docs/example-report.html) +- [Repository instructions](AGENTS.md) +- [`advisory.json`](advisory.json): offline rule metadata +- [`SHA256SUMS`](SHA256SUMS): checksum for the checker script -[Synthetic report](docs/example-report.html), [reference](docs/REFERENCE.md), [implementation status](docs/IMPLEMENTATION.md), [AGENTS](AGENTS.md), [contribution terms](CONTRIBUTING.md), [security policy](SECURITY.md), [license](LICENSE), [dual licensing](LICENSING.md). Bounded input and reporting helpers are bundled from the reviewed Node.js checker at `74aa57ba5df05c34d17ae67912cd26ec2335201b`, with package-specific range comparison and the mixed-whitespace CSV fix. No companion repository is needed at runtime. +## Contributing and security -CSV `record_type=result` rows are asset observations. A zero-result report has a `record_type=summary` row carrying completeness, schema, rule date, CVE and `not_tested`; it is not an asset finding. +Read [AGENTS.md](AGENTS.md) and [CONTRIBUTING.md](CONTRIBUTING.md) first. Before proposing a change, run `python -m unittest -v`, `python tinypool_options_rce_check.py --help` and `git diff --check`. Use synthetic evidence only, and never run application code or send exploit payloads. Report flaws in this project privately as described in [SECURITY.md](SECURITY.md). -All formats preserve the same run identity: `tool`, `tool_version`, and ISO 8601 UTC `generated_at`. CSV repeats these fields on every result and empty summary; HTML shows the same time and checker identity. The CLI captures generation time once before assessment and retains it when interrupted. `rules_as_of` remains the advisory verification date; generation time is not evidence acquisition time or deployed-patch attestation. +## License and brand assets -CSV rejects NUL characters before writing a report. An interruption after a complete report is published preserves that report and exits 130. +Available under the GNU AGPL-3.0 ([LICENSE](LICENSE)) or a separate commercial license from CompleteTech LLC; see [LICENSING.md](LICENSING.md). Contributions are licensed as described in [CONTRIBUTING.md](CONTRIBUTING.md#licensing-of-contributions). -If an interrupt leaves file publication indeterminate, the checker exits 130 without retrying. Inspect the output before retrying; the diagnostic does not establish whether publication succeeded. +CompleteTech LLC names, logos and the artwork in `docs/images/` are brand assets and are not licensed under the AGPL-3.0 or a commercial code license; see [BRAND_ASSETS.md](BRAND_ASSETS.md). From a0ab3e4168f0ae030db114aff24d8b660f00e94f Mon Sep 17 00:00:00 2001 From: Timothy Wayne Gregg Date: Mon, 26 Oct 2026 01:04:13 -0400 Subject: [PATCH 2/2] docs: resolve README review findings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b82246..19121ae 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ All formats preserve the same run identity: `tool`, `tool_version`, and ISO 8601 | --- | --- | | `0` | Run completed. | | `1` | At least one result is `potentially_affected` and `--fail-on-finding` was given. | -| `2` | Argument, input or output error; nothing was assessed or the report was not written. | +| `2` | Usage, input, output or cleanup failure (an operational error, not an assessment result). This does not establish whether report bytes were already written; inspect the output before retrying. | | `130` | Interrupted. Completed observations are preserved when possible. | `unknown` results do not trigger `--fail-on-finding` and are not a clean result.