Skip to content
Kareem Schultz.
Security & DeliveryGuide 14 min read

Security and CI/CD belong in the build process

A practical release pipeline: least-privilege permissions, reviewed changes, reproducible builds, deployment approvals, live checks and a way back when things fail.

Kareem Schultz

Technology enthusiast & systems builder · Guyana

Conceptual secure release pipeline flowing through tests, review, deployment and verification gates
Example delivery process with illustrative gates, not execution data from a real CI system.
Jump to a section

A green build is necessary, not sufficient

It is easy to spend most of a software project on the visible feature and treat security as a final checklist. The harder problems often appear later: who can approve a production change, how an automated job authenticates to a cloud provider, whether a failed test was skipped, and how to prove the deployed version is the version that was reviewed.

Security and continuous delivery belong together because the build pipeline is itself a privileged application. It reads source code, resolves dependencies, creates artifacts and may have permission to replace production. That access deserves the same care as any administrative system.

Select a stage of the sample release pipeline below. Each gate shows the expected evidence, a typical failure and the operational decision it should produce. This is an educational model, not a live GitHub Actions run.

Begin with the threat model for your pipeline

A typical repository contains application code, workflow definitions, dependency metadata, configuration and documentation. Some changes are routine; others can modify how CI obtains credentials or what gets deployed. Treat workflow files, dependency installers, generated artifacts and secrets-handling code as security-sensitive paths.

Separate untrusted input from privileged execution. For example, a pull request from an unknown source should not automatically run a deployment job with production secrets. Permission scopes, environment protections and event selection all matter.

OWASP and GitHub both recommend least privilege for job tokens and protecting third-party automation dependencies. Current GitHub recommends pinning third-party actions to a verified full-length commit SHA for immutable references, and keeping action dependencies up to date.

Pipeline surfaceThreat or mistakeSafeguard
Pull request codeUntrusted script reaches credentialsUnprivileged PR tests; separate deploy job
Actions dependenciesMutable dependency unexpectedly changesVerified lockfile or pinned commit references
Workflow tokenUnnecessary write accessMinimal permissions per job
Cloud credentialsLong-lived secret exposed to CIShort-lived OIDC where supported
Production deploymentWrong branch or artifact shippedEnvironment protection, artifact provenance
VerificationA green job but broken public serviceIndependent live smoke tests

A small pipeline with explicit gates

An effective pipeline is usually easier to reason about when each gate answers one question. Are the types and tests correct? Is the change reviewed? Can the exact artifact be identified? Is deployment authorized? Does the published application behave correctly? Each answer should be backed by observable evidence.

Not every team needs an elaborate release platform. A small repository can start with a test job, protected main branch, narrowly scoped deployment identity, staging where practical and a post-deployment HTTP check. Complexity is only justified when it solves a real release risk.

Illustrative test-only workflow (not a deployment)
yaml
name: Verifyon: [pull_request]permissions:  contents: readjobs:  test:    runs-on: ubuntu-latest    steps:      # Select verified action digests or a workflow lockfile.      # Install pinned runtime and dependencies using your standard action.      - name: Run validation        run: |          echo "Replace with repository-specific typecheck, tests and build"          echo "Never provide production credentials to this PR job"

Secrets are not a configuration shortcut

A password committed to a source repository may continue to exist in history after it is removed from the latest file. It should be treated as compromised and rotated. Use a secrets manager or approved deployment identity, scope credentials to the exact environment and keep values out of logs, build output and error messages.

Where supported, OpenID Connect allows a pipeline to request short-lived cloud credentials instead of maintaining a long-lived provider key. This still requires correct trust conditions: define which repository, branch or environment may request which cloud permissions.

Protect credential changes as carefully as application code changes. A job that can update a cloud role or rotate secrets has different risks from one that can only read repository content.

The exact commit and artifact matter

Suppose a branch passes tests, but another commit is added before merge. The earlier green check is not proof that the final deployed revision is safe. The stronger release record identifies the reviewed commit, the artifact built from it and the resulting deployment reference.

An artifact should not quietly change between test and deploy. Use a repeatable build and keep the build output or a trustworthy artifact reference, then deploy that artifact. If rebuilding is necessary, record that choice and verify the result.

An immutable reference and a post-deploy check are complementary: provenance tells you what was supposed to ship, while the live test checks what users can actually reach.

GateEvidence to retainFailure action
ValidationTypecheck/test/build result for final commitBlock merge or release
ReviewApproval linked to the actual diffReturn for changes
BuildArtifact identifier and source SHAReject uncertain provenance
DeployEnvironment, target and release identifierStop or rollback if unauthorized
Live checksHTTP, functional and dependency healthRollback, forward-fix or escalate

Demonstrate the difference between build and live

A build can finish while the published URL serves a previous version, a runtime environment variable is missing, or a reverse proxy cannot reach its origin. A live test should therefore examine the real domain from an independent network path.

A basic HEAD request is quick and useful, but it does not prove a complete user workflow. Combine a simple status check with a representative permitted action, a session test if relevant, and a check for browser/runtime errors. Use synthetic test data, not real customer credentials.

Non-mutating live reachability check
bash
curl --fail --location --max-time 15 -I https://app.example.com/# Investigate the resulting status, redirects and headers.# Follow with a separately authorized functional smoke test.# Compare the active release identifier with the approved source SHA.

Define rollback before a bad release

A rollback plan is most useful when it can be rehearsed. It should identify what to redeploy, which data migrations are compatible with the earlier version, and who may authorize the reversal. Deploying an old artifact after an irreversible schema migration can make the incident worse.

Use backward-compatible data changes where practical and separate destructive cleanup from initial deployment. If a change cannot be reversed safely, document a forward-fix path and a maintenance plan before production approval.

Microsoft Azure and AWS operational guidance both emphasize small reversible changes and collecting enough telemetry to respond to unexpected behavior.

Security is also a quality-of-life feature

Well-designed safeguards reduce interruptions. An operator sees why a deployment is blocked, a developer gets an actionable test failure, and a support person can identify which revision is live. Permissions are understandable, approvals have context, and audit records are useful instead of noisy.

Those are user-experience decisions as much as operational decisions. The goal is not a pipeline with the greatest number of badges; it is a release process that is predictable, accountable and possible to troubleshoot after something unexpected happens.

  • Make environment and release identity visible to authorized operators.
  • Explain which check failed and preserve its logs without leaking secrets.
  • Require review proportional to the risk of the change.
  • Record who approved sensitive actions and what revision they approved.
  • Test recovery and alert delivery as often as the happy path.

An achievable starting checklist

Begin with repository branch protection, a least-privilege workflow token and final-commit tests. Add dependency updates and scanning appropriate to the project. Protect production credentials, keep the deployment isolated from untrusted pull requests, and verify the live site with a test that can fail loudly.

Iterate from incident reviews and actual defects. A sound CI/CD process is neither a marketing badge nor a one-time setup task. It is the collection of habits that allows the team to ship responsibly again tomorrow.

Sources & further reading

These links provide the technical basis for the article. Check upstream advisories and documentation for revisions.

Read next

All articles