GitHub Actions workflows often need access to API keys, database passwords, access tokens, deployment credentials, and other sensitive information. These credentials allow automated workflows to build applications, publish packages, deploy services, and communicate with external systems.
The problem starts when a workflow accidentally prints sensitive information in its logs.
For example, a developer might enable debug output to investigate a failed deployment. A command may print environment variables, an API response, or an authentication header. If that output contains a credential, anyone with access to the workflow logs may be able to view it.
GitHub provides secret scanning to identify exposed credentials. Its secret scanning capabilities can also detect supported secrets in GitHub Actions workflow logs where log scanning is available and enabled.
This article explains how workflow log scanning works, how to prevent accidental exposure, how to investigate secret scanning alerts, and what to do when a credential appears in a workflow log.
Why Secrets Appear in GitHub Actions Workflow Logs
Workflow logs are useful for troubleshooting. They show which steps executed, which commands failed, and what output was generated.
However, logs can also expose sensitive information when workflows are not designed carefully.
Consider a deployment workflow that retrieves an access token from a secret:
name: Deploy Application
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Deploy
env:
API_TOKEN: ${{ secrets.DEPLOYMENT_TOKEN }}
run: |
./deploy.sh
The workflow passes the deployment token through an environment variable. This is preferable to hardcoding the credential in the workflow file.
However, the deployment script could still expose the token if it prints environment variables or includes sensitive information in an error message.
Common situations that can expose credentials include:
Printing environment variables during debugging.
Enabling verbose output in deployment scripts.
Logging HTTP request headers.
Printing database connection strings.
Displaying authentication responses.
Passing credentials through command-line arguments.
Using third-party actions that produce unsafe debug output.
The important point is that storing a credential as a GitHub Actions secret does not guarantee that every possible representation of that credential will remain hidden.
Understanding GitHub Secret Scanning for Workflow Logs
GitHub secret scanning identifies credentials that match supported detection patterns.
Traditionally, developers use secret scanning to find exposed credentials in repository content and Git history. GitHub also provides workflow log scanning where the capability is available and enabled for the enterprise.
When configured, GitHub scans completed workflow logs for supported provider-specific secret patterns.
If a matching credential is detected, GitHub creates a secret scanning alert associated with the repository. The alert can include a reference to the workflow log location where the credential was detected.
This helps security teams investigate an exposure without manually inspecting every workflow run.
There are some important limitations:
Workflow log scanning must be available and enabled for the enterprise.
Secret scanning must also be enabled for the relevant repositories.
The workflow log scanner detects supported provider patterns, not every possible sensitive value.
Generic patterns, custom patterns, and AI-detected secrets are not included in workflow log scanning.
Values already masked by GitHub Actions are not detected in the logs.
Previously completed workflow logs are not automatically scanned when the feature is enabled.
Therefore, workflow log scanning should be treated as an additional security layer rather than a replacement for secure workflow design.
Enable Secret Scanning for Workflow Logs
The availability of workflow log scanning depends on your GitHub plan and enterprise configuration.
For supported GitHub Enterprise Cloud environments, an enterprise owner can manage the setting through enterprise security policies.
The general configuration process is:
Open the enterprise account settings.
Navigate to Policies and then Advanced Security.
Find the setting for secret scanning in GitHub Actions workflow logs.
Enable the policy for the repositories that should be covered.
Verify that secret scanning is enabled for those repositories.
The exact settings available depend on the enterprise configuration and product entitlement.
After enabling the capability, review the repository's secret scanning alerts to investigate findings from workflow logs.
Do not assume that enabling repository secret scanning automatically enables every additional scanning capability. Check the relevant security settings separately.
Prevent Secrets From Appearing in Workflow Logs
Finding exposed credentials is important, but preventing them from appearing in the first place is better.
GitHub Actions automatically redacts GitHub secrets when they are printed to workflow logs. However, automatic redaction is not guaranteed for transformed values, generated credentials, or structured data.
There are several ways to reduce this risk.
Use GitHub Actions Secrets
Never store credentials directly inside workflow files.
Instead, create repository, organization, or environment secrets and reference them through the secrets context.
For example:
steps:
- name: Deploy application
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
./deploy.sh
This keeps the credential value outside the workflow definition.
Use environment-level secrets when credentials should only be available to particular deployment environments.
For example, production deployment credentials should not automatically be available to every development workflow.
Avoid Printing Environment Variables
A common debugging mistake is printing all environment variables:
- name: Debug environment
run: printenv
This can expose sensitive information if environment variables contain credentials or other confidential values.
Instead, print only the information required for troubleshooting:
- name: Check deployment configuration
run: |
echo "Deployment environment: production"
echo "Deployment configuration loaded"
If you need to verify whether a variable exists, check its presence without printing its value.
- name: Verify API token
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
if [ -z "$API_TOKEN" ]; then
echo "API token is missing"
exit 1
fi
echo "API token is configured"
This confirms that the token is available without exposing its contents.
Mask Dynamically Generated Secrets
Sometimes a workflow generates a credential during execution.
For example, a deployment script might generate a temporary access token or a signed authentication value.
GitHub cannot automatically recognize every generated value. You should register sensitive values for masking before using them in commands that might produce log output.
GitHub Actions supports the add-mask workflow command.
- name: Generate temporary token
id: token
run: |
TEMP_TOKEN=$(./generate-token.sh)
echo "::add-mask::$TEMP_TOKEN"
echo "token=$TEMP_TOKEN" >> "$GITHUB_OUTPUT"
The add-mask command instructs the runner to redact that value if it appears in subsequent log output.
Register the value before printing it or using it in another workflow command.
If you transform a credential, such as encoding it with Base64 or URL encoding, register the transformed value separately when it is sensitive.
Masking reduces accidental exposure, but it should not be used as a reason to print credentials.
Investigate Secret Scanning Alerts
When GitHub detects a secret, the repository's secret scanning alert list provides a place to investigate the finding.
A useful investigation process includes the following steps.
Identify the exposed credential
Open the secret scanning alert and review the available information.
Check:
Which provider or credential type was detected.
Which workflow run contains the finding.
Which job or log location is associated with the alert.
Whether the credential belongs to a development or production environment.
Whether the credential is still active.
Avoid copying the exposed value into an issue, pull request, or another log while investigating.
Determine how the credential was exposed
Inspect the workflow definition and the relevant log output.
Look for commands that print environment variables, verbose HTTP requests, deployment configuration, or authentication responses.
Also review the scripts and third-party actions used by the affected workflow.
The goal is to identify the source of the exposure so that the same problem does not happen in another workflow.
Revoke or rotate the credential
If a real credential has been exposed, treat it as compromised.
Revoke or rotate it through the service that issued it. Update the corresponding GitHub Actions secret with the replacement value.
For example, if a deployment token was exposed, generate a new token with the minimum permissions required and remove the old token.
Do not assume that deleting the workflow log makes the credential safe again. Someone may already have accessed or copied it.
Remove the exposed log when appropriate
After rotating the credential, follow your organization's incident response process to remove the exposed workflow logs where possible.
Also inspect other workflow runs and repositories that may have used the same credential.
The most important action is revoking the exposed credential. Removing the log is an additional cleanup step, not a substitute for rotation.
Understand the Limitations of Automatic Secret Detection
Secret scanning is valuable, but it cannot identify every form of sensitive information.
Consider the following example:
- name: Print encoded credential
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
ENCODED_TOKEN=$(printf '%s' "$API_TOKEN" | base64)
echo "$ENCODED_TOKEN"
The workflow prints an encoded representation rather than the original token.
Because the value has changed, automatic redaction may not recognize it as the original GitHub secret.
Other examples include:
Splitting a token into multiple parts.
Encoding credentials before printing them.
Embedding credentials inside JSON or XML.
Generating temporary authentication values.
Printing secrets through third-party tools.
GitHub's automatic redaction is based on matching known sensitive values. It is not a guarantee that every transformed representation will be removed from logs.
This is why developers should avoid logging sensitive information even when secret scanning and automatic masking are enabled.
Additional Security Practices for GitHub Actions
Secret scanning works best when combined with other workflow security practices.
Use Least-Privilege Permissions
Give each workflow only the permissions it needs.
For example:
permissions:
contents: read
If a workflow needs to publish a release or update repository information, grant the additional permissions only to the relevant job.
Avoid granting broad write access to every workflow by default.
Use OpenID Connect for Cloud Authentication
If your cloud provider supports OpenID Connect, consider using it instead of storing long-lived cloud credentials as GitHub secrets.
With OIDC, GitHub Actions can request a short-lived identity token that a configured cloud provider can exchange for temporary credentials.
This reduces the need to maintain long-lived access keys.
Review Third-Party Actions
A workflow may expose secrets through an external action even when the workflow itself does not print them.
Before using an action, review its source code, permissions, maintenance status, and the way it handles inputs.
For sensitive workflows, pin third-party actions to a full commit SHA rather than relying only on a movable version tag.
Review Debug Logging
Debug logging can provide useful information, but it also increases the amount of data written to workflow logs.
Enable it only when necessary and review the output before sharing logs with others.
Summary
GitHub Actions workflow logs can expose API keys, access tokens, database credentials, and other sensitive information when commands or scripts print confidential values.
GitHub secret scanning can help identify supported credentials in workflow logs where the capability is available and enabled. However, its detection scope has limitations, and automatic masking cannot guarantee protection for every transformed or generated value.
Use GitHub Actions secrets, avoid printing environment variables, mask generated credentials before they are used, and apply least-privilege permissions. If a credential appears in a workflow log, revoke or rotate it immediately and investigate how it was exposed.
The most effective approach is to prevent sensitive information from entering workflow logs rather than depending entirely on detection after exposure.
Join the conversation! Your thoughts help the community grow.