Skip to main content

GitHub Actions Security

Skylos can scan GitHub Actions workflow files as part of --danger.

This means the same CLI run that catches application security issues can also review CI/CD configuration in:

  • .github/workflows/*.yml
  • .github/workflows/*.yaml
  • action.yml
  • action.yaml

Use this when you want pull requests to flag workflow changes that affect tokens, secrets, runners, release jobs, containers, or third-party action code.

Run The Scanner​

From the repository root:

skylos . --danger

For a normal PR gate, combine workflow security with secrets and quality checks:

skylos . --danger --secrets --quality --gate

For changed-code review:

skylos . --danger --secrets --quality --diff origin/main

Workflow findings appear in the same Security Issues output as application security findings.

What Skylos Catches​

Skylos focuses on CI/CD patterns that are high-signal in code review.

RiskExamples
Privileged triggerspull_request_target, workflow_run
Broad token permissionsmissing permissions, write-all, workflow-level write scopes
Mutable third-party codeunpinned uses: references and reusable workflows
Checkout credentialsactions/checkout without persist-credentials: false
Template injectionattacker-controlled GitHub contexts expanded in run, action inputs, or container options
Runner trustself-hosted runners and dynamic runs-on expressions
Mutable containersjob containers, services, Docker actions, docker pull, and docker run without digest pinning
Secret exposuresecrets: inherit, toJSON(secrets), dynamic secret lookup, broad secret env scope
Release riskOIDC jobs invoking repo-controlled build or release scripts
Artifact and package risklax artifact uploads, package installs that run lifecycle scripts, missing timeouts
AI agents in CIclaude-code-action, codex-action, run-gemini-cli, ai-inference or agent CLIs that untrusted issues, comments or fork pull requests can steer

See Rules Reference for the full rule list.

Example Finding​

name: CI
on:
pull_request_target:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "${{ github.event.pull_request.title }}"

This workflow can produce findings for:

  • privileged pull_request_target execution
  • missing explicit token permissions
  • unpinned action reference
  • checkout credentials left available for later steps
  • attacker-controlled pull-request title expanded directly in shell

Safer Baseline​

For untrusted pull requests, keep the workflow unprivileged:

name: Skylos Security Gate

on:
pull_request:
branches: [main]

permissions:
contents: read
pull-requests: write

jobs:
skylos:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- uses: actions/setup-python@42375524d7b3f331cfa0bf3d4e21d0d3e1df3630
with:
python-version: "3.12"

- run: pip install skylos

- run: skylos . --danger --secrets --quality --gate

The SHA values above are examples. Pin actions to the full commit SHA you have reviewed.

AI Agents In Workflows​

AI coding agents in GitHub Actions follow instructions in the text they read. On events that anyone can start, that text is attacker-written. Skylos reports the combination that makes this exploitable, and names the path:

name: Issue triage
on:
issues:
types: [opened]

jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
claude_args: --allowedTools "Bash,Read,Write,Edit"
prompt: "Triage: ${{ github.event.issue.title }}"
SKY-D351 CRITICAL issues (anyone who can open an issue) → claude-code-action
(the prompt interpolates github.event.issue.title) with allowed_non_write_users: "*",
which lets any account past the action's write-access check; the agent has shell
commands (Bash in --allowedTools) and secrets ANTHROPIC_API_KEY; ...
SKY-D352 HIGH ...
SKY-D353 HIGH ...
RuleReports
SKY-D350No actor gate in front of an agent that has no built-in check (run-gemini-cli, claude-code-base-action, ai-inference, agent CLIs)
SKY-D351The action's built-in write-access check widened with "*"
SKY-D352Any-command shell access on an untrusted trigger
SKY-D353Event text interpolated into the prompt
SKY-D354An agent action or CLI version with a published advisory

What Skylos checks before reporting:

  • Who can start it. Event types that need triage access (labeled, assigned) are trusted. if: conditions on the job, its needs chain and the step are evaluated per event: author_association of the triggering account in OWNER, MEMBER or COLLABORATOR, an exact actor, a same-repository pull request check, or an earlier collaborator-permission check all gate the agent. claude-code-action, codex-action and the opencode action check write access themselves unless that check is widened.
  • Whether attacker text reaches the agent. The action loads the triggering thread, the prompt or an earlier step passes event text, the agent has a tool that reads issues or pull requests, or the job checks out the pull request's code. A fixed prompt over the base branch is not reported.
  • What the agent can do. Shell, tools that change things, read-only tools, or none. Agents with no tools are not reported. Gemini CLI 0.39.1 and later ignores workspace settings and turns off YOLO approval unless GEMINI_CLI_TRUST_WORKSPACE is set, so those runs count as read-only.
  • What the job holds. Secrets, write scopes, undeclared token permissions or id-token: write.

A fixed environment: name is mentioned in the message rather than treated as a gate: required reviewers are a repository setting the scan cannot see.

Release And Deploy Jobs​

Release workflows deserve stricter review because they often have access to:

  • package publishing tokens
  • cloud OIDC federation
  • repository write permissions
  • production secrets
  • signed artifact or provenance steps

Skylos flags release-like workflows that combine sensitive permissions with repo-controlled scripts or mutable state.

Example:

permissions:
contents: read
id-token: write

jobs:
publish:
runs-on: ubuntu-latest
steps:
- run: ./scripts/build.sh
- run: ./scripts/publish.sh

The scanner reports this because local build and release scripts become part of the OIDC trust boundary. A safer design separates build/test work from the credential-minting publish step.

Suppressions​

Use suppressions only when the workflow risk is intentional and reviewed.

Inline suppressions work on the same line or the line above the finding:

# skylos: ignore[SKY-D292] internal action is pinned by organization policy
- uses: actions/checkout@v4

For repo-wide policy, use the normal Skylos configuration and suppression workflow described in Configuration.

Limitations​

GitHub Actions scanning is static analysis. It does not execute workflows, resolve every dynamic expression, or audit remote action source code.

It is strongest for review-time guardrails:

  • spotting dangerous workflow structure
  • making token and secret scope visible
  • catching mutable action/container references
  • highlighting risky release automation

Use it alongside GitHub branch protection, environment protection rules, action pinning policy, and code-owner review for workflow changes.

Other CI Providers​

Skylos runs in Jenkins, GitLab CI, Bitbucket Pipelines, Azure Pipelines, CircleCI, Buildkite, and other CI systems as a pull request gate. skylos cicd init sets it up for the CI your repository uses; see Other CI Providers.

The workflow-configuration scanner on this page covers GitHub Actions. Skylos also checks .gitlab-ci.yml (SKY-D314 to SKY-D323). It does not parse Jenkinsfile, CircleCI, Bitbucket Pipelines, Azure Pipelines, or Buildkite configuration for CI/CD misconfiguration rules.