Capa is a programming language whose design centres on security properties. If you find a way that the language fails to deliver on those properties, a way to bypass the capability discipline, escape attenuation, or compromise the analyzer, please report it.
For a consolidated map of what the toolchain verifies unconditionally
(fail-closed), what is best-effort (fail-open), what it trusts as a
premise, and what is outside the threat model, see
docs/trust-model.md.
Please do not open a public GitHub issue for security reports.
Use GitHub's private vulnerability reporting channel:
- Go to https://github.com/nelsonduarte/capa-language/security/advisories/new
- Describe the issue in as much detail as you can:
- Affected version (
git rev-parse HEAD) - A minimal
.capareproduction, if applicable - Expected behaviour vs. what actually happens
- Why you believe it is a security issue (which property is broken)
- Affected version (
- Submit. Only repository maintainers will see the report.
You can also email nelson.duarte31@gmail.com with the subject line
[capa security]. PGP is not currently set up.
I aim to acknowledge reports within 7 days and to provide a detailed response within 30 days. Capa is a personal project; I will be transparent if a fix takes longer.
In scope:
- Compilation succeeds for a program that violates the capability discipline (e.g., a function performs IO without declaring the required capability, or aliases a capability across two arguments).
- Attenuation is bypassed at runtime: a capability constrained with
restrict_to(host)reaches a host it should not. - The
consumequalifier is bypassed (a value is used after consumption). - A way to obtain a built-in capability without it being a function
parameter (other than through
Unsafe/ Python interop, which is explicitly out of scope of the discipline by design). - Compilation accepts a program where a
@secretvalue reaches a public sink that the analyzer should reject: an information-flow / noninterference violation (for example under@strict_ifc). Capa's information-flow control is a first-class, machine-checked security property (cross-function and per-field IFC, implicit-flow enforcement under@strict_ifc), backed by the Agdalambda_ifnoninterference proof. The analyzer itself is not formally verified; the proof is over thelambda_ifmodel, so a soundness gap between the analyzer and that model is in scope. - Crash or arbitrary code execution in the analyzer / transpiler when
given a malformed
.capainput. While Capa is not yet positioned as a sandbox for untrusted source, defensible behaviour matters. - A vendored dependency under
./vendor/whose code no longer matches the commitcapa.lockfroze is read and built without detection. Both ends are verified:capa installenforces the locked SHA (and GPG / SLSA when averify_keyis set), and the build path (capa --check/--run/--transpile,capa migrate,capa test) re-verifies each git dep before readingvendor/<name>, fail-closed, on two conditions: its HEAD must equal the locked commit and its working tree must be clean at that commit. The working-tree check matters because an in-place edit of a checked-out file leaves HEAD matching the lock while changing the code the build reads. What this does not catch, by stated premise, is an attacker who adulteratesvendor/<name>, commits the change, and rewrites the committedcapa.lockto match: the lockfile is part of the project's trusted computing base. The build-time check is the post-install tamper guard; it is bypassed only by the explicitCAPA_NO_VERIFY=1opt-out, which annuls the guarantee by design and is therefore not a vulnerability.
Out of scope:
- A program that legitimately receives a capability and uses it maliciously. Capa narrows where authority can hide; it does not audit what holders of authority choose to do.
- Attacks that require uses of the
Unsafecapability orpy_import. The Python interop boundary deliberately exits the discipline. - Issues with third-party Python packages used at build time.
- Theoretical issues in the type system that have no concrete attack.
Capa is at version 1.0 and is a one-person project. Only the latest tagged release is supported for security fixes. I may publish patch releases for the latest minor when a fix is significant.
| Version | Supported |
|---|---|
| 1.0.x | yes |
| < 1.0 | no, please upgrade |
Dated advisories are kept in docs/advisories/.
The 2026-05-25 audit record lives at the repository root in
security-audit.md.
docs/advisories/2026-06-15-soundness.md: four linear / typestate affinity fixes (use-after-consume, anonymous drop,var/ re-assignment, partial consume inmatch) and two information-flow fixes (@secretfield read and destructure both dropped the label), shipped in1.2.0under the security exception.docs/advisories/2026-06-16-soundness.md: two information-flow fixes (a@secretlaundered through amatch/ifvalue or a capturing closure, and a captured-secret closure passed to a higher-order function that invokes it and sinks the result), one linear-affinity fix (double-consume via alias or captured closure), and two manifest fixes (provably_excluded_capabilitiesfalsely excluded a capability reachable through a closure hidden in a struct field or a sum-variant payload), shipped in1.3.0under the security exception.docs/advisories/2026-06-17-security.md: capability-attenuation and enforcement fixes (Proc.restrict_tofixed the binary identity not just the basename,Db.allowscanonicalises throughrealpath, and aDbopen re-validates the kernel true path to close a symlink TOCTOU), information-flow and constant-time fixes (a reassignment laundered a@secretin the default tier, a@constant_timefunction admitted a short-circuiting secret string / list compare, and an early return under@strict_ifcleaked the predicate bit and across a function boundary), encapsulation fixes (field access through an abstract-capability / trait receiver, andUnsafehidden in a capability-bearing struct), manifest fixes (provably_excluded_capabilitiesfalsely excluded a capability reachable through a cap-bearing struct, a nested field, or a sum-variant payload, plus a multi-module provenance / SBOM digest and stable single-file identifiers), and supply-chain fixes (GPG anchored on the primary key,file://traversal incl. percent-encoding, fail-closed registry index) plus aparse_intDoS, shipped in1.4.0under the security exception.
I will coordinate public disclosure with the reporter. A typical flow:
- Reporter submits via the channel above.
- Maintainer acknowledges and triages.
- Maintainer prepares a fix on a private branch.
- A GitHub Security Advisory is drafted with the reporter as a collaborator.
- The fix is merged and tagged; the advisory is published the same day.
- Reporter is credited in the advisory (unless they request otherwise).