Static analysis is only useful if it actually runs. Semgrep can catch injection vulnerabilities, insecure deserialization, and hundreds of OWASP Top 10 patterns in seconds — but when CI pipelines break, rules regress, or scan times balloon out of control, those checks silently disappear from your security posture. You only notice when a vulnerability slips through.
Most teams treat Semgrep as a set-and-forget CI step. A failed scan gets retried, the flaky pipeline gets ignored, and gradually the SAST layer becomes decorative. Monitoring changes that equation: uptime checks on the Semgrep API, alerting on scan duration spikes, and heartbeat monitors that confirm scans are actually running keep the tool honest.
This tutorial shows how to wire Vigilmon into a Semgrep-powered CI/CD pipeline to monitor scan availability, rule health, and integration continuity.
What You'll Build
- An HTTP monitor for the Semgrep API endpoint
- A heartbeat monitor that fires after every successful scan run
- A webhook integration that reports scan status to Vigilmon
- Alerts for scan failures and performance regressions
Prerequisites
- A Vigilmon account at vigilmon.online
- A Vigilmon API key (Settings → API Keys)
- Semgrep CLI installed (
pip install semgrep) or Semgrep Cloud access - A CI/CD system (GitHub Actions, GitLab CI, or similar)
Step 1: Monitor the Semgrep API Availability
If you use Semgrep Cloud or Semgrep's managed infrastructure for rule syncing, the API is a dependency. Monitor it directly.
In the Vigilmon dashboard, go to Monitors → New Monitor and create an HTTP monitor:
- Name: Semgrep API
- URL:
https://semgrep.dev/api/check-alive - Interval: 5 minutes
- Expected status: 200
- Alert: Slack or email channel of your choice
For teams using Semgrep's registry to pull rules, also add:
- Name: Semgrep Registry
- URL:
https://semgrep.dev/c/p/default - Interval: 30 minutes
- Expected status: 200
This catches registry outages that would cause your CI jobs to fail when fetching rule sets.
Step 2: Add a Heartbeat Monitor for Scan Continuity
A heartbeat monitor is the most direct way to confirm Semgrep is actually running. Create one in Vigilmon, then ping it at the end of every scan job. If the ping doesn't arrive within the expected window, Vigilmon alerts you.
In Vigilmon, create a Heartbeat Monitor:
- Name: Semgrep CI Scan - main branch
- Expected interval: 24 hours (or match your scan frequency)
- Grace period: 2 hours
Vigilmon gives you a unique heartbeat URL. Add the ping to your CI step:
# .github/workflows/semgrep.yml
name: Semgrep Scan
on:
push:
branches: [main]
pull_request:
jobs:
semgrep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
id: semgrep
run: |
pip install semgrep
semgrep scan \
--config=auto \
--json \
--output=semgrep-results.json \
.
- name: Ping Vigilmon heartbeat
if: success()
run: |
curl -s -X POST "${{ secrets.VIGILMON_HEARTBEAT_URL }}" \
-H "Content-Type: application/json" \
-d '{"status": "ok", "source": "semgrep-ci"}'
Store the heartbeat URL as a CI secret (VIGILMON_HEARTBEAT_URL). The if: success() condition ensures the heartbeat only fires when the scan completes without errors — not when it's skipped or the job errors out.
Step 3: Report Scan Metrics via Vigilmon Webhook
Semgrep's --json output contains everything you need to build rich metrics: rule count, finding count, scan duration, files scanned, and errors. Parse these and send them to Vigilmon's webhook endpoint so your dashboard reflects scan health over time.
#!/bin/bash
# report-semgrep-metrics.sh
RESULTS_FILE="semgrep-results.json"
VIGILMON_WEBHOOK="$1"
if [ ! -f "$RESULTS_FILE" ]; then
echo "No results file found"
exit 1
fi
# Extract key metrics from Semgrep JSON output
FINDINGS=$(jq '.results | length' "$RESULTS_FILE")
ERRORS=$(jq '.errors | length' "$RESULTS_FILE")
FILES_SCANNED=$(jq '.stats.files_scanned // 0' "$RESULTS_FILE")
RULES_RAN=$(jq '.stats.total_time // 0' "$RESULTS_FILE")
SCAN_DURATION=$(jq '.stats.total_time // 0' "$RESULTS_FILE")
STATUS="healthy"
if [ "$ERRORS" -gt 0 ]; then
STATUS="degraded"
fi
curl -s -X POST "$VIGILMON_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"status\": \"$STATUS\",
\"message\": \"Semgrep scan complete: $FINDINGS findings, $ERRORS errors, $FILES_SCANNED files\",
\"metadata\": {
\"findings\": $FINDINGS,
\"errors\": $ERRORS,
\"files_scanned\": $FILES_SCANNED,
\"scan_duration_s\": $SCAN_DURATION
}
}"
Wire this into your CI workflow after the scan step:
- name: Report metrics to Vigilmon
if: always()
run: |
chmod +x ./scripts/report-semgrep-metrics.sh
./scripts/report-semgrep-metrics.sh "${{ secrets.VIGILMON_WEBHOOK_URL }}"
Using if: always() ensures metrics are reported even on failed scans — which is when you most need the data.
Step 4: Monitor Rule Performance
Slow rules are a hidden CI tax. A rule that takes 30 seconds to scan a medium-sized repo can cause pipeline queues to back up across the team. Semgrep provides per-rule timing data in profiling mode.
Enable profiling in your scan:
semgrep scan \
--config=auto \
--json \
--time \
--output=semgrep-results.json \
.
The output now includes a time block with per-rule timing. Extract the slowest rules and alert when any rule exceeds a threshold:
#!/bin/bash
# check-rule-performance.sh
MAX_RULE_DURATION_MS=5000 # Alert if any rule takes more than 5 seconds
SLOW_RULES=$(jq --argjson max "$MAX_RULE_DURATION_MS" '
.time.rules
| to_entries
| map(select(.value.match_time * 1000 > $max))
| map({rule: .key, duration_ms: (.value.match_time * 1000 | floor)})
' semgrep-results.json)
SLOW_COUNT=$(echo "$SLOW_RULES" | jq 'length')
if [ "$SLOW_COUNT" -gt 0 ]; then
echo "WARNING: $SLOW_COUNT rules exceeded ${MAX_RULE_DURATION_MS}ms threshold"
echo "$SLOW_RULES" | jq .
# Send alert to Vigilmon
curl -s -X POST "$VIGILMON_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{
\"status\": \"degraded\",
\"message\": \"$SLOW_COUNT Semgrep rules are slow (>${MAX_RULE_DURATION_MS}ms)\",
\"metadata\": {\"slow_rules\": $SLOW_RULES}
}"
fi
Add this check as a non-blocking CI step so slow rules generate alerts without failing the pipeline.
Step 5: Track Finding Trends with Periodic Scans
Scheduled scans of your main branch give you a baseline to detect finding count regressions — when the number of issues spikes, it often means a risky refactor landed without review, or a new code pattern bypassed existing suppressions.
# .github/workflows/semgrep-scheduled.yml
name: Semgrep Scheduled Baseline Scan
on:
schedule:
- cron: '0 6 * * *' # Daily at 6am UTC
jobs:
baseline-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
- name: Run baseline scan
run: |
pip install semgrep
semgrep scan \
--config=auto \
--json \
--output=semgrep-baseline.json \
.
- name: Check finding delta and alert
env:
VIGILMON_API_KEY: ${{ secrets.VIGILMON_API_KEY }}
run: |
CURRENT=$(jq '.results | length' semgrep-baseline.json)
# Compare against stored baseline (you can persist this in a cache or artifact)
PREVIOUS=${SEMGREP_BASELINE_COUNT:-0}
DELTA=$((CURRENT - PREVIOUS))
STATUS="healthy"
MESSAGE="Semgrep baseline: $CURRENT findings (delta: $DELTA)"
if [ "$DELTA" -gt 10 ]; then
STATUS="degraded"
MESSAGE="Semgrep finding spike: +$DELTA new findings on main"
fi
curl -s -X POST "https://vigilmon.online/api/webhook/${{ secrets.VIGILMON_HEARTBEAT_ID }}" \
-H "Authorization: Bearer $VIGILMON_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"status\": \"$STATUS\", \"message\": \"$MESSAGE\"}"
Step 6: Set Up Alerting Channels
Route scan alerts to the right people. In Vigilmon, go to Alerts → Channels and configure:
- Slack: Post to your
#securityor#devsecopschannel for findings spikes - Email: Notify the security lead when scan errors exceed threshold
- PagerDuty or webhook: Escalate if scans haven't run in 48 hours (heartbeat miss)
Separate alert channels by severity:
- Heartbeat miss → PagerDuty (scans not running is a critical gap)
- Finding spike → Slack security channel (needs investigation, not immediate action)
- Slow rules → Slack engineering channel (performance issue, low urgency)
Monitoring Coverage Summary
| What to monitor | Monitor type | Interval | Alert on | |---|---|---|---| | Semgrep API availability | HTTP | 5 min | Non-200 response | | Rule registry reachability | HTTP | 30 min | Non-200 response | | Scan running on schedule | Heartbeat | 24h | Missed ping | | Scan finding count trend | Webhook | Per scan | Delta > threshold | | Scan error count | Webhook | Per scan | Errors > 0 | | Slow rule detection | Script + webhook | Per scan | Rule time > 5s |
Semgrep's value depends entirely on it running consistently and correctly. A single week of broken scans can undo months of security posture improvements. Vigilmon gives you the observability layer that ensures your SAST pipeline is actually doing its job — not just appearing in a CI status badge.
Start monitoring your security pipeline free at vigilmon.online
Tags: #semgrep #sast #security #devsecops #monitoring