A secret does not have to be committed to a source file to become a security problem.
It can also appear in a GitHub Actions workflow log.
This can happen when a script prints an environment variable, a command includes a token in its output, a tool logs configuration details, or an error message accidentally exposes a credential. The problem is that workflow logs are often treated as temporary debugging information, while they can contain the same sensitive values that developers normally work hard to keep out of source code.
GitHub has added secret scanning for GitHub Actions workflow logs as a public preview. When enabled, GitHub scans the logs of new completed workflow runs for supported provider patterns and creates secret scanning alerts when it finds a matching secret. Existing workflow logs are not scanned retroactively.
This changes an important part of how teams should think about CI/CD security.
The goal should not be to rely on GitHub finding secrets after they are printed. The better approach is to prevent secrets from reaching logs in the first place and use secret scanning as another layer of protection.
Why GitHub Actions Logs Can Expose Secrets
A GitHub Actions workflow often handles credentials that applications need during deployment.
For example:
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
The secret itself is stored in GitHub rather than directly in the workflow file. That is the right approach.
The problem begins when a command prints the value:
- name: Debug deployment
run: |
echo "Deploy token: $DEPLOY_TOKEN"
The workflow can still complete successfully.
But the secret has now been written into the workflow log.
Anyone who has access to that log may potentially see information that should never have been exposed.
The same problem can happen indirectly.
For example, a deployment command might fail and print its arguments:
- name: Deploy
run: deploy-tool --token "$DEPLOY_TOKEN"
Even though the developer did not explicitly write an echo command, the external tool could include command information or configuration details in its output.
This is why secret handling needs to consider the entire workflow, not just the secrets section of the YAML file.
What GitHub Is Scanning
The new workflow-log scanning capability is different from ordinary secret scanning.
GitHub already scans repository content for supported secret patterns, including source code and other supported repository content. The new capability extends detection to GitHub Actions workflow logs.
When the feature is enabled, GitHub scans new workflow-run logs after the workflow completes.
The important limitation is that this is not a historical cleanup mechanism.
Workflow runs that completed before the feature was enabled are not automatically scanned.
That means teams should not enable the feature and assume that every secret ever printed in an old Actions log will suddenly appear as an alert.
If there is already a reason to believe that a workflow has exposed credentials, the team should investigate those older logs separately.
What Happens When a Secret Is Found
When GitHub identifies a supported secret in an Actions workflow log, the finding appears in the repository's secret scanning alerts.
The alert can link back to the workflow log and identify the location where the secret was detected. A single alert can also reference multiple locations when the same secret appears in several workflow runs or jobs.
This is useful because developers do not have to manually search through every workflow execution.
The important security step, however, is what happens after detection.
If the exposed value is a real credential, treat it as compromised.
Do not simply delete the log line and close the alert.
The credential should be rotated or revoked according to the provider's security process.
Removing evidence of the leak does not make the credential safe again.
Secret Scanning Is Not a Replacement for Masking
GitHub Actions already masks supported secret values in workflow logs in many situations.
That does not mean teams should deliberately print secrets and depend on masking.
Consider:
- name: Deploy
run: |
echo "$DEPLOY_TOKEN"
Even if the value is masked in the normal log output, this is still poor workflow design.
There are several reasons.
First, masking depends on GitHub recognizing the value as a secret.
Second, masking is not a general solution for every form of sensitive information.
Third, a command may transform the value before printing it.
For example:
echo "$TOKEN" | base64
The resulting output is no longer the original token string.
A security control designed to recognize the original value may not provide the protection you expect.
The better rule is simple:
Do not write secrets to logs unless there is an exceptional and well-understood reason to do so.
What Teams Should Change in Workflow Files
The first practical step is to review existing workflows.
Search your workflow files for commands that print environment variables or sensitive configuration.
For example:
- run: echo "$TOKEN"
- run: printenv
- run: env
- run: ./deploy.sh "$API_KEY"
- run: cat config.json
Not every occurrence is automatically a vulnerability.
The important question is whether the command can expose sensitive values.
A workflow that runs:
- name: Show environment
run: env
should receive particular attention.
Environment variables can contain much more than developers expect, especially when workflows inherit variables from jobs, environments, or action configuration.
A debugging command that seemed harmless during development can become a credential exposure point in production CI.
Be Careful With Shell Debugging
Shell debugging can create another problem.
For example:
- name: Debug
run: |
set -x
./deploy.sh "$DEPLOY_TOKEN"
Shell tracing can print commands as they execute.
That means values passed as command arguments can potentially appear in the log.
Debugging options should therefore be treated carefully in workflows that handle credentials.
If detailed command tracing is needed, isolate it from commands that process sensitive values.
Do not enable verbose debugging for an entire deployment job just because a single command is failing.
Check Third-Party Actions Too
Your workflow may not be the only thing writing to the log.
A third-party GitHub Action can produce log output as well.
For example:
steps:
- uses: some-company/deploy-action@v1
with:
token: ${{ secrets.DEPLOY_TOKEN }}
The token is passed to the action, but your workflow does not completely control what the action does internally.
A poorly designed action could print configuration values, command arguments, or error information.
This is one reason to treat third-party actions as part of your software supply chain.
Before adding an action that handles credentials, review:
- What inputs it accepts
- Whether it documents secret handling
- What it writes to logs
- Which permissions it requests
- Whether the action is maintained
- Which version or commit you are using
Passing a secret to an action means you are trusting that action to handle it correctly.
Review Workflow Permissions Too
Secret exposure is only one part of the problem.
A workflow should also receive only the permissions it needs.
For example:
permissions:
contents: read
is preferable to granting broad repository permissions when the workflow only needs to read source code.
The principle is similar to database permissions or cloud IAM:
Give the workflow the smallest set of permissions required to perform its job.
If a secret is accidentally exposed, limiting the workflow's permissions can reduce the potential damage.
This does not make the leaked secret safe, but it can reduce the blast radius around the workflow.
Use GitHub Secrets Instead of Hardcoding Credentials
A common mistake is putting a credential directly into workflow YAML:
env:
API_KEY: "abc123..."
This creates an immediate source-control problem.
Even if the value is removed later, it may already exist in repository history or in copies of the repository.
Use GitHub Actions secrets instead:
env:
API_KEY: ${{ secrets.API_KEY }}
GitHub's push protection can also block supported secrets from being pushed into protected repositories, helping prevent credential leaks before they reach the repository.
Secret scanning and push protection serve different purposes.
Push protection tries to stop supported secrets before they enter the repository.
Secret scanning looks for secrets that have already reached supported locations.
Workflow-log scanning adds another place where exposed credentials can be detected.
Together, these controls provide different layers rather than one replacement for another.
Do Not Assume Every Secret Will Be Detected
This is an important limitation of the current workflow-log scanning preview.
GitHub's Actions workflow-log scanning does not detect every possible form of secret.
The current preview scans for provider patterns. It does not detect generic patterns, custom patterns, AI-detected secrets, or values that GitHub Actions has masked in the log.
For example, your company may use an internal credential format such as:
COMPANY-KEY-9f82a7...
If that value is not covered by the supported detection scope, you should not assume that workflow-log scanning will find it.
This is why secure workflow design remains necessary even after enabling the feature.
A security tool should be treated as a detection layer, not permission to handle secrets carelessly.
Alerts Do Not Currently Replace Manual Monitoring
Another detail teams should understand is how alerts behave during the public preview.
GitHub states that secret-scanning alerts from Actions workflow logs do not generate notifications during the preview. Teams need to review the repository's secret scanning alerts directly.
That changes how security teams should monitor the feature.
If your organization enables the feature but nobody reviews the resulting alerts, the detection capability may not lead to timely remediation.
A practical process could look like this:
Workflow runs
|
v
Actions log scanning
|
v
Secret scanning alert
|
v
Security review
|
v
Credential rotation
|
v
Workflow cleanup
The alert is only the beginning of the process.
How to Handle a Secret Found in an Actions Log
Suppose GitHub reports that a real API credential appeared in a workflow log.
Do not start by deleting the workflow.
Handle the credential first.
Step 1: Determine Whether the Secret Is Real
Check whether the detected value is:
- A real production credential
- A test credential
- An expired credential
- A false positive
If it is a real active credential, treat it as exposed.
Step 2: Rotate or Revoke It
The safest response is to invalidate the exposed credential and issue a replacement.
For example, if the secret belongs to a cloud provider, use that provider's credential-management process.
Do not wait until after the workflow has been cleaned up.
Step 3: Find Where It Came From
Check the workflow file, scripts, third-party actions, and commands that ran during the affected workflow.
Find the exact step that printed the value.
Step 4: Fix the Workflow
Remove the command or change it so sensitive values are not written to logs.
Step 5: Search for Other Occurrences
If the same credential was used in several workflows, search for all references.
One exposed credential may have been used by multiple deployment jobs.
Step 6: Review the Root Cause
Ask why the secret reached the log in the first place.
Was it:
A debugging command?
A deployment tool?
A third-party action?
An error message?
A shell trace?
An environment dump?
A generated configuration file?
Fixing the underlying cause is more valuable than closing a single alert.
A Better GitHub Actions Pattern
Instead of exposing deployment information through logs, keep the secret as an input to the command that needs it.
For example:
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Deploy application
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: ./scripts/deploy.sh
The workflow does not print the token.
The deployment script receives it through the environment and uses it where required.
The script should follow the same rule.
Avoid code such as:
echo "Token: $DEPLOY_TOKEN"
and avoid dumping the complete environment during troubleshooting.
If debugging is required, log safe information instead:
echo "Deployment started"
echo "Target environment: production"
echo "Application package created"
The goal is to make the log useful without making it a copy of your application's confidential configuration.
Review Existing GitHub Actions Workflows
Teams should review workflows that handle:
- Cloud credentials
- Deployment tokens
- Package registry credentials
- Database credentials
- Signing certificates
- API keys
- SSH credentials
- Infrastructure credentials
Start with deployment and release workflows because these usually have access to the most sensitive credentials.
Then review reusable workflows and shared actions.
A secret may be passed through several workflow layers before it reaches the command that actually uses it.
The repository structure might look simple:
.github/
workflows/
build.yml
test.yml
deploy.yml
release.yml
But the effective workflow can be much larger when reusable workflows and third-party actions are involved.
Review the complete execution path.
What Teams Should Do Now
The most useful response is not to wait for a secret-scanning alert.
Start with the workflows that already have access to production credentials.
Check whether they contain:
echo of secret variables
environment dumps
shell tracing
verbose deployment commands
temporary debugging code
third-party actions handling secrets
generated configuration files
Then enable the new workflow-log scanning capability where your GitHub plan and enterprise configuration support it.
For enterprise administrators, GitHub provides an enterprise policy for enabling or disabling secret scanning of Actions workflow logs. The policy is disabled by default during the public preview.
After enabling it, remember that only new workflow runs are scanned.
Do not treat enabling the feature as a replacement for reviewing older logs.
A Simple Security Checklist
Before your next production deployment, check the following:
| Area | What to Check |
|---|
| Workflow YAML | No hardcoded credentials |
| Secrets | Use GitHub secrets for sensitive values |
| Logs | Do not print secret values |
| Shell debugging | Avoid tracing commands that handle secrets |
| Environment | Do not dump the complete environment |
| Third-party actions | Review how actions handle credentials |
| Permissions | Give workflows only the access they need |
| Push protection | Enable it where appropriate |
| Secret scanning | Enable supported secret detection |
| Actions log scanning | Enable it where available |
| Alerts | Review secret scanning alerts regularly |
| Incident response | Rotate exposed credentials immediately |
This checklist is useful even if you never receive a secret-scanning alert.
The best result is still that the secret never reaches the log.
Common Mistakes
Assuming GitHub Masks Everything
Masking helps, but it should not be treated as a license to print secrets.
Design workflows so sensitive values are never intentionally written to logs.
Assuming Secret Scanning Finds Every Secret
Actions workflow-log scanning currently focuses on supported provider patterns and has limitations during the public preview.
Ignoring Third-Party Actions
An action that receives a secret can potentially produce log output using that secret.
Review the actions in security-sensitive workflows.
Cleaning the Log Instead of Rotating the Credential
If a real credential was exposed, deleting the output is not enough.
Rotate or revoke the credential first.
Enabling the Feature and Forgetting About It
During the current preview, workflow-log secret alerts do not generate notifications. Teams need to review the secret scanning alert list directly.
Summary
GitHub Actions logs can contain the same sensitive credentials that teams try to protect in source code.
Secret scanning for Actions workflow logs adds another detection layer, but it should not replace safe workflow design. Keep secrets out of logs, limit workflow permissions, review third-party actions, enable push protection, and rotate any real credential that reaches a log.
Join the conversation! Your thoughts help the community grow.