You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
These attacks fall into three buckets we should be defending against:
Maintainer-account takeover → malicious publish. Phishing or session-cookie theft on a maintainer's PyPI/npm/GitHub account leads to a poisoned release that auto-installs in CI everywhere.
Transitive-dep compromise. A dependency (direct or transitive) gets a malicious version published; our wheels pull it in.
Post-install / import-time code execution. Malicious code in setup.py / __init__.py / npm postinstall fires on pip install or npm install.
Migrate TestPyPI publishes to OIDC trusted publishing. Today publish_engine_testpypi.yml and publish_schema_testpypi.yml still use the long-lived TEST_PYPI_API_TOKEN secret. A token leak from a workflow log or a compromised fork PR would be enough to ship a poisoned dev snapshot that internal smoke tests then pull. Same OIDC model as release.yml.
Sigstore signing + PEP 740 attestations on all published wheels.pypa/gh-action-pypi-publish@release/v1 supports attestations: true when used with trusted publishing. Produces verifiable provenance ("this wheel was built by this workflow run at this commit") that downstream consumers can check.
Require manual approval (GitHub environments) for the production release.yml publish job. Even with OIDC, a compromised main could trigger a tagged release. An environment: pypi-prod with required reviewers gates this.
Publish an SBOM (CycloneDX or SPDX) alongside each release. Lets adopters diff what changed between versions and audit transitive deps.
B. Maintainer-account hardening (organisation policy)
Enforce 2FA on the Idun-Group GitHub org (org setting → security → require 2FA).
Document a hardware-key requirement for accounts with publish access on PyPI / npm. PyPI now supports WebAuthn; npm requires it for maintainers of high-impact packages.
Restrict PyPI / npm publish roles to the minimum number of maintainers. Audit current owner/maintainer lists for idun-agent-engine, idun-agent-schema, and the npm side (if applicable). Remove inactive accounts.
Document a credential-rotation + incident runbook in SECURITY.md covering: maintainer-account suspicion → revoke tokens → yank affected versions on PyPI → notify Socket / GHSA → post-mortem template. Shai-Hulud spread because nobody had a 1-hour-from-detection plan.
C. Dependency hygiene
Lockfile audit on every PR. We have uv.lock and pnpm-lock.yaml. Add a workflow step that fails the PR if a lockfile changed without a corresponding pyproject.toml / package.json change (catches lockfile-only injection).
pip install --require-hashes for the engine wheel's own deps in CI smoke tests. Catches a TanStack-style mid-air swap during install.
OpenSSF Scorecard workflow on main — public score + alerts on regressions.
Pin guardrails-ai to a specific known-good version range in the [guardrails] extra rather than a floor pin, given the 0.10.1 incident. Add a comment linking to the Socket report. (Now that fix(engine): make guardrails-ai an optional extra to unblock PyPI quarantine #642 made it optional, the blast radius is smaller, but adopters who opt in still need a safe default range.)
D. Workflow-token blast radius
Set permissions: contents: read as the default at the workflow level for every workflow, then escalate per-job. Reduces what a compromised action can do.
Disallow pull_request_target unless we explicitly need it; prefer pull_request everywhere. pull_request_target runs with the target branch's secrets — the attack vector behind several recent action-compromise incidents.
Block third-party action submissions in CI. Only allow actions from actions/, pypa/, astral-sh/, Idun-Group/, and an explicit allowlist. Enforced via repo settings (Settings → Actions → "Allow specified actions").
Acceptance
This issue is closed when:
Section A items A1–A4 have shipped and are linked here.
Section B is reflected in updated SECURITY.md + org settings (org-side changes confirmed by screenshot in a comment).
Section C has at least the lockfile-audit step, OpenSSF Scorecard, and guardrails-ai pin range in place.
Section D's permissions: default + action allowlist are in repo settings.
Out of scope
Runtime-side hardening of agent execution (sandboxing, network egress controls) — separate concern, separate issue.
Customer-side guidance for verifying installed wheels — belongs in docs/, follow-up after attestations ship.
Why
Three recent incidents make it worth auditing our supply-chain posture as a public, pip-installable + npm-adjacent project:
client-python— GHSA-wx9m-wx4f-4cmg (advisory)guardrails-ai0.10.1__init__.pysuspicious content (Socket file view) — relevant to us becauseguardrails-aiis a runtime extra ofidun-agent-engine(we just moved it to optional in fix(engine): make guardrails-ai an optional extra to unblock PyPI quarantine #642, but adopters who opt in still pull it).These attacks fall into three buckets we should be defending against:
setup.py/__init__.py/ npm postinstall fires onpip installornpm install.What we already have
release.ymlusesid-token: write(chore(security): SHA-pin actions, dep-audit job, SECURITY.md hardening #638)pinactpre-commit hook + scheduled dep-audit job (chore(security): SHA-pin actions, dep-audit job, SECURITY.md hardening #638, chore(security): pinact hook + Next.js/aiohttp CVE fixes #641)guardrails-aimoved to an optional extra so it's not in the default install path (fix(engine): make guardrails-ai an optional extra to unblock PyPI quarantine #642)Gaps to close
A. Publishing pipeline
publish_engine_testpypi.ymlandpublish_schema_testpypi.ymlstill use the long-livedTEST_PYPI_API_TOKENsecret. A token leak from a workflow log or a compromised fork PR would be enough to ship a poisoned dev snapshot that internal smoke tests then pull. Same OIDC model asrelease.yml.pypa/gh-action-pypi-publish@release/v1supportsattestations: truewhen used with trusted publishing. Produces verifiable provenance ("this wheel was built by this workflow run at this commit") that downstream consumers can check.release.ymlpublish job. Even with OIDC, a compromisedmaincould trigger a tagged release. Anenvironment: pypi-prodwith required reviewers gates this.B. Maintainer-account hardening (organisation policy)
idun-agent-engine,idun-agent-schema, and the npm side (if applicable). Remove inactive accounts.SECURITY.mdcovering: maintainer-account suspicion → revoke tokens → yank affected versions on PyPI → notify Socket / GHSA → post-mortem template. Shai-Hulud spread because nobody had a 1-hour-from-detection plan.C. Dependency hygiene
uv.lockandpnpm-lock.yaml. Add a workflow step that fails the PR if a lockfile changed without a correspondingpyproject.toml/package.jsonchange (catches lockfile-only injection).pip install --require-hashesfor the engine wheel's own deps in CI smoke tests. Catches a TanStack-style mid-air swap during install.main— public score + alerts on regressions.guardrails-aito a specific known-good version range in the[guardrails]extra rather than a floor pin, given the 0.10.1 incident. Add a comment linking to the Socket report. (Now that fix(engine): make guardrails-ai an optional extra to unblock PyPI quarantine #642 made it optional, the blast radius is smaller, but adopters who opt in still need a safe default range.)D. Workflow-token blast radius
permissions: contents: readas the default at the workflow level for every workflow, then escalate per-job. Reduces what a compromised action can do.pull_request_targetunless we explicitly need it; preferpull_requesteverywhere.pull_request_targetruns with the target branch's secrets — the attack vector behind several recent action-compromise incidents.actions/,pypa/,astral-sh/,Idun-Group/, and an explicit allowlist. Enforced via repo settings (Settings → Actions → "Allow specified actions").Acceptance
This issue is closed when:
SECURITY.md+ org settings (org-side changes confirmed by screenshot in a comment).guardrails-aipin range in place.permissions:default + action allowlist are in repo settings.Out of scope
docs/, follow-up after attestations ship.References: #634 (Socket gates), #638 (SHA-pin + dep-audit + SECURITY.md), #641 (CVE fixes + pinact), #642 (
guardrails-aioptional extra).