Attackers exploited a flaw in a GitHub Actions workflow to run a malicious bash script inside the developers' environment, harvesting signing keys and publish tokens without any credential phishing.
Version 0.23.3 scoured infected systems for user profiles, dbt warehouse credentials, cloud provider API keys, SSH keys, and .env file contents — all silently exfiltrated.
Both the PyPI package and the Docker image were poisoned. CI/CD runners are especially exposed — they routinely have broad sets of secrets mounted at runtime that were all within reach of this payload.
The malicious version was published on Friday and removed approximately 12 hours later on Saturday. Elementary Cloud, the Elementary dbt package, and all other CLI versions were not affected.
⚠ These versions are free of known CVEs but are significantly older builds. Verify feature compatibility with your environment before downgrading, or evaluate a replacement tool entirely.
element-data is a command-line interface tool used by data and ML engineering teams to monitor performance and detect anomalies in machine-learning systems. With more than 1 million monthly downloads, it sits inside the development and CI/CD environments of a large number of organizations globally — making it a high-value supply chain target.
On Friday, April 25, an unknown threat actor exploited a vulnerability in a GitHub Actions workflow maintained by the element-data developers. By submitting a pull request with embedded malicious code, the attacker was able to trigger a bash script that executed inside the developers' own account context — a technique that bypasses standard access controls entirely, requiring no stolen credentials upfront.
Using the tokens and signing keys obtained from that workflow exploitation, the attacker published a trojanized version of element-data to both PyPI and the Docker Hub image registry. The malicious package was nearly indistinguishable from the legitimate release. The developers discovered the compromise through a third-party issue report and removed the malicious version within three hours of detection — but it had already been live for approximately 12 hours.
Assume Compromise
The developers' own advisory is unambiguous: "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." CI/CD runners are especially at risk — they typically have broad sets of secrets mounted at runtime.
pip install elementary-data without a pinned version receives the compromised package. The credential harvesting payload executes on installation.The root cause was a vulnerable GitHub Actions workflow — a category of vulnerability that security researchers have long flagged as systemic across open source projects. When a repository accepts pull requests from external contributors and runs CI/CD workflows triggered by those PRs, an attacker can craft a PR whose code executes with the privileges of the repository's own workflow context.
In this case, the attacker's bash script ran inside that privileged context and extracted the secrets needed to impersonate the legitimate maintainers. With valid publish tokens and signing keys in hand, the attacker could push packages that appeared entirely authentic — passing all standard integrity checks that rely on those very credentials.
Why This Attack Class Is Hard to Prevent
GitHub Actions workflows that use pull_request_target triggers or that check out untrusted PR code in a context with access to secrets are vulnerable. The attack does not require the attacker to have any existing repository access — only the ability to submit a pull request. HD Moore noted that tools exist to audit for such vulnerabilities, but most open-source maintainers are unaware their workflows are exposed.
GitHub Actions PR Exploit
A malicious pull request triggered a bash script in the developers' CI workflow context — no credential phishing, no account takeover. The workflow itself was the vulnerability.
Signing Keys & Publish Tokens
The bash script extracted PyPI publish tokens and signing keys from the workflow environment — granting the attacker full ability to publish packages as the legitimate maintainer.
Credential Harvesting at Install
The malicious package scoured every system it was installed on for dbt profiles, warehouse credentials, cloud API keys, SSH keys, and all .env file contents.
Pipeline Secrets Exposed
CI/CD runners that auto-install dependencies without pinned versions had every mounted secret — deployment keys, cloud accounts, application credentials — within range of this payload.
The following steps address the immediate threat from version 0.23.3. Versions 0.2.8 through 0.4.6 are the only known CVE-free releases — downgrading to this range is an option if the package is operationally required, but these are significantly older builds. Verify functionality before committing any downgrade to production, and consider replacing the package entirely if feature gaps are unacceptable.
Downgrade — Not Upgrade — Is the Safer Path
Versions 0.2.8 through 0.4.6 carry no known CVEs and are the only confirmed clean range. However, these are significantly older builds — functionality gaps and breaking changes are expected compared to the 0.23.x series. Validate compatibility thoroughly before downgrading in production. If full functionality cannot be retained at these versions, replacing the package entirely is the recommended long-term position.
pip cache purge
# Downgrade to a CVE-free version (0.2.8 – 0.4.6) if required:
pip install elementary-data==0.4.6
# Verify functionality — breaking changes are likely vs 0.23.x
# If features are missing, evaluate a replacement tool
/tmp/.trinny-security-update
# Windows — check for:
%TEMP%\.trinny-security-update
| Risk Area | Severity | Description |
|---|---|---|
| Credential Theft | Critical | Cloud API keys, warehouse credentials, SSH keys, dbt profiles, and .env file contents on any affected machine should be assumed stolen and rotated immediately. |
| CI/CD Pipeline Exposure | Critical | Automated build pipelines without pinned dependency versions are maximally exposed. A compromised runner has access to every secret mounted at pipeline runtime — typically the organization's most privileged credentials. |
| GitHub Actions Attack Surface | Critical | The root cause is systemic. Any open-source project that accepts external PRs and runs CI workflows without proper isolation may be vulnerable to the same technique. This is not a one-off flaw. |
| Docker Image Compromise | High | The Docker image was simultaneously poisoned alongside the PyPI release. Containerized deployments that pulled the affected tag and ran it in environments with mounted secrets are equally exposed. |
| ML System Access | High | element-data is deployed specifically in ML monitoring environments. These environments commonly hold credentials to data warehouses, model registries, and cloud ML infrastructure — all high-value targets. |
| Detection Difficulty | High | The malicious version was nearly indistinguishable from a legitimate release, signed with valid keys. Standard package integrity checks would not have flagged it. Only behavioral monitoring or version pinning would have provided protection. |
| Downstream Dependencies | Medium | Organizations that do not use element-data directly but have it as a transitive dependency in data tooling stacks may have been exposed without knowing the package was installed. |
pip show elementary-data | grep Version and treat any environment running 0.23.3 as compromised./tmp/.trinny-security-update (macOS/Linux) or %TEMP%\.trinny-security-update (Windows). Presence confirms payload execution.pull_request_target or that check out untrusted code with access to secrets. Use the tool referenced by HD Moore or GitHub's own security advisories.pip install elementary-data would have installed the malicious version automatically.