What Happened
The elementary-data package on PyPI, downloaded over 1.1 million times monthly, was compromised to distribute version 0.23.3 containing malware designed to steal sensitive data and cryptocurrency wallets. The attacker exploited a GitHub Actions script injection vulnerability to publish the malicious package as an official release. Because the package's workflow automatically built and pushed Docker images, the compromise extended beyond PyPI to containerized deployments.
Timeline
The attack sequence unfolded through the CI/CD pipeline:
- Initial compromise: The attacker identified a script injection flaw in the elementary-data GitHub Actions workflow.
- Exploitation: Malicious code injected into the workflow allowed unauthorized package publication.
- Publication: Version 0.23.3 was released to PyPI with an embedded infostealer payload.
- Propagation: The automated Docker image build process distributed the compromised package to container registries.
- Detection: StepSecurity identified the malicious release and published their analysis.
This timeline shows how quickly a CI/CD compromise can cascade through multiple distribution channels.
Which Controls Failed or Were Missing
Workflow input validation: The GitHub Actions workflow accepted untrusted input without sanitization. Script injection vulnerabilities occur when workflow files use expressions like ${{ github.event.issue.title }} without proper escaping. The attacker used this to inject arbitrary commands into the build process.
Code signing and verification: The package lacked cryptographic signatures for authenticity verification. Even if signatures existed, the automated workflow would have signed the malicious package with legitimate credentials.
Pipeline access controls: The workflow had permissions to publish packages without requiring manual approval, allowing the attacker to complete the attack chain programmatically.
Dependency pinning: Organizations consuming elementary-data likely used version ranges (e.g., elementary-data>=0.23.0) rather than pinned versions, causing automatic updates to pull the malicious release.
Runtime monitoring: No behavioral analysis detected the infostealer's data exfiltration attempts during execution, indicating inadequate runtime application self-protection (RASP) or endpoint detection.
What the Relevant Standard Requires
NIST 800-53 Rev 5 Control SA-10 (Developer Configuration Management) requires configuration management during system development. Specifically, SA-10(1) mandates software integrity verification, and SA-10(6) requires integrity verification of software packages. The elementary-data workflow failed both — it didn't verify the integrity of workflow inputs, and consumers had no mechanism to verify package integrity.
ISO 27001 Annex A.8.31 (Separation of Development, Test and Production Environments) requires logical or physical separation and formal change management. The compromised workflow had direct production publishing capabilities without separation or approval gates. Proper implementation would require manual promotion from a staging environment to production registries.
NIST CSF v2.0 function PR.DS-6 states: "Integrity checking mechanisms are used to verify software, firmware, and information integrity." The package ecosystem provided no integrity checking beyond HTTPS transport. Consumers need cryptographic signatures and verification tooling.
PCI DSS v4.0.1 Requirement 6.3.2 mandates secure development of custom software, including secure coding practices. The workflow's script injection vulnerability violates basic secure coding principles.
SOC 2 Type II CC6.6 requires logical access controls to restrict access to system components. The GitHub Actions workflow had overly permissive access to publishing credentials without compensating controls like manual approval or multi-party authorization.
Lessons and Action Items for Your Team
Pin Your Dependencies — Today: Open your requirements.txt, package.json, or equivalent. Replace version ranges with exact versions: elementary-data==0.23.2 instead of elementary-data>=0.23.0. This creates maintenance overhead, forcing you to review changes before they enter your environment. Use tools like Dependabot or Renovate to automate the review process, but never the approval.
Audit Your GitHub Actions Workflows: Search your repositories for ${{ github.event patterns in workflow files. Any workflow using event data in a run: block without proper sanitization is vulnerable. Replace direct variable interpolation with environment variables:
# Vulnerable
run: echo "${{ github.event.issue.title }}"
# Safer
env:
TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
Better yet, avoid using untrusted input in shell commands entirely.
Implement Publishing Gates: Your CI/CD pipeline should build artifacts automatically but require manual approval to publish. Configure your package registry credentials as environment secrets available only to protected branches or specific workflow jobs. Use GitHub Environments with required reviewers for any job that publishes to production registries.
Enable SLSA Provenance: Configure your build pipeline to generate SLSA (Supply Chain Levels for Software Artifacts) provenance attestations. GitHub Actions supports this through reusable workflows. Consumers can then verify that packages came from your legitimate build infrastructure, not from an attacker who compromised your workflow.
Monitor Package Behavior: Deploy runtime monitoring that alerts on unexpected network connections, file system access, or process creation. The elementary-data infostealer accessed cryptocurrency wallet files and exfiltrated data — behaviors that should trigger immediate alerts if you're monitoring process activity.
Verify Package Signatures: If your language ecosystem supports package signing (like Python's PEP 740 or npm's signature verification), enable it. If it doesn't, consider implementing your own verification layer using tools like Sigstore or in-toto. At minimum, verify package checksums against a known-good manifest stored in your version control.
Segment Your Build Environments: Your package publishing workflow should run in an isolated environment with minimal access to other systems. If an attacker compromises the workflow, they shouldn't gain access to your source code repositories, production credentials, or internal networks. Apply the principle of least privilege ruthlessly.
The elementary-data incident proves that your dependencies are only as secure as their CI/CD pipelines. You can't audit every package's workflow, but you can control when and how updates enter your environment. Start with pinned versions and expand from there.



