Security

Three permissions, a container with no network, and a list of what that misses.

Credda clones code it did not write, reads issue text written by strangers, and executes that repository’s own build and test commands. All three are hostile input.

Every figure below was read out of the engine repository, with the file and date beside it. Not an audit summary; there has not been one.

01What it asks for

Read the code, comment on one issue, name the job. That is the whole list.

On the Action: no webhook endpoint, no hosted service, no credential beyond the job’s own token. Three scopes, one of which writes. The app is a second path with a different grant; /install compares them.

core/action/action.ymlgranted by the calling workflow · read 2026-08-29
Permissions the Credda Action requires, with what each is used for.
ScopeUsed forWhy it is needed
contents: readCheck out the repository under test.Credda cannot run code it cannot see.
issues: writePost one comment on the issue that triggered the run.How a run reports back, and the only write Credda performs against a repository today.
id-token: writeMint a short-lived OIDC token that identifies the workflow to Credda.How the engine is fetched: the repository is private, so the download is exchanged for a token GitHub signs about your job. It grants nothing over yours.
One source, shared with /docs and /welcome.

And what a default Action install does not grant

A scope a workflow has not granted cannot be spent. Two of the three below are what opening a pull request from this path would cost; the app and worker path asks for those two openly at install time. The third is the boundary on both paths.

  • contents: writeNot in a default install, and that is a default rather than a limit. The fix stage writes a patch inside its own disposable checkout, and a pinned install ends at the report that carries the diff. This is one of the two scopes the Action’s open-pull-request input needs. That input IS declared at the published v1 tag and on the default branch, and it is off unless you turn it on — so granting these two scopes in your own workflow and setting it to true is what opens the pull request, on your own GITHUB_TOKEN. The hosted worker path opens one on a proven verdict with no flag at all.
  • pull-requestsNot in a default install. This is the second scope that input needs, granted on your own GITHUB_TOKEN in your own workflow file. Credda supplies no credential to that path, and no path merges.
  • actions, packages, deploymentsNever requested. Credda does not merge and does not deploy.

The blast radius of a wrong answer is a diff you decline. Credda holds no merge function, and a committed test fails if one ever appears. On a pinned install neither write scope above is granted: the patch is authored and verified inside the sandbox and handed back in the report. What arrives.

02Where the commands run

A container that has its network taken away before your code runs.

Reproducing a failure means running the repository’s own test and build commands, written by whoever opened the pull request. Under the Action they run in a Docker container with every network interface removed once provisioning returns. A run that then reaches for a registry fails, and Credda records that as its own failure, not as the defect.

core/docs/security/posture.mdsection 3.2 · probed 2026-08-23
Probes run inside a live Credda sandbox container and their results.
ProbeResultWhat it establishes
iduid=1000(node) gid=1000(node)Not root.
/proc/self/statusCapEff: 0, CapBnd: 0, NoNewPrivs: 1Every Linux capability dropped, and no path to regaining one.
/proc/net/devlo onlyNo network interface exists at all. Not a firewall rule, an absence.
fetch('https://example.com')EAI_AGAINName resolution fails because there is nothing to resolve over.
/sys/fs/cgroup/memory.max21474836482 GiB, enforced by cgroup.
/sys/fs/cgroup/pids.max512A fork bomb hits a wall.
env11 variables, none a credentialA test suite that prints its own environment finds nothing worth stealing.
the filesystema docker volumeNo host path is bind mounted, so no host path is reachable.
The kernel's answers from inside a live container, not the flags passed to it.

Every row above except the network row is common to both paths. The probed repository had no dependency manifest, so no install ran and the container was created with no network from the start. Where a repository does have dependencies, the container is attached for the install and every interface removed before provisioning returns, read from source and covered by a test that was not run on that machine.

03Untrusted text

A repository can try to talk to the model. It cannot set the verdict.

A README saying to ignore previous instructions, a test printing a fake system prompt, a dependency description carrying orders: all of it reaches Credda, because reading it is the job. External text is fenced into user messages. Nothing external is interpolated into a system prompt.

Defence in depth, not a guarantee: no known technique makes a language model immune to injection. The control that holds is structural. A verdict is a function over executed signals, not an opinion, so persuading the model proves nothing. A successful injection:

  • cannot grant itself tools
  • cannot reach a credential worth stealing
  • cannot change what the exit code was

Injection-shaped patterns are counted and surfaced rather than deleted: redaction would corrupt real source, and a file discussing prompt injection is not an attack.

04What is not covered

The part of this page a security review should read first.

Copied from the engine repository’s own list of known gaps as it stood on 2026-08-24.

  • The app receives a copy of your code. The Action does not.

    Everything else here describes the Action: the engine is fetched into your own runner and Credda receives no copy of your source. On the app path Credda clones the repositories you select onto its own infrastructure, under an installation token Credda holds rather than your workflow’s. Neither path merges anything.

  • The command line defaults to your host, not to the container

    The Action asks for the docker plane and refuses to start on a runner that cannot provide it. The CLI does not. Its default plane runs a repository’s own commands as the same operating system user, on the same machine, as Credda itself: containment against accident, not against intent.

  • Installing dependencies is the one window with a network

    When a repository needs an install, the container is created attached to a network, the install runs, and only then is every interface removed. Dependency lifecycle scripts execute inside that window, and no registry allowlist bounds it. Everything after it is offline.

  • Provisioning starts a root container

    Loading the repository into the volume starts a second, short-lived container as root with no capabilities dropped. It runs two commands on arguments Credda built, then is removed. It is still a root container in the provisioning path.

  • A shared kernel is not a virtual machine

    This is container isolation. A kernel exploit from inside the container is not contained by anything described on this page.

  • Redaction is a bound on damage, not a control

    Command output is scrubbed for credential-shaped text before anything durable is written. The scrubber knows the shapes it was taught and nothing else: a bare high-entropy string with no prefix or keyword passes through unchanged. What keeps Credda’s own credentials safe is the environment allowlist, which is structural: a repository process cannot print what it was never given.

  • None of this has been attacked

    No prompt-injection corpus has been run against the agents, no escape attempted from a sandbox container, no penetration test performed on any surface. Every boundary here is a design read out of code and probed once. None has held under attack.

05Reporting

If you find one, write to a person.

A way to make Credda modify a repository it should have left alone, or report a finding it never established, is a security issue. Both are wanted.

Where to send it
security@credda.io. Include the affected component, reproduction steps and impact.
GitHub private reporting
Not a route to us. The engine repository is private, so its Security tab cannot be opened from outside our organisation, and the public repositories publish no security policy. Use the address above.
Disclosure
Please give a reasonable window for a fix before publishing. No bounty is offered and none is implied.

The full threat model, data-flow trace and plane-by-plane comparison are in the engine repository as SECURITY.md and docs/security/posture.md, unreachable from outside our organisation. Ask and they will be sent.