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:
critical- only block on CRITICAL findings (least restrictive)high- block on HIGH or CRITICAL (recommended default)medium- block on MEDIUM, HIGH, or CRITICALlow- block on any findingnever- scan runs, results upload to SARIF, build never fails
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.