GitHub Actions is not just automation. It is part of your software supply chain.
Your workflows decide:
- which code gets executed
- which tokens and secrets are available
- which packages get installed
- which artifacts get published
- which cloud credentials can be minted
That makes workflow security a pull-request concern, not only a platform checklist.
Skylos scans GitHub Actions workflows alongside application code so risky CI/CD changes show up before merge.
What Skylos Checks In GitHub Actions
Skylos looks for workflow patterns that are easy to miss in review but serious when combined with secrets, publishing, or untrusted pull requests.
Privileged Triggers
Some events execute with more trust than developers expect.
Skylos flags risky triggers such as:
pull_request_targetworkflow_run
These are not always wrong. They become dangerous when the workflow checks out or executes code influenced by an untrusted contributor.
on:
pull_request_target:
jobs:
test:
steps:
- uses: actions/checkout@v4
- run: npm test
The safer pattern is to keep untrusted pull-request execution separate from jobs that can write to the repository, access secrets, or publish artifacts.
Broad GITHUB_TOKEN Permissions
GitHub Actions permissions should be scoped to the job.
Skylos flags broad permissions such as:
permissions: write-all- workflow-level write permissions that apply to every job
- jobs that request powerful scopes without a clear need
permissions: write-all
For most analysis jobs, start small:
permissions:
contents: read
Then add only the permission the job actually needs.
Unpinned Actions And Reusable Workflows
Tags such as @v4 are convenient, but they are still moving references.
Skylos flags unpinned uses: references so reviewers can see where the workflow depends on mutable external code.
steps:
- uses: third-party/example-action@v1
For sensitive workflows, pin third-party actions to a full commit SHA.
steps:
- uses: third-party/example-action@2d3f7a9d1b4c0a6e9f8b7c5d4e3a2f1b0c9d8e7f
This matters most for release, deploy, package publishing, and permission-bearing workflows.
Unsafe actions/checkout Defaults
actions/checkout persists credentials by default unless configured otherwise.
Skylos flags checkout steps that omit:
with:
persist-credentials: false
That setting is especially important when a job later runs third-party code, dependency scripts, or commands that do not need repository write credentials.
Template Injection In Shell And Action Inputs
GitHub expression interpolation can turn untrusted event data into shell input.
Skylos flags risky use of event-controlled values in places such as:
run:commands- known command-like action inputs
- container and service options
- run: echo "${{ github.event.pull_request.title }}"
The safer pattern is to pass untrusted values through environment variables and quote them carefully inside the shell.
Self-Hosted And Dynamic Runners
Self-hosted runners are powerful because they can reach internal systems. That also makes them risky.
Skylos flags jobs that use:
self-hosted- dynamic runner labels
- expression-derived runner selection
runs-on: ${{ inputs.runner }}
Dynamic runner selection should be treated as sensitive configuration, especially in reusable workflows.
Unpinned Containers And Docker Images
Workflows often pull Docker images directly through:
- job containers
- service containers
docker://actionsdocker pulldocker run
Skylos flags image references that are not pinned by digest.
container:
image: node:latest
Prefer digest-pinned images for sensitive jobs:
container:
image: node@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Secret Exposure And Inheritance
Secrets should be scoped to the smallest surface area possible.
Skylos flags patterns such as:
- reusable workflows with
secrets: inherit - broad secret env variables at workflow or job scope
- dynamic secret lookup with
secrets[...] toJSON(secrets)- jobs that use secrets without an environment boundary
jobs:
deploy:
secrets: inherit
Secret inheritance is convenient, but it can make a reusable workflow much harder to reason about.
Unsafe Writes To $GITHUB_ENV And $GITHUB_PATH
Writing untrusted data to GitHub's environment files can affect later steps in the same job.
Skylos flags unsafe writes such as:
- run: echo "NAME=${{ github.event.issue.title }}" >> "$GITHUB_ENV"
Treat environment-file writes as privileged behavior. Keep untrusted values out unless they are normalized first.
GitHub App Token Misuse
GitHub App tokens are powerful when they can write repository content, open pull requests, or trigger workflows.
Skylos flags risky actions/create-github-app-token usage when token permissions or repository scoping are too broad for the job.
The scanner cannot know every organizational policy, but it can make dangerous defaults visible during review.
Spoofable Actor Checks
Workflows sometimes trust users by checking string values such as:
if: contains(github.actor, 'bot')
Skylos flags spoofable actor checks because usernames and bot-like labels are not a strong authorization boundary.
If a job controls releases, secrets, or repository writes, use explicit allowlists and platform permissions instead.
Release Jobs With OIDC And Repo-Controlled Scripts
OIDC is better than long-lived cloud secrets, but it still creates a credential minting path.
Skylos flags jobs that request:
permissions:
id-token: write
and then invoke repo-controlled release or build scripts.
That does not mean the job is wrong. It means the script becomes part of the trust boundary and deserves careful review.
Package Installs That Run Lifecycle Scripts
JavaScript package managers can run lifecycle scripts during install.
Skylos flags install commands that do not disable scripts in workflows where dependency execution increases risk.
- run: npm ci
Safer baseline:
- run: npm ci --ignore-scripts
When a workflow truly needs lifecycle scripts, that exception should be intentional.
Missing Timeouts On Privileged Jobs
Privileged jobs should not run forever.
Skylos flags sensitive jobs that omit timeout-minutes, especially release or deploy jobs.
jobs:
publish:
runs-on: ubuntu-latest
steps:
- run: npm publish
Add an explicit timeout:
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
Why Scan Workflows And Code Together
Workflow-only scanning catches CI/CD misconfiguration.
Code scanning catches application bugs.
Real incidents often need both views:
- a workflow runs on the wrong trigger
- the job has broad token permissions
- the job installs untrusted dependencies
- the repository contains a dangerous release script
- a pull request changes both workflow and code in one diff
Skylos is designed for that combined review path. The goal is to show the reviewer when a pull request changes the security properties of the repository, not just whether a single file has a known bad pattern.
How To Run It
Run Skylos locally first:
pip install skylos
skylos . --danger --secrets --quality
Then add it to CI as a pull-request gate:
name: skylos-security
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Skylos
run: pip install skylos
- name: Scan repository
run: skylos . --danger --secrets --quality --gate
For Python-specific setup, use:
What To Fix First
Prioritize findings in this order:
- Privileged triggers that execute untrusted code
- Broad write permissions on pull-request workflows
- Release jobs with secrets, OIDC, or package publish access
- Unpinned third-party actions and containers
- Secret inheritance or workflow-level secret exposure
- Dependency installs that execute lifecycle scripts
- Missing timeouts and unsafe artifact behavior
That order keeps attention on the paths where a workflow can write, publish, or mint credentials.
Where Skylos Fits
If you already use a dedicated GitHub Actions linter, keep it.
Skylos is useful when you want one review signal for:
- workflow security
- Python security
- secret exposure
- dead code
- AI-generated code regressions
- CI gate enforcement
That makes it a good fit for teams that want GitHub Actions security checks and application security checks in the same pull request workflow.