Foundry is a control plane that decides whether AI agents may act on your code. Vulnerabilities in it are, by definition, supply-chain-shaped: please report them privately.
Use GitHub's private vulnerability reporting on this repository (Security tab → "Report a vulnerability"). Please include reproduction steps and the impact you believe the issue has on the policy/approval guarantees.
We aim to acknowledge reports within 72 hours.
Anything that lets work bypass the governance loop, including:
- approving or dispatching a run without a configured approver's action (auth bypass on the approval surfaces);
- satisfying a sensitive-area approval requirement without the configured role (privilege escalation);
- forging or replaying Linear/GitHub webhooks past signature verification;
- getting an agent to act on paths the policy forbids, or evading the diff-aware risk escalation;
- secrets reaching an agent prompt past the dispatch guard;
- tampering with the audit trail (artifact/decision hashes).
Foundry fails closed by design, but you still need to deploy it sensibly:
- Set strong values for
FOUNDRY_LINEAR_WEBHOOK_SECRET,FOUNDRY_GITHUB_WEBHOOK_SECRETandFOUNDRY_API_TOKEN. Without any API credential the REST approval endpoint is disabled (this is intentional). - OIDC API auth (optional). Instead of (or alongside) the static
FOUNDRY_API_TOKEN, you can front the token-gated API with your IdP by settingauth.oidc(issuer/audience/jwks_uri). A bearer JWT is then accepted only if its signature verifies against the IdP's JWKS and itsiss/aud/exp(with bounded clock-skew leeway) check out. The signing algorithm allow-list defaults to RS256 only — keep it asymmetric so a token can never be accepted by presenting the public JWKS key as an HMAC secret (alg:none/ HS-confusion are refused). All three OIDC settings are required together; a partial config fails closed at startup. - OIDC approver binding + IdP-group → role mapping (optional). When a REST
approval is authenticated via OIDC, the approver identity is taken from
the verified token (
auth.oidc.subject_claim, defaultemail, falling back tosub), not the request body — so a token holder cannot approve as someone else; a bodyuserthat disagrees with the verified subject is refused. The approver's roles are the committedapproval.approversgrant for that verified identity, unioned with the roles the verifiedauth.oidc.group_claimmaps to through the committedauth.oidc.group_role_map. Crucially, the group→role mapping lives in reviewed, committed YAML; only the cryptographically-signed identity/group claims come from the token. A caller still cannot self-assert a role — the map is config — and the policy gate's role requirements are unchanged, so a group that grants the wrong role still cannot approve sensitive work. The static-token path is unchanged: identity is the bodyuser, roles come fromapproval.approvers, and the IdP-group map plays no part. - Dashboard browser login / SSO (optional). When the browser-login parts
are configured (
auth.oidcclient_id/authorization_endpoint/token_endpoint/redirect_uri) plus the env-only secretsFOUNDRY_OIDC_CLIENT_SECRETandFOUNDRY_SESSION_SECRET, an operator can sign in to/dashboardthrough your IdP (OAuth2 authorization-code with PKCE) instead of pasting an API token. The flow enforces a CSRFstate, an OIDCnonce(anti-replay) and PKCES256, and verifies the returned id_token with the same hardened verifier (audience = the client id). Success mints a signed session cookie (HttpOnly,SameSite=Lax,Secureunlesscookie_secure: falsefor local HTTP) carrying only the verified subject — it is HMAC-signed for integrity and expiry, not encryption, so nothing secret is stored in it. The session cookie authenticates the dashboard's read calls; it is deliberately rejected on the approval endpoint, so a cookie a browser sends automatically can never be tricked (CSRF) into driving an approval — approvals still require a bearer token or a signed webhook. KeepFOUNDRY_SESSION_SECRETsecret and rotate it to invalidate all live sessions. Optionally configure RP-Initiated (federated) logout (auth.oidcend_session_endpoint, + an optional IdP-registeredpost_logout_redirect_uri):/dashboard/logoutthen ends the IdP SSO session too, not just the local Foundry cookie — without it, logging out of Foundry leaves the IdP session live, so a shared/kiosk workstation silently re-authenticates on the next visit. The logout URL carriesclient_id(in lieu of anid_token_hint, so no id_token is stored client-side) and is built only from committed config, so it is not an open-redirect surface. The local session cookie is cleared either way. - Terminate TLS in front of the API; webhook signatures authenticate payloads, not transport.
- Keep the approver → roles mapping in reviewed, committed YAML.
- To encrypt artifact payloads at rest, set
FOUNDRY_ARTIFACT_ENCRYPTION_KEY(a Fernet key; install thecryptoextra). Treat it like any data-encryption key: store it in your secrets manager, never in YAML, and rotate by listing the new key first and the old one after it, comma-separated (the new key encrypts; both decrypt). The content hashes that protect audit integrity are computed over plaintext, so they remain verifiable with or without the key — but losing the key makes encrypted payloads unrecoverable, and reads of encrypted rows fail loud if the key is removed. - Treat
FOUNDRY_JIRA_WEBHOOK_SECRETas an approver-level credential. Jira webhooks carry no HMAC signature over the body, so the approver identity is taken from the payload (comment.author.emailAddress). Anyone holding the shared token can therefore assert any configured approver's email and approve sensitive work as them. Scope and rotate the token accordingly. Foundry accepts the token from theX-Foundry-Webhook-Tokenheader only; query-string delivery (?token=, which leaks into access logs, proxies, and link history) is off unless you explicitly settracker.jira_allow_query_token: true. The GitHub PR webhook falls back to the Linear signing secret whenFOUNDRY_GITHUB_WEBHOOK_SECRETis unset, so one leaked secret can cover both providers — set a distinct GitHub secret in production.