Overview
The Shieldome GitHub Actions integration uses the REST API directly - no marketplace action, no Docker image to pull. The generated workflow starts a scan, polls for completion, uploads SARIF to GitHub Code Scanning, and fails the build if findings meet your configured severity threshold.
Prerequisites
- A Shieldome account with an API key - see API Keys
- Your target URL must be publicly reachable from GitHub Actions runners (or use
custom-ipfor internal staging) - Repository permission:
security-events: write(for SARIF upload to GitHub Security tab)
Step 1 - Add secrets to your repository
- Go to your GitHub repository → Settings → Secrets and variables → Actions
- Add secret
SHIELDOME_API_KEY- your API key from Profile → API Keys - Optionally add secret
SHIELDOME_TARGET- the URL to scan (or hard-code it in the YAML)
Step 2 - Download the workflow file
The easiest way: open your Shieldome dashboard, enter your target URL, then click the CI/CD button and choose Download workflow YAML. The file is pre-filled with your target URL and ready to commit.
Alternatively, create .github/workflows/shieldome-scan.yml manually:
name: Shieldome Security Scan on: push: branches: [ main, master ] pull_request: branches: [ main, master ] schedule: - cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC workflow_dispatch: permissions: security-events: write contents: read env: SHIELDOME_HOST: https://app.shieldome.com TARGET_URL: ${{ secrets.SHIELDOME_TARGET }} # or hard-code: https://your-app.com FAIL_ON: high # critical | high | medium | low | never jobs: security-scan: name: Shieldome Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Start scan id: scan run: | RESPONSE=$(curl -sf --retry 3 -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"}') echo "$RESPONSE" SCAN_ID=$(echo "$RESPONSE" | jq -r '.scan_id // empty') if [ -z "$SCAN_ID" ]; then echo "::error::Failed to start scan. Check SHIELDOME_API_KEY and TARGET_URL." exit 1 fi echo "scan_id=$SCAN_ID" >> $GITHUB_OUTPUT echo "Scan started: $SCAN_ID" - name: Wait for completion (max 15 min) run: | SCAN_ID="${{ steps.scan.outputs.scan_id }}" for i in $(seq 1 90); do DATA=$(curl -sf -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}" \ "$SHIELDOME_HOST/api/v1/scan/$SCAN_ID") STATUS=$(echo "$DATA" | jq -r '.status') PCT=$(echo "$DATA" | jq -r '.progress.percent // "-"') echo "[$i/90] status=$STATUS progress=${PCT}%" if [ "$STATUS" = "completed" ]; then break; fi if [ "$STATUS" = "failed" ] || [ "$STATUS" = "aborted" ]; then echo "::error::Scan ended with status '$STATUS'" exit 1 fi sleep 10 done - name: Security gate id: 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 . PASSED=$(echo "$GATE" | jq -r '.passed') GRADE=$(echo "$GATE" | jq -r '.grade') SCORE=$(echo "$GATE" | jq -r '.risk_score') { echo "## 🛡 Shieldome Security Scan" echo "" echo "| | |" echo "|---|---|" echo "| **Target** | $TARGET_URL |" echo "| **Grade** | $GRADE |" echo "| **Risk Score** | $SCORE / 100 |" echo "| **Gate** | $FAIL_ON+ → $([ \"$PASSED\" = \"true\" ] && echo \"✅ PASSED\" || echo \"❌ FAILED\") |" echo "" echo "| Severity | Count |" echo "|---|---|" echo "$GATE" | jq -r '.counts | to_entries[] | "| \(.key) | \(.value) |"' } >> $GITHUB_STEP_SUMMARY if [ "$PASSED" != "true" ]; then echo "::error::Security gate FAILED - $FAIL_ON+ findings detected." exit 1 fi - name: Download 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 || true - name: Upload SARIF to GitHub Security tab if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: shieldome-results.sarif category: shieldome continue-on-error: true - name: Download PDF report if: always() run: | curl -sf -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}" \ "$SHIELDOME_HOST/api/scan/${{ steps.scan.outputs.scan_id }}/report" \ -o shieldome-report.pdf || true - name: Upload artifacts if: always() uses: actions/upload-artifact@v4 with: name: shieldome-security-report path: | shieldome-report.pdf shieldome-results.sarif retention-days: 30
Configuration options
| Variable | Default | Description |
|---|---|---|
TARGET_URL | - | Required. URL to scan, e.g. https://staging.example.com |
FAIL_ON | high | Fail the build at this severity: critical, high, medium, low, or never |
scan_type (in curl body) | vuln | vuln, perf, both, or api |
SHIELDOME_HOST | https://app.shieldome.com | Override for self-hosted deployments |
Advanced example - PR comment with security summary
- name: Comment on PR if: github.event_name == 'pull_request' && always() uses: actions/github-script@v7 with: script: | const gate = JSON.parse(process.env.GATE_JSON || '{}'); const icon = gate.passed ? '✅' : '❌'; github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: [ `## ${icon} Shieldome Security Scan`, `| Severity | Count |`, `|---|---|`, `| 🔴 Critical | ${gate.counts?.critical ?? 0} |`, `| 🟠 High | ${gate.counts?.high ?? 0} |`, `| 🟡 Medium | ${gate.counts?.medium ?? 0} |`, `| 🔵 Low | ${gate.counts?.low ?? 0} |`, ``, `**Risk Score:** ${gate.risk_score ?? '-'}/100 · **Grade:** ${gate.grade ?? '-'}`, `[View full report](https://app.shieldome.com)`, ].join('\n') })
Authenticated scans
To scan behind a login, pass a session cookie via the API body:
- name: Start authenticated scan run: | curl -sf -X POST "$SHIELDOME_HOST/api/v1/scan" \ -H "Content-Type: application/json" \ -H "X-Shieldome-Key: ${{ secrets.SHIELDOME_API_KEY }}" \ -d '{ "target": "${{ secrets.SHIELDOME_TARGET }}", "scan_type": "vuln", "cookie": "${{ secrets.SCAN_SESSION_COOKIE }}" }'
"custom_ip": "10.0.1.50" to the scan body to hit a specific server IP while keeping the Host header set to your production domain. Useful for testing infrastructure changes before go-live.
GitLab CI
The same curl-based approach works in any CI system. See the GitLab CI guide and generic CI/CD guide for platform-specific examples.
Troubleshooting
| Problem | Solution |
|---|---|
| SARIF upload fails with 403 | Add permissions: security-events: write to the job or top-level workflow |
| Build fails on the security gate | Set FAIL_ON: critical or FAIL_ON: never while investigating new findings |
| Scan ID is empty / curl returns nothing | The API key is wrong or the target URL is unreachable. Run the curl command locally to confirm. |
| Scan times out after 90 × 10 s = 15 min | Full scans with Playwright can take longer - increase the loop to 120 iterations or switch to scan_type: vuln |
| Want to scan a specific server IP | Add "custom_ip": "1.2.3.4" to the POST body - Host header stays correct |