Use GitHub's private vulnerability reporting on this repository. That reaches the maintainer without the report being public first.
Please do not open a public issue for something that lets an agent escape a restriction porta claims to enforce. Anything else — a crash, a wrong error message, a confusing default — is fine in the open.
There is one maintainer and no service-level commitment. You will get an acknowledgement, and if the report is valid, a fix and credit in the release notes unless you would rather not be named.
porta's claims are the invariants in CLAUDE.md, and what porta does and does
not defend against is written out in the threat model,
with each claim mapped to the test that keeps it honest. A report is interesting
if it breaks one of them. The short version:
- A command cannot write outside the mounts it was granted, or
/tmpand/dev. - Under
--read-policy strictit cannot read outside those mounts and the platform's own directories. - Under proxy mode the loopback proxy is its only egress — no UDP, no Unix
sockets, no
io_uring— and the proxy serves only a client presenting this run's credential, never tunnels to loopback, link-local, metadata or multicast addresses, and records every decision before acting on it. - The child's environment holds nothing of the caller's shell beyond
PATH,HOME, the locale and the terminal unless-eor--env-passnamed it. - What the preset (
native/presets/default.toml, shown byporta explain) closes stays closed: credential stores to reads in every mode, the credential agents' sockets toconnect, and inside a writable mount the repository hooks and config and the trusted files at the root, which cannot be written or renamed away. On Linux the sockets and the mount protections need the command's mount namespace; without one the run says what stays open. - On macOS another process's arguments, the Keychain and
open(1)are closed. - Under
--allow-netno UDP leaves: on Linux an internet socket must be TCP. - A WASM agent's tool arguments are validated against the declared schema before anything executes, and a guest cannot grant itself a capability, choose a credential, or bypass a failed completion check.
- Model credentials stay in the host and are never handed to a guest.
- A restriction porta cannot enforce refuses the run rather than narrowing it.
These are known and documented rather than undiscovered. A report that porta is not a container is accurate and not news.
- porta is not a VM. Restrictions apply to a process on your kernel. A kernel privilege escalation defeats them, and defeats them for a container too. Report those upstream.
- Under
strict, the/etcfiles a command needs to start are readable. On Linux they are granted one by one — the loader cache, the resolver configuration, the trust store,passwd,localtime— andshadow,sudoersand the host keys are not among them. On macOSsandbox-execgrants/private/etcwhole; file permissions separateshadowthere, which is one reason porta refuses to run as root. - Reads are open by default.
--read-policy strictis opt-in in 0.6.x. - The default policy is not a secret store. Without
strict, an agent can read what your user can read, minus what the preset closes. A credential store the preset does not name is readable; add it with--deny-read. - With the network open, a child may listen and may reach Unix sockets.
Listening is closed only once
--allow-netnames a port, and reopened per port by--allow-bind. The credential sockets the preset names are closed; on Linux only those bound when the run starts, and only where the host allows unprivileged user namespaces. - Inside a writable mount on a Linux host without user namespaces, nothing in particular is protected. Landlock grants a directory whole; the run says so.
- Name resolution under
--allow-neton Linux goes over TCP. A resolver that ignoresRES_OPTIONS=use-vccannot resolve there; proxy mode can. - A tool you granted can do what you granted it. porta checks arguments against a schema, not intent.
Fixes go to the latest release. 0.6.x is the current line; there are no backports to 0.5.x.