Report privately through GitHub's private vulnerability reporting on this repository. Please do not open a public issue for something exploitable. Expect a first response within a week; this is a solo-maintained project, not a funded security programme.
Two parts of drydock are worth understanding before you run it, and neither is a vulnerability — they are what the tool does.
The board serves file contents from the STATE_HOME it is pointed at.
plugin/board/server.py binds 127.0.0.1 only, and exposes a fixed
allowlist of item files (SPEC.md, QUESTION.md, DELIVERABLE.md,
RUN.md, REJECTION.md, REPORT.md, REVIEW.md, READY.md) plus the
static frontend. There is no authentication, because there is no network
exposure to authenticate. Putting it behind a tunnel, a reverse proxy, or a
0.0.0.0 bind turns a local dashboard into an unauthenticated file server
for your queue — including whatever your specs and run logs happen to quote.
STATE_HOME (~/.drydock by default) is never given a git remote.
/drydock:install refuses to proceed if one is already configured there,
and every skill and contract that touches STATE_HOME treats a remote
appearing later as a hard stop. This is deliberate: STATE_HOME is where your
specs — which can quote internal systems, credentials-adjacent context, or
anything else your teammates shouldn't read — accumulate as git history.
Before this was a separate, remote-less directory, /drydock:install cloned
the drydock tool itself and the same clone doubled as the queue; a git push
from that clone, whether by habit or by the orchestrator's own housekeeping,
could have sent that history to whatever remote the clone happened to have —
including this project's own, if you'd also cloned it to contribute a fix.
Get durability without a remote via git bundle create <path> --all to
storage your own backup policy already covers.
The orchestrator dispatches agents that write to your repositories,
unattended. Executors run as background Claude Code sessions with
--permission-mode bypassPermissions, because a background session has nobody
to answer a permission prompt. That flag does not widen what drydock may do:
each executor is a separate Claude Code process that loads your
configuration — your settings.json permissions, your PreToolUse hooks,
your deny rules — and drydock never routes around them. Your permission
policy is the boundary. If you do not have a deny layer you trust, run
executors with --permission-mode acceptEdits instead and accept that some
runs stall on prompts. A stalled run is recoverable; an unreviewed mutation
against live infrastructure is not.
What follows from that: a specification is an instruction to an agent with
your credentials. Treat STATE_HOME/specs/inbox/ the way you would treat a
shell script someone handed you — reviewing a spec before it is dispatched is
the control, and it is why /drydock:spec writes specs rather than executing
them.
Path traversal or allowlist escapes in plugin/board/server.py; anything
that lets the board serve a file outside the item directory it was asked
for; a contract path that would cause an executor to act outside a spec's
declared blast radius.
That the board has no authentication on 127.0.0.1. That executors run with
elevated permissions. That a specification can instruct an agent to do
something destructive — that is the adopter's permission policy and review
pass, described above.