Skip to content

sisaku-security/sisakulint

Repository files navigation

sisakulint

Before moving on, please consider giving us a GitHub star ⭐️. Thank you!

sisakulint logo

what is this?

In recent years, attacks targeting the Web Application Platform have been increasing rapidly. sisakulint is a static and fast SAST for GitHub Actions.

This great tool can automatically validate yaml files according to the guidelines in the security-related documentation provided by GitHub!

It also includes functionality as a static analysis tool that can check the policies of the guidelines that should be set for use in each organization.

These checks also comply with the Top 10 CI/CD Security Risks provided by OWASP.

It implements most of the functions that can automatically check whether a workflow that meets the security features supported by github has been built to reduce the risk of malicious code being injected into the CI/CD pipeline or credentials such as tokens being stolen.

It does not support inspections that cannot be expressed in YAML and "repository level settings" that can be set by GitHub organization administrators.

It is intended to be used mainly by software developers and security personnel at user companies who work in blue teams.

It is easy to introduce because it can be installed from brew.

It also implements an autofix function for errors related to security features as a lint.

It supports the SARIF format, which is the output format for static analysis. This allows reviewdog to provide a rich UI for error triage on GitHub.


🎤 Featured at BlackHat Arsenal

📖 About the Presentation

sisakulint was showcased at BlackHat Asia 2025 Arsenal, one of the world's leading information security conferences. The presentation demonstrates how sisakulint addresses real-world CI/CD security challenges and helps development teams build more secure GitHub Actions workflows.

Key topics covered:

  • 🔒 Security challenges in GitHub Actions workflows
  • 🔍 SAST approach and semantic analysis techniques
  • ⚙️ Practical rule implementations with real-world examples
  • 🤖 Automated security testing and auto-fix capabilities
  • 🛡️ Defense strategies against OWASP Top 10 CI/CD Security Risks

Tool features

Full Documentation: https://sisaku-security.github.io/lint/

OWASP Top 10 CI/CD Security Risks Coverage

OWASP Risk Description sisakulint Rules
CICD-SEC-01 Insufficient Flow Control Mechanisms improper-access-control, bot-conditions, unsound-contains, ai-action-unrestricted-trigger
CICD-SEC-02 Inadequate Identity and Access Management permissions
CICD-SEC-03 Dependency Chain Abuse known-vulnerable-actions, archived-uses, impostor-commit, ref-confusion, reusable-workflow-taint
CICD-SEC-04 Poisoned Pipeline Execution (PPE) dangerous-triggers-*, code-injection-*, envvar-injection-*, envpath-injection-*, output-clobbering-*, argument-injection-*, untrusted-checkout-*, request-forgery-*, ai-action-prompt-injection
CICD-SEC-05 Insufficient PBAC self-hosted-runners, ai-action-excessive-tools
CICD-SEC-06 Insufficient Credential Hygiene credentials, artipacked, secrets-in-artifacts, secret-exfiltration, secret-exposure, unmasked-secret-exposure, secrets-inherit
CICD-SEC-07 Insecure System Configuration timeout-minutes, deprecated-commands, cache-bloat
CICD-SEC-08 Ungoverned Usage of 3rd Party Services action-list, commit-sha, unpinned-images
CICD-SEC-09 Improper Artifact Integrity Validation artifact-poisoning-*, cache-poisoning-*
CICD-SEC-10 Insufficient Logging and Visibility obfuscation

Severity Summary

sisakulint categorizes rules by severity based on CVSS scores, attack impact, and exploitability:

Severity Count CVSS Range Description
Critical 14 9.0-10.0 Immediate risk, can lead to RCE or full compromise
High 19 7.0-8.9 Significant risk, enables serious attacks
Medium 14 4.0-6.9 Moderate risk, requires specific conditions
Low 6 0.1-3.9 Best practices, minimal direct security impact

Complete Rule Reference

Category Rule Severity Description Fix Docs
Syntax id Low ID collision detection for jobs/env vars docs
env-var Low Environment variable name validation docs
permissions High Permission scopes and values validation Yes docs
workflow-call Medium Reusable workflow call validation docs
job-needs Low Job dependency validation docs
expression Medium Expression syntax validation docs
cond Medium Conditional expression validation Yes docs
deprecated-commands High Deprecated workflow commands detection docs
Config timeout-minutes Low Ensures timeout-minutes is set Yes docs
cache-bloat Low Cache bloat with restore/save pair Yes docs
Credentials credentials High Hardcoded credentials detection Yes docs
secret-exposure High Excessive secrets exposure detection Yes docs
unmasked-secret-exposure High Unmasked derived secrets detection Yes docs
artipacked Critical Credential leakage via persisted checkout Yes docs
secrets-in-artifacts High Sensitive data in artifact uploads Yes docs
secrets-inherit High Excessive secrets inheritance Yes docs
secret-exfiltration Critical Secret exfiltration via network commands docs
Injection code-injection-critical Critical Untrusted input in privileged triggers Yes docs
code-injection-medium Medium Untrusted input in normal triggers Yes docs
envvar-injection-critical Critical Untrusted input to $GITHUB_ENV (privileged) Yes docs
envvar-injection-medium Medium Untrusted input to $GITHUB_ENV (normal) Yes docs
envpath-injection-critical Critical Untrusted input to $GITHUB_PATH (privileged) Yes docs
envpath-injection-medium Medium Untrusted input to $GITHUB_PATH (normal) Yes docs
output-clobbering-critical Critical Untrusted input to $GITHUB_OUTPUT (privileged) Yes docs
output-clobbering-medium Medium Untrusted input to $GITHUB_OUTPUT (normal) Yes docs
argument-injection-critical Critical Command-line argument injection (privileged) Yes docs
argument-injection-medium Medium Command-line argument injection (normal) Yes docs
Checkout untrusted-checkout Critical Untrusted PR code in privileged contexts Yes docs
untrusted-checkout-toctou-critical Critical TOCTOU with labeled events Yes docs
untrusted-checkout-toctou-high High TOCTOU with deployment environment Yes docs
Supply Chain commit-sha High Action version pinning validation Yes docs
action-list Low Organization allowlist/blocklist enforcement docs
impostor-commit Critical Fork network impostor commit detection Yes docs
ref-confusion High Branch/tag name collision detection Yes docs
known-vulnerable-actions Varies Known CVE detection via GitHub Advisories Yes docs
archived-uses Medium Archived action/workflow detection docs
unpinned-images Medium Container image digest pinning docs
reusable-workflow-taint Critical Untrusted inputs in reusable workflow calls Yes docs
Poisoning artifact-poisoning-critical Critical Artifact poisoning and path traversal Yes docs
artifact-poisoning-medium Medium Third-party artifact download in untrusted triggers Yes docs
cache-poisoning High Unsafe cache patterns with untrusted inputs Yes docs
cache-poisoning-poisonable-step High Untrusted code execution after unsafe checkout Yes docs
Access Control improper-access-control High Label-based approval and synchronize events Yes docs
bot-conditions High Spoofable bot detection conditions Yes docs
unsound-contains Medium Bypassable contains() in conditions Yes docs
dangerous-triggers-critical Critical Privileged triggers without mitigations docs
dangerous-triggers-medium Medium Privileged triggers with partial mitigations docs
Other obfuscation High Obfuscated workflow pattern detection Yes docs
self-hosted-runners High Self-hosted runner security risks docs
request-forgery-critical Critical SSRF vulnerabilities (privileged) Yes docs
request-forgery-medium Medium SSRF vulnerabilities (normal) Yes docs
AI Actions ai-action-unrestricted-trigger High AI agent actions with allowed_non_write_users: "*" docs
ai-action-excessive-tools High Dangerous tools (Bash/Write/Edit) in AI agents with untrusted triggers docs
ai-action-prompt-injection High Untrusted input interpolated into AI agent prompt parameters docs

install for macOS user

$ brew tap sisaku-security/homebrew-sisakulint
$ brew install sisakulint

install from release page for Linux user

# visit release page of this repository and download for yours.
$ cd <directory where sisakulint binary is located>
$ mv ./sisakulint /usr/local/bin/sisakulint

Architecture

sisakulint architecture diagram

sisakulint automatically searches for YAML files in the .github/workflows directory. The parser builds an Abstract Syntax Tree (AST) and traverses it to apply various security and best practice rules. Results are output using a custom error formatter, with support for SARIF format for integration with tools like reviewdog.

Key components:

  • 📁 Workflow Discovery - Automatic detection of GitHub Actions workflow files
  • 🔍 AST Parser - Converts YAML into a structured tree representation
  • ⚖️ Rule Engine - Applies security and best practice validation rules
  • 📊 Output Formatters - Custom error format and SARIF support for CI/CD integration

Quick Start

# Run in your repository (auto-detects .github/workflows/)
$ sisakulint

# Analyze specific file
$ sisakulint .github/workflows/ci.yaml

# Preview auto-fixes without modifying files
$ sisakulint -fix dry-run

# Apply auto-fixes
$ sisakulint -fix on

# Output in SARIF format for CI/CD integration
$ sisakulint -format "{{sarif .}}"

Example: Detecting Security Vulnerabilities

Given a workflow file with common security issues:

name: PR Comment Handler

on:
  pull_request_target:
    types: [opened, synchronize]
  issue_comment:
    types: [created]

jobs:
  process-pr:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}

      - name: Echo PR title
        run: |
          echo "Processing PR: ${{ github.event.pull_request.title }}"

      - name: Run build
        run: npm install && npm run build

Running sisakulint detects multiple security issues:

.github/workflows/demo.yaml:1:1: workflow does not have explicit 'permissions' block. Without explicit permissions, the workflow uses the default repository permissions which may be overly broad. Add a 'permissions:' block to follow the principle of least privilege. See https://sisaku-security.github.io/lint/docs/rules/permissions/ [permissions]
      1 👈|name: PR Comment Handler

.github/workflows/demo.yaml:4:3: dangerous trigger (critical): workflow uses privileged trigger(s) [pull_request_target, issue_comment] without any security mitigations. These triggers grant write access and secrets access to potentially untrusted code. Add at least one mitigation: restrict permissions (permissions: read-all or permissions: {}), use environment protection, add label conditions, or check github.actor. See https://sisaku-security.github.io/lint/docs/rules/dangeroustriggersrulecritical/ [dangerous-triggers-critical]
      4 👈|  pull_request_target:

.github/workflows/demo.yaml:10:3: timeout-minutes is not set for job process-pr; see https://sisaku-security.github.io/lint/docs/rules/timeoutminutesrule/ for more details. [missing-timeout-minutes]
       10 👈|  process-pr:

.github/workflows/demo.yaml:13:9: timeout-minutes is not set for step <unnamed>; see https://sisaku-security.github.io/lint/docs/rules/timeoutminutesrule/ for more details. [missing-timeout-minutes]
       13 👈|      - uses: actions/checkout@v4

.github/workflows/demo.yaml:13:9: the action ref in 'uses' for step '<unnamed>' should be a full length commit SHA for immutability and security. See https://sisaku-security.github.io/lint/docs/rules/commitsharule/ [commit-sha]
       13 👈|      - uses: actions/checkout@v4

.github/workflows/demo.yaml:13:9: [Medium] actions/checkout without 'persist-credentials: false' at step "<unnamed>". Credentials are stored in .git/config. While no dangerous upload-artifact was found in this job, consider adding 'persist-credentials: false' to prevent credential exposure. See https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/ [artipacked]
       13 👈|      - uses: actions/checkout@v4

.github/workflows/demo.yaml:15:16: checking out untrusted code from pull request in workflow with privileged trigger 'pull_request_target' (line 4). This allows potentially malicious code from external contributors to execute with access to repository secrets. Use 'pull_request' trigger instead, or avoid checking out PR code when using 'pull_request_target'. See https://sisaku-security.github.io/lint/docs/rules/untrustedcheckout/ for more details [untrusted-checkout]
       15 👈|          ref: ${{ github.event.pull_request.head.sha }}

.github/workflows/demo.yaml:17:9: timeout-minutes is not set for step Echo PR title; see https://sisaku-security.github.io/lint/docs/rules/timeoutminutesrule/ for more details. [missing-timeout-minutes]
       17 👈|      - name: Echo PR title

.github/workflows/demo.yaml:19:35: code injection (critical): "github.event.pull_request.title" is potentially untrusted and used in a workflow with privileged triggers. Avoid using it directly in inline scripts. Instead, pass it through an environment variable. See https://sisaku-security.github.io/lint/docs/rules/codeinjectioncritical/ [code-injection-critical]
       19 👈|          echo "Processing PR: ${{ github.event.pull_request.title }}"

.github/workflows/demo.yaml:21:9: timeout-minutes is not set for step Run build; see https://sisaku-security.github.io/lint/docs/rules/timeoutminutesrule/ for more details. [missing-timeout-minutes]
       21 👈|      - name: Run build

.github/workflows/demo.yaml:21:9: cache poisoning risk via build command: 'Run build' runs untrusted code after checking out PR head (triggers: pull_request_target, issue_comment). Attacker can steal cache tokens [cache-poisoning-poisonable-step]
       21 👈|      - name: Run build

What sisakulint detected

Finding OWASP Risk Severity Auto-fix
Missing permissions block CICD-SEC-02 Medium
Dangerous privileged triggers CICD-SEC-01 Critical
Missing timeout-minutes CICD-SEC-07 Low Yes
Action not pinned to SHA CICD-SEC-08 Medium Yes
Credential exposure risk CICD-SEC-06 Medium Yes
Untrusted checkout CICD-SEC-04 Critical Yes
Code injection CICD-SEC-04 Critical Yes
Cache poisoning CICD-SEC-09 High Yes

SARIF Output & Integration with reviewdog

sisakulint supports SARIF (Static Analysis Results Interchange Format) output, which enables seamless integration with reviewdog for enhanced code review workflows on GitHub.

Why SARIF + reviewdog?

SARIF format allows sisakulint to provide:

  • Rich GitHub UI integration - Errors appear directly in pull request reviews
  • Inline annotations - Issues are shown at the exact file location
  • Automatic triage - Easy filtering and management of findings
  • CI/CD pipeline integration - Automated security checks in your workflow

Visual Example

reviewdog integration showing sisakulint findings in GitHub PR

sisakulint findings displayed directly in GitHub pull request using reviewdog

How to integrate

Add the following step to your GitHub Actions workflow:

name: Lint GitHub Actions Workflows
on: [pull_request]

jobs:
  sisakulint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install sisakulint
        run: |
          # Download from release page or install via brew
          # Example: wget https://github.com/sisaku-security/sisakulint/releases/latest/download/sisakulint-linux-amd64

      - name: Run sisakulint with reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          sisakulint -format "{{sarif .}}" | \
          reviewdog -f=sarif -reporter=github-pr-review -filter-mode=nofilter

SARIF format usage

To output results in SARIF format

# Output to stdout
$ sisakulint -format "{{sarif .}}"

# Save to file
$ sisakulint -format "{{sarif .}}" > results.sarif

# Pipe to reviewdog
$ sisakulint -format "{{sarif .}}" | reviewdog -f=sarif -reporter=github-pr-review

Benefits in CI/CD

  • Automated security reviews - Every PR is automatically checked
  • Early detection - Find issues before merging
  • Clear feedback - Developers see exactly what needs to be fixed
  • Consistent standards - Enforce security policies across all workflows
  • Integration with existing tools - Works with your current GitHub workflow

Using autofix features

sisakulint provides an automated fix feature that can automatically resolve certain types of security issues and best practice violations. This feature saves time and ensures consistent fixes across your workflow files.

Available modes

  • -fix dry-run: Show what changes would be made without actually modifying files
  • -fix on: Automatically fix issues and save changes to files

Rules that support autofix

The following rules support automatic fixes:

1. missing-timeout-minutes (timeout-minutes)

Automatically adds timeout-minutes: 5 to jobs and steps that don't have it set.

Before:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

After:

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4

2. commit-sha (commitsha)

Converts action references from tags to full-length commit SHAs for enhanced security. The original tag is preserved as a comment.

Before:

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v3

After:

steps:
  - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
  - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v3

3. credentials

Removes hardcoded passwords from container configurations.

Before:

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: myregistry/myimage
      credentials:
        username: ${{ secrets.REGISTRY_USERNAME }}
        password: my-hardcoded-password

After:

jobs:
  test:
    runs-on: ubuntu-latest
    container:
      image: myregistry/myimage
      credentials:
        username: ${{ secrets.REGISTRY_USERNAME }}

4. untrusted-checkout

Adds explicit ref specifications to checkout actions in privileged workflow contexts to prevent checking out untrusted PR code.

Before:

on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install

After:

on:
  pull_request_target:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.base.ref }}
      - run: npm install

5. artifact-poisoning

Adds validation steps to artifact download operations to prevent path traversal and poisoning attacks.

Before:

steps:
  - uses: actions/download-artifact@v4
    with:
      name: build-output
  - run: bash ./scripts/deploy.sh

After:

steps:
  - uses: actions/download-artifact@v4
    with:
      name: build-output
  - name: Validate artifact paths
    run: |
      # Validate no path traversal attempts
      find . -name ".." -o -name "../*" | grep . && exit 1 || true
  - run: bash ./scripts/deploy.sh

Usage examples

1. Check what would be fixed (dry-run mode)

$ sisakulint -fix dry-run

This will show all the changes that would be made without actually modifying your files. Use this to preview changes before applying them.

2. Automatically fix issues

$ sisakulint -fix on

This will automatically fix all supported issues and save the changes to your workflow files.

3. Typical workflow

# First, run without fix to see all issues
$ sisakulint

# Preview what autofix would change
$ sisakulint -fix dry-run

# Apply the fixes
$ sisakulint -fix on

# Verify the changes
$ git diff .github/workflows/

Important notes

  • Always review changes: Even though autofix is automated, always review the changes made to your workflow files before committing them
  • Commit SHA fixes require internet: The commit-sha rule needs to fetch commit information from GitHub, so it requires an active internet connection
  • Rate limiting: The commit SHA autofix makes GitHub API calls, which are subject to rate limiting. For unauthenticated requests, the limit is 60 requests per hour
  • Backup your files: Consider committing your changes or backing up your workflow files before running autofix
  • Not all rules support autofix: Some rules like expression, permissions, issue-injection, cache-poisoning, and deprecated-commands require manual fixes as they depend on your specific use case
  • Auto-fix capabilities: Currently, timeout-minutes, commit-sha, credentials, untrusted-checkout, and artifact-poisoning rules support auto-fix. More rules will support auto-fix in future releases

JSON schema for GitHub Actions syntax

paste into your settings.json:

 "yaml.schemas": {
     "https://github.com/sisaku-security/homebrew-sisakulint/raw/main/settings.json": "/.github/workflows/*.{yml,yaml}"
 }

About

CI-Friendly static linter with autofix, SAST, semantic analysis for GitHub Actions

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages