Why
The runner is the highest-risk component. Current defenses are good, but:
client/remote.go:15does not require SSH host-key verification; a MITM or looseknown_hostscan feed a runner malicious jobs.ci/agent/worktree.go:64usesjob.RepoandrunIDdirectly infilepath.Joinwithout validation.ci/agent/agent.go:796(publishSite) usesjob.Pages.Pathdirectly; a compromised/buggy server could point it at../../.job.SHA,job.Ref,job.Tag,job.Branch, artifact names, and step metadata are not all re-validated runner-side.ci/agent/agent.go:906(logBuffer) anduploadLogkeep entire logs in memory/disk with no cap.- Artifacts have no per-run total/count cap beyond workflow limits.
- All contributors share one cache namespace under
contrib.
Scope
- Require strict SSH host-key verification for runners by default; add a known-hosts flag and an explicit test-only override.
- Validate every job field on the runner before use.
- Share pages/artifact validation between server and runner.
- Reject paths containing
:, NUL, or newlines before building--volumearguments. - Cap job logs, artifact count, total artifact bytes, and per-run disk usage.
- Separate contributor caches by account or tip.
- Keep image builds rootless and pinned by digest; document build-time network access.
- Add a test asserting
container.RunArgsnever contains--privileged,--cap-add,/var/run/docker.sock, or unexpected host mounts.
Acceptance
- A malicious job payload with
repo=../../...,pages.path=../../..., a bad SHA, or a symlink artifact is rejected/contained. - Runner memory/disk stays bounded under a noisy job.