Skip to content

GitHub Action

Overview

The xydac/checkagent composite action runs a CheckAgent safety scan against your agent and uploads the results to GitHub Code Scanning as a SARIF file. Once uploaded, probe failures appear as code scanning alerts in the Security tab and as inline annotations on pull requests — giving reviewers a clear view of new safety regressions without leaving the PR review interface.


Basic Usage

The minimal configuration requires only a target — the Python importable pointing to your agent's entry function.

name: CheckAgent Safety Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  safety-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Run CheckAgent scan
        uses: xydac/checkagent@v0.2
        with:
          target: my_agent:run

The action installs Python 3.11, installs checkagent, installs your repo's dependencies from requirements.txt, runs checkagent scan, and uploads the SARIF results to Code Scanning — even if the scan finds failures.


With LLM Judge

Adding llm-judge: true passes each borderline finding through an LLM evaluator to reduce false positives. This makes the scan more accurate but adds approximately $0.01–$0.10 per run depending on your agent's complexity and the number of borderline findings.

jobs:
  safety-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      - name: Run CheckAgent scan
        uses: xydac/checkagent@v0.2
        with:
          target: my_agent:run
          llm-judge: "true"
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

You can use ANTHROPIC_API_KEY instead of OPENAI_API_KEY — the action passes whichever key is present through to the scan process.


Full Workflow Example

The workflow below shows a complete production setup: fast scan on every PR (no LLM judge, free), proper permissions, SARIF upload with fork protection, and a badge generation step.

name: CheckAgent Safety Scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  safety-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - uses: actions/checkout@v4

      # Run the scan — LLM judge disabled for speed and cost
      - name: Run CheckAgent scan
        id: scan
        uses: xydac/checkagent@v0.2
        with:
          target: my_agent:run
          llm-judge: "false"
          sarif-file: checkagent-results.sarif
          requirements: requirements.txt

      # Upload SARIF — always runs, even if scan found failures
      # Fork PR protection: only upload when we have write access
      - name: Upload SARIF to Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        if: >
          always() &&
          (github.event_name != 'pull_request' ||
           github.event.pull_request.head.repo.full_name == github.repository)
        with:
          sarif_file: checkagent-results.sarif
          category: checkagent-scan

      # Generate a status badge and commit it to the repo
      - name: Generate badge
        if: github.ref == 'refs/heads/main'
        run: checkagent report --badge checkagent-results.sarif

      # Fail the job explicitly if critical probes fired
      # (the scan exits non-zero on critical findings by default,
      #  but this step makes the failure reason visible in the log)
      - name: Assert no critical findings
        run: |
          CRITICAL=$(python - <<'EOF'
          import json, sys
          with open("checkagent-results.sarif") as f:
              sarif = json.load(f)
          runs = sarif.get("runs", [])
          results = runs[0].get("results", []) if runs else []
          criticals = [r for r in results if r.get("level") == "error"]
          print(len(criticals))
          EOF
          )
          if [ "$CRITICAL" -gt "0" ]; then
            echo "::error::CheckAgent found $CRITICAL critical safety finding(s). See the Security tab for details."
            exit 1
          fi

Inputs Reference

Input Required Default Description
target Yes Agent target: module:function (e.g. my_agent:run) or --url http://localhost:8080 for HTTP agents
llm-judge No "false" Set to "true" to use LLM-as-judge for borderline findings. Requires OPENAI_API_KEY or ANTHROPIC_API_KEY in the environment.
sarif-file No "results.sarif" Path where the SARIF output file is written. Pass the same path to upload-sarif if you upload separately.
requirements No "requirements.txt" Path to the requirements file for the agent under test. Falls back to pyproject.toml if the file is not found.

SARIF and Code Scanning

Once SARIF is uploaded, findings surface in two places:

Security tab — Navigate to Security > Code scanning in your repository. Each probe failure becomes an alert with: - The probe name and category (e.g. direct_injection / LLM01 Prompt Injection) - Severity (note, warning, or error mapped from CheckAgent's low/medium/high/critical) - A description of what the probe tested and why the finding was raised

Pull request annotations — For any probe failure introduced by a PR (i.e. not present on the base branch), GitHub adds an inline annotation to the PR diff. Reviewers see the finding without navigating away from the PR.

Findings are dismissed or resolved the same way as any other code scanning alert — through the Security UI or by fixing the underlying behaviour so the probe passes.


Permissions

The security-events: write permission is required for the SARIF upload step. Without it, github/codeql-action/upload-sarif will fail with a 403 error.

Fork pull requests do not have access to security-events: write — GitHub restricts this permission for workflows triggered by forks to prevent malicious PRs from polluting the repository's security findings. Add a conditional to the upload step to skip it for fork PRs:

- name: Upload SARIF to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  if: >
    always() &&
    (github.event_name != 'pull_request' ||
     github.event.pull_request.head.repo.full_name == github.repository)
  with:
    sarif_file: results.sarif
    category: checkagent-scan

The scan itself still runs for fork PRs — only the SARIF upload is skipped. Probe results are still visible in the workflow logs.