Skip to content
Commitpedia
Log in Join & edit

Scan your own code and containers in CI: Gitleaks, Semgrep and Trivy

Set up a GitHub Actions workflow that runs Gitleaks, Semgrep and Trivy on every push of your own project, learn what each step catches, and read the first report without being overwhelmed.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of Scan your own code and containers in CI: Gitleaks, Semgrep and Trivy: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
4 min

A good first layer of security checks for a project costs little: three open source scanners, each answering a different question, run on every push. Gitleaks asks whether a secret was committed. Semgrep asks whether the source code contains known bug and vulnerability patterns. Trivy asks whether the dependencies and the container image carry published vulnerabilities or obvious misconfigurations. This how-to wires all three into one GitHub Actions workflow for a repository you control and explains how to read the output. The same commands work in GitLab CI or any other runner.

Before you start

  • A repository of your own on GitHub with Actions enabled.
  • Optionally a Dockerfile at the root; the Trivy image step is skipped if there is none.
  • Scan only projects you own or are permitted to test. These tools run against your own code and images and never touch anyone else’s systems.

1. Add the workflow

Create .github/workflows/security.yml with three jobs that run independently, so one failure does not hide the others:

name: security

on:
  push:
  pull_request:

permissions:
  contents: read

jobs:
  secrets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  static-analysis:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4
      - run: semgrep scan --config auto --error

  dependencies-and-image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aquasecurity/[email protected]
        with:
          scan-type: fs
          scan-ref: .
          scanners: vuln,misconfig,secret
          severity: HIGH,CRITICAL
          exit-code: '1'
      - if: hashFiles('Dockerfile') != ''
        run: docker build -t local/app:ci .
      - if: hashFiles('Dockerfile') != ''
        uses: aquasecurity/[email protected]
        with:
          scan-type: image
          image-ref: local/app:ci
          severity: HIGH,CRITICAL
          exit-code: '1'

Commit and push. The Actions tab shows three jobs; each turns red when its scanner finds something at or above the threshold you set.

2. What each job catches

secrets checks out the full history (fetch-depth: 0) so Gitleaks can walk every commit, not just the latest tree. It reports strings that look like API keys, tokens, passwords and private keys, with the commit and author. A hit here means the credential must be rotated, because removing the file in a later commit does not remove it from history.

static-analysis runs Semgrep inside its official container with --config auto, which selects community rulesets for the languages it detects. Findings are code patterns: a SQL query built with string concatenation, a subprocess call with shell=True, a disabled TLS verification, a weak hash used for passwords. --error makes the job fail when there are findings of the default severity. Each result names the rule, and the rule page explains the risk and the usual fix.

dependencies-and-image runs Trivy twice. The file system scan reads lock files such as package-lock.json, requirements.txt or go.sum and reports dependencies with published vulnerabilities of HIGH or CRITICAL severity, plus misconfigurations in any Dockerfile, Kubernetes manifest or Terraform file and any secrets it spots. If a Dockerfile exists, the image is built and scanned, which adds the operating system packages of the base image to the report.

3. Read the first report

The first run on an existing project usually produces more output than expected. Work through it in this order:

  1. Gitleaks findings first. Confirm each one; if it is a real credential, rotate it immediately, then add the path or fingerprint to .gitleaksignore only for confirmed false positives such as example keys in documentation.
  2. Trivy CRITICAL entries next. The report lists the installed version and the fixed version; most are solved by bumping a dependency or the base image tag and rebuilding.
  3. Semgrep findings last, starting with those tagged as security rather than best practice. Fix the obvious ones; for rules that do not apply to your project, create a .semgrep.yml that excludes them rather than lowering the whole threshold.
  4. Once the run is green, keep it green. New findings now correspond to the change that introduced them, which is far easier to triage than a backlog.

To see the full detail of a Trivy or Semgrep result locally, run the same command on your machine; the entries on each tool in this topic show the one-line installs.

Adjusting the thresholds

The example fails the build on HIGH and CRITICAL vulnerabilities and on default-severity Semgrep findings. Teams that prefer to see results without blocking merges can set exit-code: '0' for Trivy and drop --error for Semgrep for a few weeks, then tighten. Both tools can also emit SARIF, which GitHub displays in the Security tab of the repository when uploaded with the github/codeql-action/upload-sarif action, though that requires the code scanning feature to be available on your plan.

Official documentation

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

  • 0313 What is CI/CD? CI/CD is the habit of automatically testing every code change and, once it passes, deploying it without manual steps. Beginner · Explains an idea · Any system Article Pipelines, runners & hooks 15 Oct 2026
  • 0378 Continuous integration explained What continuous integration means, why small frequent merges plus an automated build on every change prevent integration pain, what a good pipeline… Beginner · Explains an idea · Any system Article Pipelines, runners & hooks 15 Oct 2026
  • 0379 Your first GitHub Actions workflow: run tests on every push Build a working GitHub Actions pipeline from nothing: a small Python project with a test, a workflow file that runs pytest on… Beginner · Guided project · Any system Article GitHub Actions · Pipelines, runners & hooks 15 Oct 2026
  • 0381 pre-commit hooks: catch mistakes before they are committed Install the pre-commit framework, configure hooks for whitespace, YAML and JSON syntax, large files, leaked private keys, Python linting and shellcheck, run… Beginner · Step by step · Linux, macOS, Windows Article Pipelines, runners & hooks · Setup 15 Oct 2026

Repos in the wild

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +5,389 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?