Marimo GHSL-2026-226: Test-Release Workflow TOCTOU
A same-second timestamp race let a pull-request update pass a maintainer’s test-release approval, run in the test-release job, and pass artifacts to OIDC-enabled publishing jobs for TestPyPI and npm. The vulnerable workflow was deleted; no CVE was assigned.
A lab manager signs off on one sample, but the lab checks only the second on a wall clock. If someone swaps the sample within that same second, the replacement is mistaken for the approved one. Marimo’s workflow made that mistake with a pull-request commit, then carried the built package into publishing jobs.
Core Concepts & Subsystem Terms
TOCTOU- Time-of-check to time-of-use: a resource changes after a security check but before the system uses it.
OIDC publishing- A CI job exchanges a GitHub Actions identity token for short-lived credentials at a package registry, instead of storing a long-lived password.
Workflow artifact- A file bundle transferred between GitHub Actions jobs. A downstream job must treat artifacts from untrusted workflows as untrusted input.
GHSL-2026-226- GitHub Security Lab’s advisory identifier for this Marimo workflow flaw. The advisory did not assign a CVE.
CWE-367 / CWE-829- The advisory identifies a time-of-check/time-of-use race and execution of functionality from an untrusted control sphere.
Framework mappings
Step-by-Step Attack Flow
A maintainer authorizes a test release
A maintainer comments on a pull request to start Marimo’s test-release workflow. The authorization is intended for the pull-request state the maintainer reviewed.
The pull-request head changes in the same second
The workflow compares the repository’s pushed_at timestamp with the comment’s created_at timestamp after converting both to whole seconds. A same-second push compares equal.
The vulnerable check accepts equality
The deleted workflow rejected only pushed_at values strictly greater than the comment time. Equality passed, and the workflow recorded the updated pull-request commit.
Untrusted code builds package artifacts
The job checked out that commit and ran repository-controlled actions and build scripts. Its outputs were uploaded, then consumed by downstream publishing jobs without rebuilding from a trusted commit.
Publishing authority reaches the untrusted output
The TestPyPI and npm jobs used OIDC publishing permissions. The advisory explains that the artifact could reach package publication; it does not report that a real package was compromised.
Source Code: Flaw vs. Secure Implementation
GHSL-2026-226 says the vulnerable workflow was deleted in PR #10765; this is not a claim that the project applied the comparator change shown here. The old check used -gt, so equal whole-second timestamps passed. The illustrative -ge guard rejects equality, but timestamp checks are weaker than binding approval to the exact reviewed commit. Keep untrusted pull-request builds separate from OIDC-enabled publishing jobs, rebuild from the authorized immutable SHA, and validate provenance before publication. No exploit payload is included.
# Deleted marimo-bot workflow: whole-second comparison
if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
exit 1
fi
# The resulting head_sha was checked out and its build artifacts
# were passed to separate package-publishing jobs.
# Illustrative control only; GHSL reports the vulnerable
# workflow was deleted rather than patched in place.
if [[ $(date -d "$pushed_at" +%s) -ge $(date -d "$COMMENT_AT" +%s) ]]; then
exit 1
fi
# Stronger design: authorize an immutable reviewed SHA, rebuild
# from that SHA in a trusted job, then publish verified outputs.
Engineering & System Hardening Checklist
- ✓Remove or replace affected copies of the deleted marimo bot workflow; verify the current repository no longer enables the vulnerable release path.
- ✓Bind maintainer approval to the exact immutable pull-request head SHA and fail closed if the head changes before checkout.
- ✓Do not use whole-second timestamps as the sole proof that a mutable pull-request branch is the version a maintainer approved.
- ✓Build untrusted pull requests in jobs without publishing credentials; keep OIDC id-token: write confined to a separate trusted publishing job.
- ✓Rebuild package outputs from the authorized commit in the trusted release path instead of promoting artifacts created by untrusted workflow code.
- ✓Verify artifact provenance, source revision, package contents, and release target before TestPyPI or npm publication.
- ✓Treat the GHSL advisory as evidence of a vulnerable path, not evidence that a package compromise or exploitation occurred.
Sources
- GitHub Security Lab advisory GHSL-2026-226 — reported by Jaroslav Lobačevski
- Marimo pull request #10765 deleting the vulnerable workflow
- GitHub Security Lab: Preventing pwn requests
- MITRE CWE-367: Time-of-check Time-of-use Race Condition
- MITRE CWE-829: Inclusion of Functionality from Untrusted Control Sphere
- Related workflow case (distinct): JupyterLab CVE-2026-84973