What actually happens to your code when you connect Parry.
A security scanner that touches your source code is itself a security boundary. This page is the boundary's spec — written for the engineer reviewing it before signing, not for the marketing funnel. If a claim isn't here, we don't make it.
Where your code lives, second by second
- 01
Webhook arrives
GitHub posts the push event to /v1/webhooks/github. Parry verifies the HMAC, enqueues a scan task on Redis. No code has crossed yet.
- 02
Worker mints a fresh install token
GitHub App installation token, scoped to one repo, valid for one hour. Used for git clone only. Never written to disk; never logged.
- 03
Hardened git clone
Cloned into a per-scan workspace dir on the worker filesystem. Clone runs with protocol allowlists, symlinks disabled, submodules off, no credential prompts. See git flags below.
- 04
Sandboxed fan-out
Each scanner runs as its own docker container. Workspace mounted read-only. No network egress by default. No Linux capabilities. Resource caps. See sandbox flags below.
- 05
Findings persisted, raw reports stored
Findings (rule, severity, title, matched-line snippet) and raw scanner JSON (≤8MB each) land in Postgres in your tenant. Used for lifecycle, SARIF export, audit evidence.
- 06
Workspace destroyed
os.RemoveAll on both the workspace dir and the artifacts dir. The full repo is gone from disk within seconds of scan completion. Only the durable rows above remain.
Every scanner runs with these flags
Scanners parse attacker-controlled repo content and have CVE history (parser RCEs in semgrep, gitleaks, others). Without sandboxing, a single parser bug is a 10-minute window of free compute and outbound network. The flags below turn that into: ten minutes of CPU and memory only, no privileges, no kernel attack surface, no network.
--cap-drop=ALLStrips every Linux capability. The container runs with the absolute minimum privilege set.
--security-opt=no-new-privilegesBlocks setuid escalation. A breached process can't gain more rights than it started with.
--network=none (default)No DNS, no routing, no outbound. A parser-RCE inside a scanner cannot phone home, exfiltrate, or call C2. The eight scanners that genuinely need outbound (vuln databases, package registries, GitHub API, DAST target) opt in explicitly.
--memory=2g --pids-limit=512 --cpus=2A runaway scanner is bounded — can't fork-bomb, can't eat all RAM, can't starve other scans.
--read-only workspace mount (:ro)Your code is mounted into the container as read-only. The scanner sees it; nothing inside the container can modify it.
Per-tool 10-minute timeout + docker kill on cancelA hung scanner can't run forever. On timeout or cancel, the orchestrator issues a targeted docker kill so the container actually stops (not just the docker CLI).
What protects the worker from a malicious repo
git clone itself has been a CVE surface (CVE-2022-24765 and relatives). A hostile repository can ship .gitattributes, .gitmodules, or symlinks that exploit the clone process before any scanner sees the workspace. Parry sets the following on every clone:
protocol.allow=builtinRefuses ext::, file::, and similar pseudo-protocols an attacker URL might smuggle.
protocol.file.allow=neverRefuses local-path protocol on fetch, even mid-clone.
core.symlinks=falseSymlinks defined inside the repo are ignored — they can't escape the workspace.
--no-recurse-submodulesAn attacker .gitmodules can't pull additional repositories from elsewhere.
GIT_TERMINAL_PROMPT=0git refuses to prompt for credentials. A manipulated URL can't hang the worker waiting for input.
Stored after the scan
- Findings (rule, severity, title, fingerprint)Postgres, your tenant
Lifecycle tracking — open / fixed / suppressed. Without this, every scan is a re-triage.
- Code snippets — only the matched linesPostgres, your tenant
Rendered in the finding drawer so you can decide without leaving Parry. Not the full file.
- Raw scanner reports (JSON, ≤8MB each)Postgres, your tenant
Required for SARIF re-export, AI Review re-runs, scanner regression debugging, compliance audit evidence.
- CycloneDX SBOM per scanPostgres, your tenant
Hand to procurement. One-click download from the scan page.
Removed within seconds
- The cloned source code
Workspace dir is os.RemoveAll'd within seconds of scan completion. The full repo never leaves disk for longer than the scan itself.
- Your install token
Used only inside one scan, then discarded. Never written to disk; only forwarded into containers via env.
- Files outside the matched lines
Only the lines a scanner flagged become code_snippet rows. Everything else is dropped with the workspace.
What crosses, when, and with what redaction
- When does code cross the LLM boundary?
- Only when the org owner has explicitly opted into AI Review for that org. Off by default.
- What gets sent?
- The diff under review, plus surrounding context lines from the changed files. Whole repos are not sent.
- What is stripped before transmission?
- Detected secrets (sourced from gitleaks output) and .env file contents are redacted in-process before the payload reaches the model. The redaction is unit-tested in internal/llm/redact_test.go.
- Which model provider?
- Configured per-deployment. Org owners see which provider is in use before opting in.
- Are model providers training on your code?
- Parry uses provider APIs configured for no-training (zero-data-retention where the provider supports it). The opt-in screen names the provider so you can verify their current policy yourself.
Where Parry runs and who can reach your data
- Region
Single-region today: Hetzner, Frankfurt (EU). Source code, findings, and raw reports never leave EU infrastructure. Multi-region is on the roadmap, no committed date.
- Tenant isolation
Every API path scopes by org id at the handler layer. memberships.role is a typed enum (owner / admin / member); destructive actions (billing, ruleset edits, finding acceptance) require the matching role. Open-mode (AUTH_DISABLED) is dev-only and refuses to start unless bound to loopback.
- Transport
TLS everywhere — webhooks, OAuth, API. Sessions are 32-byte random tokens; the database stores the SHA-256 hash so a snapshot leak can't replay live sessions.
- Audit trail
Mutations (suppress, accept, ruleset edits, billing changes, role grants) write to an audit_log table the owner can inspect via /v1/audit. Read-event logging and tamper-evidence are roadmap (Phase 5.6).
The honest gap list
If your security policy depends on any of these, do not sign with us yet. Each item is on the roadmap; none are shipped.
- Single-tenant / customer-cloud (BYOC) deployment
Roadmap (Phase 5.4). For organisations that cannot allow source code to touch shared infrastructure, this is the only honest answer — and it isn't shipped yet. Don't sign with us before this lands if your security policy requires it.
- Multi-region
Single-region today (Frankfurt, Hetzner). EU-only deployment is achievable today; a multi-region or US-region deployment is not. Roadmap, no committed date.
- Automatic data retention policy
No nightly purge job today. Findings, scans, raw reports, and audit log accumulate. We will add per-org retention controls (default 90 days, configurable) before charging for retention as a feature. Roadmap (Phase 6.4).
- SOC 2 / ISO 27001 certification
Not yet. The controls many auditors check for (RBAC enforcement, audit log, hardened sandbox, no public open-mode, /readyz) are in place; the formal report is not. Engagement-mode buyers should treat this page as the security questionnaire response in lieu of the report.
- Image pinning by digest
Scanner images are pinned by version tag, not by digest. A registry compromise of a pinned tag would be undetected. Roadmap (Phase 1.8 follow-up).
Verify in source
The claims on this page reduce to specific files and tests. If you're reviewing Parry for procurement, point your engineer at these:
- internal/scan/runner/orchestrator.go — sandbox flags
- internal/scan/runner/orchestrator_test.go — flag regression test
- cmd/worker/scan.go — git clone hardening
- cmd/api/access/access.go — RBAC gates
- internal/llm/redact_test.go — secret redaction proof
- migrations/0021_member_role_enum.sql — typed role column
- cmd/worker/artifacts.go — what persists, what doesn't
- cmd/api/main.go — /readyz, AUTH_DISABLED loopback enforcement
Ask for a procurement walkthrough at hello@parryai.dev.