Script injection in GitHub Actions ships malicious elementary-data to PyPI
TL;DR - An attacker dropped a crafted PR comment that got interpolated into a shell context by a vulnerable GitHub Actions workflow. That gave them code execution on a CI runner with access to workflow secrets. They used those secrets to trigger release.yml and publish elementary-data==0.23.3 - a malicious build - to PyPI and a Docker registry. If you ran 0.23.3, treat every credential in that environment as compromised.
What happened
elementary-data is Elementary's open-source Python CLI, commonly run inside data and CI pipelines.
On April 24, 2026, an attacker exploited a script-injection vulnerability in one of Elementary's GitHub Actions workflows. The attack shape: a crafted pull request comment body was interpolated directly into a shell context by a vulnerable workflow step. That gave the attacker arbitrary code execution on the CI runner, which had access to workflow secrets. From there, they triggered release.yml via workflow_dispatch and published two malicious artefacts: elementary-data version 0.23.3 to PyPI, and a corresponding Docker image to Elementary's registry.
The window ran from 2026-04-24 22:10 UTC to 2026-04-25 09:45 UTC.
The real risk here is not a bad package version sitting on a registry. It is that a single workflow injection can become a direct publishing compromise - which is the same failure mode behind Cline, tj-actions, and most other recent CI supply-chain incidents.
Who is impacted
- Users who downloaded and executed
elementary-data==0.23.3during the exposure window. - Users who pulled and ran the affected Docker image between 2026-04-24 22:10 UTC and 2026-04-25 09:45 UTC.
- Elementary confirms no impact to Elementary Cloud, the Elementary dbt package, or any other CLI versions.
Indicators of compromise (from the report):
| Indicator | Value |
|---|---|
| Compromised package | elementary-data==0.23.3 |
| Injection file | elementary.pth |
| Exfiltration domain | igotnofriendsonlineorirl-imgonnakmslmao.skyhanni.cloud |
| Execution marker | $TMPDIR/.trinny-security-update |
What to do now
- Check whether you installed the compromised version:
pip show elementary-data | grep Version
- If you have
0.23.3, remove it and install the safe release:"If the version is 0.23.3, uninstall it and replace with the safe version:"
pip uninstall elementary-datapip install elementary-data==0.23.4
"In your requirements and lockfiles, pin explicitly to elementary-data==0.23.4."
- Delete pip cache files per vendor guidance to avoid stale artefacts.
- Check for the execution marker on any machine where the CLI may have run:
- macOS / Linux:
/tmp/.trinny-security-update - Windows:
%TEMP%\.trinny-security-update
- macOS / Linux:
- Treat this as a secrets exposure event if
0.23.3ran anywhere in your environment:"Users who installed 0.23.3, or who pulled and ran the affected Docker image, should assume that any credentials accessible to the environment where it ran may have been exposed."
- Rotate every credential accessible to the affected environment. Elementary explicitly calls out: dbt profiles, warehouse credentials, cloud provider keys, API tokens, SSH keys, and
.envcontents. - Prioritise CI/CD runners first - they typically hold the broadest secret access at runtime.
- Rotate every credential accessible to the affected environment. Elementary explicitly calls out: dbt profiles, warehouse credentials, cloud provider keys, API tokens, SSH keys, and
- Escalate to your security team to hunt for unauthorised use of exposed credentials, using the IOCs above.
Why this matters for AppSec
This incident is the same attack chain that has hit multiple projects in the last 12 months: unsanitised GitHub context variables interpolated into shell steps, leading to CI code execution, leading to registry compromise.
The lessons are straightforward:
- Never interpolate GitHub context values directly into shell steps. Use intermediate environment variables instead. GitHub's own hardening guide covers this explicitly.
- Scope publish tokens tightly and rotate them. A token that can publish to PyPI should not live indefinitely as a workflow secret with broad scope.
- Adopt OIDC-based publishing where the registry supports it (PyPI does). Short-lived, audience-scoped tokens eliminate the stolen-token class of attack entirely.
- Audit
workflow_dispatchtriggers. An attacker who gains code execution on a runner with access to workflow secrets can trigger downstream workflows. Treatworkflow_dispatchas a privileged action.
Additional information
- Elementary's full disclosure includes a UTC timeline, remediation actions (token rotation, workflow removal and audit, release of
0.23.4), and complete IOCs: https://www.elementary-data.com/post/security-incident-report-malicious-release-of-elementary-oss-python-cli-v0-23-3 - The initial external report: https://github.com/elementary-data/elementary/issues/2205
Related
Content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly, send corrections to [email protected]. Always consult official documentation and validate key implementation decisions before making design or security choices.
