Most security scanning integrations for GitHub Actions follow the same pattern: install a marketplace action, trust a third-party Docker image, and hope the action is maintained. There is a better approach: call the Shieldome REST API directly from your workflow using curl and jq, both available in every GitHub-hosted runner.

Why avoid marketplace actions for security tools?

Marketplace actions introduce a supply chain dependency. When you write uses: somevendor/scanner@v2, you are running arbitrary code from that vendor's repository at that git tag. Tags are mutable - a vendor can push different code to v2 without changing the tag name. For a security tool specifically, this is an ironic risk.

A curl-based integration is transparent: you can read every line, and it only does what the YAML says.

The full workflow

Here is a minimal, production-ready Shieldome scan workflow. Save it as .github/workflows/shieldome-scan.yml:

name: Shieldome Security Scan

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
  schedule:
    - cron: '0 6 * * 1'   # Weekly on Mondays

permissions:
  security-events: write
  contents: read

env:
  SHIELDOME_HOST: https://app.shieldome.com
  TARGET_URL: ${{ secrets.SHIELDOME_TARGET }}
  FAIL_ON: high

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

      - name: Start scan
        id: scan
        run: |
          RESPONSE=$(curl -sf -X POST "$SHIELDOME_HOST/api/v1/scan"             -H "Content-Type: application/json"             -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}"             -d '{"target":"'"$TARGET_URL"'","scan_type":"vuln"}')
          SCAN_ID=$(echo "$RESPONSE" | jq -r '.scan_id // empty')
          echo "scan_id=$SCAN_ID" >> $GITHUB_OUTPUT

      - name: Wait for completion
        run: |
          for i in $(seq 1 90); do
            STATUS=$(curl -sf -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}"               "$SHIELDOME_HOST/api/v1/scan/${{ steps.scan.outputs.scan_id }}" | jq -r '.status')
            echo "[$i] $STATUS"
            [ "$STATUS" = "completed" ] && break
            sleep 10
          done

      - name: Security gate
        run: |
          GATE=$(curl -sf -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}"             "$SHIELDOME_HOST/api/v1/scan/${{ steps.scan.outputs.scan_id }}/gate?fail_on=$FAIL_ON")
          echo "$GATE" | jq .
          [ "$(echo "$GATE" | jq -r '.passed')" = "true" ] || exit 1

      - name: Upload SARIF
        if: always()
        run: |
          curl -sf -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}"             "$SHIELDOME_HOST/api/v1/scan/${{ steps.scan.outputs.scan_id }}/sarif"             -o shieldome-results.sarif

      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: shieldome-results.sarif
        continue-on-error: true

Adding a PR comment

The most useful addition for pull request workflows is a comment that surfaces the scan summary without requiring reviewers to click into the Actions tab:

      - name: Comment on PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const gate = JSON.parse(process.env.GATE_JSON || '{}');
            github.rest.issues.createComment({
              owner: context.repo.owner, repo: context.repo.repo,
              issue_number: context.issue.number,
              body: [
                '## 🛡 Shieldome Security Scan',
                `**Risk Score:** ${gate.risk_score}/100 · **Grade:** ${gate.grade}`,
                '',
                '| Severity | Count |',
                '|---|---|',
                `| 🔴 Critical | ${gate.counts?.critical ?? 0} |`,
                `| 🟠 High     | ${gate.counts?.high ?? 0} |`,
                `| 🟡 Medium   | ${gate.counts?.medium ?? 0} |`,
              ].join('\n')
            })

Scanning staging before DNS cutover

A common CI/CD pattern is deploying to a staging server with an internal IP before updating DNS. Shieldome supports this via the custom_ip parameter - the scan hits the specified IP while keeping the Host header set to your production domain:

-d '{"target":"https://example.com","scan_type":"vuln","custom_ip":"10.0.1.50"}'

This means your TLS certificate, domain-specific headers, and application routing are all tested as they would be in production - without requiring public DNS.

Configuring the severity gate

The fail_on query parameter controls which severity level triggers a build failure:

Start with never to understand your baseline, then tighten the gate gradually once you have remediated existing findings.

Getting started

The Shieldome dashboard can generate a pre-filled workflow YAML for your target URL. After logging in, enter your target URL, click CI/CD, and download the workflow file. Commit it to .github/workflows/, add SHIELDOME_API_KEY as a repository secret, and the first scan runs on your next push.

Create a free account to get your API key. The first scan is included - no credit card required.