mix_audit is a Mix task that audits Hex packages in your Elixir project against the Elixir security advisories database, flagging dependencies with known CVEs or vulnerabilities. It is a critical line of defence in your CI pipeline — but like any scheduled or CI-triggered process, it can stop running silently: a broken CI configuration, a skipped pipeline step, or a task that always exits zero even when the advisory database is unreachable. Vigilmon lets you verify that your security scans are actually completing, so you never discover a vulnerability gap after a breach.
What You'll Set Up
- Cron heartbeat confirming
mix_auditruns complete successfully on your scheduled cadence - CI pipeline health check via Vigilmon HTTP monitor
- Alert routing so security failures reach the right people
- SSL certificate monitoring for the advisory database endpoint
Prerequisites
- Elixir 1.14+ with
mix_auditin yourmix.exsdev/test dependencies - A CI system (GitHub Actions, GitLab CI, or similar)
- A free Vigilmon account
Step 1: Add mix_audit to Your Project
# mix.exs
defp deps do
[
{:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false},
# ... other deps
]
end
mix deps.get
mix deps.audit
A passing audit exits 0; a failing audit (dependencies with known CVEs) exits non-zero. Run it as part of your standard CI check:
# Verify it works
mix deps.audit
# No vulnerable packages found
Step 2: Set Up a Vigilmon Cron Heartbeat for Your Audit Schedule
The critical gap in most security tooling is that nobody notices when the scan stops running. CI pipelines get disabled, scheduled jobs get removed, and months pass without audits. A Vigilmon heartbeat closes that gap.
Create a heartbeat monitor:
- In Vigilmon, click Add Monitor → Cron Heartbeat.
- Set Expected ping interval to match your audit schedule:
- Daily audits:
1440minutes (24 hours) - Weekly audits:
10080minutes (7 days) - Per-PR audits: set conservatively at
1440minutes (daily) to catch abandoned pipelines
- Daily audits:
- Copy the heartbeat URL (e.g.
https://vigilmon.online/heartbeat/abc123). - Set the
VIGILMON_AUDIT_HEARTBEAT_URLenvironment variable in your CI system.
Step 3: Integrate the Heartbeat Ping Into Your CI Pipeline
GitHub Actions
# .github/workflows/security.yml
name: Security Audit
on:
schedule:
- cron: '0 6 * * *' # daily at 06:00 UTC
push:
branches: [main]
pull_request:
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: '1.16'
otp-version: '26'
- name: Install dependencies
run: mix deps.get
- name: Run mix_audit
run: mix deps.audit
- name: Ping Vigilmon heartbeat
if: success() && github.ref == 'refs/heads/main'
run: |
curl -fsS "${{ secrets.VIGILMON_AUDIT_HEARTBEAT_URL }}" > /dev/null
The if: success() condition ensures the heartbeat is only pinged when the audit passes. The github.ref filter ensures only main-branch runs count toward the heartbeat — PR runs verify the code but should not reset the heartbeat timer.
GitLab CI
# .gitlab-ci.yml
security_audit:
stage: test
image: elixir:1.16
only:
- schedules
- main
script:
- mix local.hex --force
- mix deps.get
- mix deps.audit
- |
if [ "$CI_PIPELINE_SOURCE" = "schedule" ] || [ "$CI_COMMIT_BRANCH" = "main" ]; then
curl -fsS "$VIGILMON_AUDIT_HEARTBEAT_URL" > /dev/null
fi
Step 4: Make mix_audit Failures Actionable in CI
By default, mix deps.audit prints a list of vulnerable packages and exits non-zero. To make output more actionable in CI, use the JSON format and parse it:
# Generate machine-readable output
mix deps.audit --format json > audit_result.json 2>&1 || true
# Check for vulnerabilities
VULN_COUNT=$(jq '.vulnerabilities | length' audit_result.json 2>/dev/null || echo "0")
if [ "$VULN_COUNT" -gt "0" ]; then
echo "::error ::mix_audit found $VULN_COUNT vulnerable dependencies"
jq -r '.vulnerabilities[] | " \(.package)@\(.version): \(.advisory.title)"' audit_result.json
exit 1
fi
# Only ping heartbeat on clean audit
curl -fsS "$VIGILMON_AUDIT_HEARTBEAT_URL" > /dev/null
This approach gives you both a structured exit code (for CI) and a clear Vigilmon signal (heartbeat only on clean pass). If you want to alert on vulnerabilities even when CI is configured to allow the build to pass, add a second webhook notification step.
Step 5: Add an HTTP Monitor for Your CI Status API
If your CI system exposes a status API (GitHub, GitLab, Buildkite all do), add a Vigilmon HTTP monitor pointing at your pipeline's last-run status. This gives you a second signal beyond the heartbeat:
GitHub Actions via GitHub API:
- Click Add Monitor → HTTP / HTTPS.
- Enter the workflow runs API URL:
https://api.github.com/repos/YOUR_ORG/YOUR_REPO/actions/workflows/security.yml/runs?per_page=1&branch=main - Add an Authorization header with a GitHub personal access token.
- Under Response body, enable Contains keyword and enter
"completed". - Set Check interval to
60minutes.
This monitor alerts if no completed run appears in the last hour — a signal that the CI schedule is broken before the heartbeat window has even expired.
Step 6: SSL Certificate Monitoring for the Advisory Database
mix_audit fetches the advisory database from hex.pm's security endpoint. If that endpoint's certificate expires or becomes unreachable, mix deps.audit will fail with a TLS error rather than a clean advisory report. Add certificate monitoring so you know before your CI starts failing with cryptic SSL errors:
- Click Add Monitor → HTTP / HTTPS.
- Enter
https://hex.pm. - Enable Monitor SSL certificate.
- Set Alert when certificate expires in less than
14 days. - Click Save.
This is an external dependency you don't control — but knowing about it early gives you time to investigate or work around it.
Step 7: Configure Alert Channels
Security alerts warrant their own routing:
- Go to Alert Channels in Vigilmon.
- Add a dedicated Slack channel for security alerts (e.g.
#security-alerts) separate from your general uptime alerts. - Add email to reach the security or platform team directly.
- Set Consecutive failures before alert to
1for the heartbeat monitor — a single missed audit heartbeat is always a real concern.
Use Maintenance windows when you intentionally pause the CI schedule (e.g. over a holiday freeze):
curl -X POST https://vigilmon.online/api/maintenance \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"monitor_id": "audit-heartbeat-id",
"duration_minutes": 4320,
"reason": "Holiday CI freeze"
}'
Handling False Positives: Ignoring Known Advisories
If an advisory applies to a dependency you've assessed and accepted, ignore it rather than disabling the audit entirely:
# mix.exs — ignored advisories
def project do
[
# ...
deps_audit_ignored_advisories: [
# Advisory ID from https://github.com/dependabot/advisory-database
"GHSA-xxxx-xxxx-xxxx"
]
]
end
With explicit ignores in place, mix deps.audit still exits 0 and pings Vigilmon — confirming the scan ran cleanly rather than the advisory list just being empty.
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| Cron heartbeat | Heartbeat URL after clean audit | Silent CI failure, missed scan schedule, audit errors |
| GitHub Actions HTTP | Workflow runs API | Broken CI schedule before heartbeat window expires |
| SSL certificate | https://hex.pm | TLS errors causing advisory fetch failures |
mix_audit is only useful when it runs. Vigilmon's heartbeat monitoring ensures you always know when it stops — giving you a continuous guarantee that your dependency vulnerability scanning is active, not just configured.