A CI job executes repository code with network access, package scripts, and automation tokens. If the job can also publish packages, modify source, or deploy production, one compromised dependency or untrusted contribution can cross from code execution into a supply-chain incident.
Least privilege means each job receives only the permissions, environment, and lifetime needed for its one task. It also requires a recovery plan because scoped credentials can still be stolen and used within their scope.
Map credentials to individual jobs
Inventory every credential available to the workflow: platform token, cloud role, package registry token, signing identity, SSH key, webhook secret, database credential, and third-party service token. For each one, record:
- operations it permits;
- repositories, packages, accounts, and environments it reaches;
- which event and job receive it;
- issue time and expiry;
- storage and masking behavior;
- audit trail and revocation path.
Split build, test, publish, and deploy into separate jobs. A unit-test job should not inherit the package publishing token merely because a later job needs it. Job separation is useful only if credentials and network permissions are separated too.
Set the platform's default workflow token to read-only or no permissions, then grant explicit capabilities at job scope. On GitHub Actions, the documentation for GITHUB_TOKEN shows permissions can be adjusted with the permissions key. Confirm the effective permission set in repository and organization settings because defaults can vary by configuration.
Keep secrets away from untrusted execution
Pull requests, forks, generated code, dependency install scripts, test fixtures, and build tools can execute attacker-controlled instructions. Do not expose privileged secrets to a job that checks out and runs untrusted code.
Separate the trusted release workflow from the contribution workflow. Build an artifact in a restricted job, record its digest, and promote that same artifact through an authorized job rather than rebuilding arbitrary branch content with production credentials.
Be cautious with workflow events that run code from a contribution in the context of the target repository. Evaluate the exact platform semantics, checked-out revision, and secret availability. A naming convention such as “PR validation” is not a security boundary.
Self-hosted runners retain additional risk: filesystem state, process isolation, network position, and machine credentials can survive a job. Prefer ephemeral workers for untrusted code and verify that destruction actually removes the execution environment.
Prefer short-lived federated identity
Long-lived cloud access keys in repository settings create a broad theft window. Where the provider supports it, exchange the CI platform's signed workload identity for a short-lived role scoped by repository, branch or environment, workflow, audience, and operation.
Validate every claim used by the trust policy. Trusting any repository in an organization or any branch can be much broader than intended. Keep session duration short enough for the job and do not allow role chaining to bypass the original restriction.
Federation removes one stored secret but introduces reliance on the CI identity issuer, cloud trust policy, and token audience validation. Monitor role assumptions and retain a revocation path for compromised workflows.
Protect privileged environments
Production deployment and package publication should require stronger conditions than ordinary tests:
- an immutable reviewed source reference;
- required checks completed on that reference;
- environment-specific authorization;
- concurrency control to prevent overlapping deployments;
- artifact digest verification;
- protected branch or tag policy;
- bounded manual approval when risk warrants it.
An approval is meaningful only if the approver can see the artifact, source, target, and checks being authorized. Do not ask someone to approve a mutable branch name while the job later resolves a different commit.
Environment protections should delay credential issuance until the gate passes. Injecting a secret at workflow start and merely postponing its use leaves it available to earlier compromised steps.
Treat third-party actions and build tools as code
A workflow dependency can read the workspace and any credentials available to its process. Review its source and permissions, minimize its inputs, and pin it to an immutable commit when the platform supports that control. GitHub's secure use reference recommends full-length commit pinning as the immutable form for actions.
An immutable pin limits unexpected upstream changes; it does not prove the pinned code is safe. Update pins through review with release notes, source diffs, and tests. Apply the same scrutiny to package-manager hooks, downloaded binaries, container images, and reusable workflows.
Keep credentials out of command-line arguments when process listings or debug logs can expose them. Disable shell tracing around sensitive operations and pass secrets through the provider's supported protected channel.
Prevent disclosure through output and artifacts
Masking is a backup control, not permission to print secrets. Derived values, encoded forms, multiline credentials, and short substrings may bypass automatic masking.
Use structured allowlisted logs. Do not upload environment dumps, home directories, credential stores, cloud configuration, or broad workspace archives as debugging artifacts. Set artifact retention and access according to sensitivity.
On failure, return a sanitized reason and correlation identifier. Store privileged provider diagnostics in an access-controlled system rather than the public build log.
Test denial and incident recovery
Verify that the test job cannot publish, the staging job cannot deploy production, and a forked contribution cannot obtain secrets. Test expired tokens, incorrect branch claims, missing approval, changed artifact digest, and concurrent deployments.
Prepare response steps: disable the workflow, revoke or narrow the role, invalidate registry tokens, identify runs within the exposure window, inspect published artifacts, rotate affected trust material, and communicate impacted releases. Deleting a log line does not revoke a credential already copied.
Least privilege is demonstrated by denied operations and bounded recovery, not by a permissions file that looks restrictive. Every job should be able to explain why it has each capability and what the system does if that capability is abused.
Was this guide useful?
Your rating helps us prioritize clearer, more practical technical content.
Review diffs, run checks, and prepare the release in Arezgit.
Keep Git review, security scanning, API checks, database inspection, and release preparation together in one local desktop application.
Explore Arezgit