Supply Chain · PyPI · GitHub Actions · element-data

Poisoning
the Well
element-data

A malicious version of element-data, a Python CLI with over 1 million monthly downloads, was published to PyPI and Docker Hub after attackers exploited a GitHub Actions workflow vulnerability to steal signing keys and account tokens.

Severity CRITICAL
Package elementary-data
Bad Version 0.23.3 — Remove Now
CVE-Free Range 0.2.8 – 0.4.6
1M+
Monthly Downloads
~12h
Active Exposure Window
0.23.3
Malicious PyPI Version
Executive Summary
Critical Risk
The Compromise

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.

The Payload

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.

The Blast Radius

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.

Affected Versions — Treat as Malicious Remove immediately
PyPI Package elementary-data Version 0.23.3
Docker Image element-data Docker Affected Tag
Marker File (macOS/Linux) /tmp/.trinny-security-update IOC File
Marker File (Windows) %TEMP%\.trinny-security-update IOC File

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.

Downgrade Options (No Known CVEs): 0.4.6 0.4.5 0.4.4 0.4.3 0.4.2 0.4.1 0.4.0 0.3.0 0.2.9 0.2.8

⚠ 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.

Friday — Attack Window Opens
GitHub Actions Exploitation: The threat actor submits a malicious pull request to the element-data repository. The embedded code triggers a bash script that runs inside the developer account's GitHub Actions context, silently harvesting signing keys, publish tokens, and other account secrets.
Friday — Shortly After Exploitation
Malicious Package Published: Using the stolen PyPI publish token and signing keys, the attacker releases elementary-data version 0.23.3 to PyPI. The matching Docker image is simultaneously pushed to Docker Hub. Both are structurally identical to legitimate releases — no obvious anomalies in package metadata.
Friday to Saturday — ~12 Hours
Active Exposure Period: The malicious version is live and available for installation. Any developer or CI/CD pipeline running pip install elementary-data without a pinned version receives the compromised package. The credential harvesting payload executes on installation.
Saturday — Third-Party Issue Report
Compromise Discovered: The element-data team receives a third-party report identifying the malicious version. The investigation begins immediately.
Saturday — Within 3 Hours of Discovery
Package Removed & Credentials Rotated: Version 0.23.3 is pulled from PyPI and Docker Hub. The developers rotate all credentials that the malicious workflow had access to, patch the GitHub Actions vulnerability, and audit all other workflows for the same flaw. Safe version 0.23.4 is released.

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.

Initial Vector

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.

Credential Theft

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.

Payload Delivery

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.

CI/CD Risk

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.

Official Developer Remediation Checklist
01
Check Your Installed Version
Run the following command to determine what version of elementary-data is installed in your environment.
pip show elementary-data | grep Version
02
If Version is 0.23.3 — Uninstall, Then Downgrade or Remove
Remove the malicious version now. The only known CVE-free versions are 0.2.8 through 0.4.6. If you must retain this package, downgrade to one of these versions — but be aware they are significantly older builds and may lack features your workflows depend on. Test compatibility before deploying to production. If operational requirements cannot be met at these versions, replace the package entirely.
pip uninstall elementary-data
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
03
Check for the Malware Marker File
If this file is present, the payload executed on that machine. Treat the environment as fully compromised and escalate to your security team immediately.
# macOS / Linux — check for:
/tmp/.trinny-security-update

# Windows — check for:
%TEMP%\.trinny-security-update
04
Rotate All Exposed Credentials
Rotate every credential that was accessible in environments where 0.23.3 may have run. This includes: dbt profiles, warehouse credentials, cloud provider keys (AWS, GCP, Azure), API tokens, SSH keys, and contents of .env files. CI/CD runners are especially exposed — treat all pipeline secrets as compromised.
05
Hunt for Unauthorized Credential Usage
Contact your security team to audit logs for any unauthorized usage of the exposed credentials. Cross-reference cloud provider access logs, GitHub audit logs, and warehouse query history against the ~12 hour exposure window (Friday through Saturday).
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.
Immediate Actions DO NOW
Verify installed version of elementary-data across all developer machines, CI/CD runners, Docker images, and virtual environments. Run pip show elementary-data | grep Version and treat any environment running 0.23.3 as compromised.
Check for the malware marker file on every machine that may have run the affected version: /tmp/.trinny-security-update (macOS/Linux) or %TEMP%\.trinny-security-update (Windows). Presence confirms payload execution.
Rotate all credentials immediately that were accessible in any environment where 0.23.3 ran — dbt profiles, warehouse credentials, cloud provider keys (AWS/GCP/Azure), API tokens, SSH keys, and .env file contents.
Downgrade to a CVE-free version (0.2.8 – 0.4.6) if the package is operationally required — or remove it entirely. These older versions carry no known CVEs but are significantly behind the 0.23.x feature set. Verify compatibility with your workflows before deploying the downgrade to production. Do not upgrade to any 0.23.x version other than removing 0.23.3.
Short-Term Hardening WITHIN 30 DAYS
Audit all GitHub Actions workflows in your own repositories for the same vulnerability class. Look for workflows triggered by 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.
Pin all dependency versions in every Python project and enforce lockfile consistency in CI/CD pipelines. An unpinned pip install elementary-data would have installed the malicious version automatically.
Isolate secrets from build environments that install external dependencies. Secrets should be injected only after the dependency installation phase, or install steps should run in network-restricted containers without access to production credentials.
Enable anomaly detection on cloud provider and data warehouse access to catch unauthorized usage of any credentials that may have been exfiltrated during the exposure window.
Long-Term Resilience ONGOING
Maintain a Software Bill of Materials (SBOM) for every project and production environment, including transitive Python dependencies, so you always know what is installed and can respond immediately when a package is identified as compromised.
Integrate a Software Composition Analysis (SCA) tool into all pipelines to automatically flag newly introduced or changed dependencies before they reach production — including version changes that were not explicitly approved.
Subscribe to PyPI security advisories and data ecosystem security feeds. This incident was reported and resolved within hours — organizations with active intelligence subscriptions received the earliest warning and were able to check exposure before broad disclosure.
Apply least-privilege principles to CI/CD secrets. No pipeline runner should have access to credentials it does not require for the specific job it is executing. Broad secret mounts amplify the impact of every supply chain compromise.
Threat Indicators — Treat these as malicious
Malicious PyPI Package
elementary-data==0.23.3
Compromised Distribution
Docker image — affected tag (Friday release)
Marker File — macOS/Linux
/tmp/.trinny-security-update
Marker File — Windows
%TEMP%\.trinny-security-update
Attack Vector
GitHub Actions Workflow (PR exploitation)
Exposure Window
~12 hours (Friday → Saturday)
Data Targeted
dbt profiles, cloud keys, SSH keys, .env
Detection Method
Third-party issue report
Scope — Unaffected
Elementary Cloud, dbt package, other CLI versions
CVE-Free Downgrade Range
0.2.8 → 0.4.6 (verify compatibility)