Most deployment-security checklists are warmed-over web application security advice. They tell you to validate input, hash passwords, and rotate keys — all true, all already covered better by OWASP's own pages. None of them address the part where the code actually moves from a developer's laptop to production: the build runner, the deploy key, the environment variable, the SSH session, the rollback path. That gap is where breaches happen.
This is an OWASP-aligned checklist written for the deployment pipeline specifically. It maps each item to the current OWASP project that owns it (Top 10 2021, ASVS 4.0.3, CI/CD Top 10, SCVS 1.0, LLM Top 10) so you can audit a real CI/CD setup against authoritative references — not a vague be more secure
essay.
If you ship code through any kind of pipeline — DeployHQ, GitHub Actions, GitLab CI, Jenkins, Argo, Spinnaker, anything — every checklist item below applies to you.
Which OWASP projects actually apply to deployments
OWASP has hundreds of projects. Most don't apply to deployment. These five do, and the rest of this checklist references them by name:
- OWASP Top 10 (2021) — the canonical web application risk list. Cited as
A01:2021…A10:2021. The 2025 refresh is in draft as of this writing; we cite 2021 because it's still the authoritative version. - OWASP Top 10 CI/CD Security Risks (2022) — the one most deployment teams have never read. Cited as
CICD-SEC-1…CICD-SEC-10. This is the deployment-specific OWASP list and the most important reference for anyone running a pipeline. - OWASP ASVS 4.0.3 — Application Security Verification Standard. Concrete, numbered, testable requirements at three levels (L1, L2, L3). Use ASVS L2 as the baseline for production deployments.
- OWASP SCVS 1.0 — Software Component Verification Standard. The SBOM and supply-chain side. If you ship containers or third-party packages, this owns your dependency hygiene.
- OWASP Top 10 for LLM Applications (2025) — covers prompt injection, training-data poisoning, and supply chain risks specific to AI-augmented systems. Relevant if your pipeline includes AI coding agents or your application runs LLM inference.
Most OWASP checklist
articles cite only the Top 10. The deployment-specific risks live in the CI/CD list — that's what we'll spend most of this post on.
Phase 1: Source control and access (CICD-SEC-1, CICD-SEC-6)
Insufficient flow control: who can push what, where
CICD-SEC-1 is the boring one nobody fixes: a developer with write access to main can push a malicious commit that triggers a deploy. No code review, no CI gate, straight to production. This is the single most common breach pattern in startup CI/CD setups.
Concrete checklist for source control:
- Require pull requests for the default branch. No direct pushes to
main,master, orproductionbranches. Enforced at the repository level, not the team norm level. - Require at least one review from someone other than the author. GitHub: branch protection rule with
Required approving reviews: 1andDismiss stale pull request approvals when new commits are pushed. GitLab:Merge requests approvalswithPrevent approval by author. - Require status checks to pass before merge. Tests, linting, security scans — all green before the merge button is enabled.
- Disallow force-push to protected branches. A force-push to
mainrewrites history and can erase the audit trail of an attacker's changes. - Sign your commits. Require GPG- or SSH-signed commits for merges to protected branches. Unsigned commits should fail status checks. This blocks attacker-controlled commits forged through stolen tokens.
Identity and access (CICD-SEC-2)
CICD-SEC-2: Inadequate Identity and Access Management covers what most teams call we'll clean up permissions later.
Specifics:
- Enforce two-factor authentication on every Git provider account. GitHub: organization-wide 2FA requirement. GitLab: instance enforcement. See how to enable 2FA on DeployHQ for the deployment side. SMS 2FA is acceptable but not preferred — use TOTP (Authenticator app) or a hardware key (YubiKey).
- Use short-lived tokens, not personal access tokens. GitHub fine-grained PATs with explicit repo scope and 30-day expiry. Avoid classic PATs with
reposcope — they grant access to every repo the user can read. - Revoke offboarded users within 24 hours. Not
at the next audit.
Build a script. Tie it to your HR system. - Audit deploy keys quarterly. This is the one almost everyone fails. See the section on deploy-key sprawl below.
Phase 2: Secrets management (CICD-SEC-6, ASVS V2)
Secrets in environment variables: the most overrated security pattern
The conventional wisdom is put secrets in environment variables, not in code.
This is correct but incomplete. Environment variables get logged. They show up in error dumps, in ps auxe, in process inspection, in third-party crash reporters (Sentry, Bugsnag, Rollbar). Once a secret enters the environment, you've lost control of its blast radius.
The checklist:
- Never
console.log(process.env)or equivalent. Filter your logger. Use an allowlist of keys it's allowed to print, not a denylist. - Sanitize crash reporter payloads. Sentry's
denyUrls,beforeSend, andignoreErrorsare not enough — the request body and headers go up by default. ConfigurebeforeSendto scrubAuthorization,Cookie, and any header matching/[Tt]oken|[Ss]ecret|[Kk]ey/. - Use a secrets manager, not
.envfiles in production. AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, or 1Password Connect..envfiles are fine in development; in production they get baked into container images and leak. For the boundary between the two, see the guide on encrypted environment variables with Dotenvx. - Rotate secrets on a schedule, not on suspicion. ASVS L2 requires rotation; the practical schedule is 90 days for API keys, 30 days for database credentials, immediately on offboarding. Tie rotation to the same script that revokes Git provider access.
- Mask secrets in CI/CD logs. GitHub Actions:
add-mask::$SECRET. GitLab:masked: trueon the variable. Verify by running a job that intentionally echoes the masked secret — if you see it in the log, the mask isn't working.
For DeployHQ users specifically, configuration files containing secrets should use the encrypted config files feature rather than being stored as raw text. The .env patterns in how to keep API keys out of your Git repository cover the source-control side; the deploy-target side belongs in a secrets manager.
Deploy-key sprawl: the silent failure mode
Every CI/CD system, every staging server, every monitoring agent, every backup script, every developer's laptop — each gets a deploy key. After two years of growth, you have 40 keys in your GitHub deploy-key list and nobody can identify what 30 of them are for.
The fix is mechanical:
- One deploy key per system, named with the system it belongs to.
deploy-prod-web-01-2026-q2, notdeploy-key-3. - Read-only by default. A web server doesn't need write access to your repo. Only your CI system does, and even there it can usually use a fine-grained PAT instead.
- Quarterly audit: delete any key whose owner can't identify it in writing. If nobody on the team can explain what
deploy-key-12is for, it's an attacker's path or a forgotten developer laptop — either way, delete it. - Use machine users instead of deploy keys for CI. A GitHub machine user with fine-grained PATs scoped to specific repos is auditable, revocable, and centralized. Deploy keys are not.
Phase 3: Build pipeline security (CICD-SEC-3, CICD-SEC-4, CICD-SEC-7)
Poisoned pipeline execution (CICD-SEC-4)
CICD-SEC-4 is the most under-reported deployment vulnerability of the last three years. The pattern: an attacker submits a pull request from a fork. The CI configuration runs on that PR. Because the CI configuration itself is part of the PR, the attacker controls what runs. Result: arbitrary code execution on your build runner, with your secrets in the environment.
The GitHub Actions variant has its own name: pull_request_target injection, sometimes called pwn-request.
If your workflow uses pull_request_target (which has access to secrets) and then checks out github.event.pull_request.head.sha (the attacker's code), the attacker has root on your runner. CVE-2023-49291 is one of many publicly disclosed cases.
The checklist:
- Never run third-party code with access to your secrets. Workflows triggered by external PRs must run without access to repository secrets. Use
pull_request(read-only token, no secrets) for forks, notpull_request_target. - Pin third-party actions by SHA, not by tag.
uses: tj-actions/changed-files@v44is a tag — the maintainer can move it to point at malicious code.uses: tj-actions/changed-files@a29e8b565651ce417abb5db7164b4a2ad8b6155cis a commit SHA — immutable. The March 2025tj-actions/changed-filescompromise affected ~23,000 repos that pinned by tag. Dependabot will help you keep SHA pins up to date. - Use the principle of least privilege for the
GITHUB_TOKEN.permissions: contents: readat the workflow level. Add specific write permissions only on the jobs that need them. The defaultwrite-allis almost always wrong. - Treat build runners as ephemeral and untrusted. Self-hosted runners running on persistent VMs are a footgun — one malicious build can leave behind a backdoor for the next build. Use ephemeral runners (fresh VM or container per job) or cloud-hosted runners.
Insecure system configuration (CICD-SEC-7)
Build agents and deploy targets need the same hardening you'd apply to a production server. For the deploy-target side specifically, see how to secure a Linux server for deployments — that post covers SSH hardening, firewall configuration, unattended upgrades, and audit logging in depth. For the build-runner side:
- Patch the base image weekly, not
when something breaks.
A Jenkins controller pinned to an LTS from 2022 is a public archive of unpatched CVEs. CVE-2024-23897 (Jenkins arbitrary file read via CLI) was exploited in the wild within days of disclosure; tens of thousands of internet-facing Jenkins controllers were vulnerable for months. - Don't expose the build controller to the public internet. Jenkins, TeamCity, Octopus — these should sit behind a VPN or zero-trust proxy. The
we only expose port 443 with auth
defense fails the moment a CVE drops authentication entirely. - Disable unused build features. Jenkins Script Console, Groovy sandbox bypass, etc. If you don't use it, uninstall it.
Ungoverned third-party dependencies (CICD-SEC-3)
This is the SCVS overlap area. The supply chain attack surface for a typical Node.js or PHP application is in the thousands of transitive dependencies — and every one of them is code you ship.
- Generate an SBOM on every build. CycloneDX (
cyclonedx-npm,cyclonedx-php-composer) or SPDX. Store it alongside the build artifact, not just in CI logs. - Scan for known CVEs at build time, fail the build on critical findings. Trivy, Grype, Snyk, GitHub Advanced Security — pick one and wire it into your build pipeline. A
criticalfinding on a transitive dependency should block the deploy. - Lockfile integrity is non-negotiable.
package-lock.json,composer.lock,Gemfile.lock,poetry.lock— committed and reviewed.npm install --frozen-lockfile(ornpm ci) in CI, notnpm install. - Treat post-install scripts as hostile.
npmpostinstallhooks have been the vector for multiple supply-chain attacks (theevent-streamincident,ua-parser-js, the recentlottie-playertyposquats). Use--ignore-scriptsin CI where you can, and audit the ones you can't disable.
Phase 4: Deployment and runtime (CICD-SEC-8, CICD-SEC-9, ASVS V14)
Insufficient logging and visibility (CICD-SEC-9)
You can't respond to what you can't see. The checklist:
- Log every deploy: who triggered it, from what commit SHA, to what target, at what time. This is the deployment audit trail. DeployHQ ships this by default; if you're rolling your own, you need to.
- Forward deploy events to your SIEM or centralized log store. Datadog, Splunk, Elastic, Grafana Loki — wherever your security team looks. A deploy at 3 AM from an unfamiliar IP should generate an alert.
- Retain deploy logs for at least 12 months. SOC 2 wants this. Most breach investigations need 90+ days of history. See SOC 2 compliance for deployment workflows for the specific control mappings.
- Sign your build artifacts. Sigstore + cosign for containers; in-toto attestations for build provenance. The deploy target should verify the signature before running the artifact.
Improper artifact integrity validation (CICD-SEC-8)
If your deploy script runs curl … | bash or pulls the latest
image without verification, an attacker who compromises your artifact storage has compromised your production environment. The defenses:
- Pin artifact versions by digest, not by tag.
myapp:latestis dangerous;myapp@sha256:a1b2c3…is safe. Same principle as pinning actions by SHA. - Verify signatures before running.
cosign verifyin your deploy step. If the signature check fails, the deploy fails. - Use immutable artifact storage. A tagged version should never be overwritten. Container registries support this via tag immutability rules — turn it on.
Rollback path: the under-tested safety net
The OWASP CI/CD list doesn't have a rollback
item, but it should. A working rollback is what makes the difference between we shipped a security regression
and we shipped a security regression for 6 hours.
The checklist:
- Every deploy must be reversible to the previous known-good state in under 5 minutes. Practiced, not theoretical. One-click rollback is a DeployHQ feature for the same reason it's an ASVS V14 control: you need it before you need it.
- Zero-downtime deployments make rollback safer. Blue-green or canary deploys mean the previous version is still running when the new one ships; rolling back is a load-balancer config change, not a redeploy.
- Test the rollback in staging quarterly. A rollback you've never tested doesn't exist.
Phase 5: Application-layer security (Top 10 2021, ASVS V5/V7/V10)
The application-layer items everyone talks about. Briefer here because the OWASP Top 10 itself is a better reference than any one blog post.
A01:2021Broken Access Control — server-side authorization checks on every request. Don't trust the client. ASVS V4 has the testable requirements.A02:2021Cryptographic Failures — TLS 1.2+ everywhere, including internal service-to-service. Modern hashing (Argon2id, bcrypt, scrypt) for passwords. Never roll your own crypto.A03:2021Injection — parameterized queries for SQL, structured logging for log injection, content security policy headers for XSS. ASVS V5 has the test cases.A04:2021Insecure Design — threat-model your architecture before you build, not after the breach.A05:2021Security Misconfiguration — disable unused features, change default credentials, harden TLS configuration. The same hardening that applies to your build runner applies to your application server.A06:2021Vulnerable Components — covered by the SCVS section above.A07:2021Authentication Failures — MFA, account lockout, password policies. ASVS V2 is the canonical reference.A08:2021Software and Data Integrity Failures — covered byCICD-SEC-8artifact integrity above. This is the SolarWinds category.A09:2021Security Logging and Monitoring Failures — covered byCICD-SEC-9above.A10:2021Server-Side Request Forgery (SSRF) — allowlist outbound destinations from your application servers. Block link-local and metadata IP ranges (169.254.169.254) at the application layer, not just the network layer.
Phase 6: AI and LLM-augmented pipelines (OWASP Top 10 for LLMs)
If your pipeline includes AI coding agents (Claude Code, Codex, GitHub Copilot Workspace, Cursor) or your application performs LLM inference, the OWASP LLM Top 10 applies:
LLM01: Prompt Injection— an attacker controlling part of a prompt (via a PR description, an issue comment, a fetched web page) can hijack the agent. If your CI agents read PR bodies and act on them, treat the PR body as untrusted input.LLM05: Improper Output Handling— nevereval()orexec()LLM output. Pipeline operators have shipped malware this way.LLM03: Supply Chain— model files are dependencies. Pin model versions. Verify checksums on download.
The patterns differ from human-written code; treat the agent as a privileged automated user with the same controls you'd apply to any service account. See running AI coding agents in CI/CD for the headless-mode setup that contains the blast radius.
How DeployHQ maps to this checklist
DeployHQ isn't a security tool, but several of its defaults align with this checklist:
- Server-side deploy execution — your source code is checked out and built in DeployHQ's infrastructure, not on the deploy target. The target only sees the finished artifact. Reduces the attack surface compared to running
git pullon production. - Audit trail by default — every deploy is logged with user, commit SHA, target, and timestamp (
CICD-SEC-9). - Role-based access control — granular permissions on who can deploy which project to which environment (
CICD-SEC-2). - SSH key management — keys are stored encrypted, scoped per project, and revocable in one click.
- One-click rollback — the recovery path described in Phase 4, built in.
- Build pipelines — your compile/test/scan steps run in a clean, ephemeral environment per deploy (
CICD-SEC-7).
If you're evaluating deployment tooling against this checklist, see how DeployHQ compares to traditional CI/CD setups or start a free trial.
The audit you should actually run
Once a quarter, sit down with this checklist and a real CI/CD setup. For each item, you should be able to answer:
- Is the control in place? (Y/N)
- How do I know? (Show me the configuration, not the team norm.)
- When was it last tested?
Items where the answer to (2) is I think Mike set that up last year
are the ones an attacker will find. Security is not the absence of we should fix that
— it's the presence of here is the configuration line that enforces it.
For questions about applying this checklist to your DeployHQ setup, email support@deployhq.com or reach us on X.