Daily security agent
A report-only agent route for the last 24h of commits, lightweight vulnerability scanning, threat-model drift, and ownership risk. It turns concrete findings into focused Linear tickets without changing code.
Operating model
report-onlyThe job should behave like the existing Sentry and E2E report routes. A systemd timer creates a dated Linear issue, Symphony picks it up through a dedicated label, Codex runs read-only checks, and the run completes with a report. The only writes are follow-up tickets for confirmed findings, one report comment, and a compact Slack summary.
Daily security should catch newly introduced risk quickly. Broad threat-model and ownership graph refreshes are valuable, but they belong in a weekly audit or in a triggered follow-up when the daily run detects drift.
System shape
click the nodesThis is a thin timed-job layer on top of the existing Symphony workflow. It uses the same timed issue pattern as Sentry triage and local E2E, then routes the issue into a report-only security policy. Click any node to trace the run.
Cadence design
daily vs weeklyThe right split is a fast daily delta plus a slower weekly audit. Use the control to compare the run contract.
Fast loop: catch new risk quickly
The daily run is intentionally narrow. It checks what changed in the last completed 24h, then adds lightweight whole-system scanners so obvious vulnerabilities are not missed.
- Review commits merged in the fixed 24h UTC window.
- Run dependency audit, secret scan, focused Semgrep, and workflow lint.
- Create Linear bugs only for actionable, reproducible findings.
- Post one report comment and one Slack summary.
Daily run remains focused. Below 75%, the report should create a weekly-audit follow-up instead of pretending the daily pass is complete.
Analysis tracks
interactiveFour tracks make the job understandable. The first two run daily. The last two are daily drift detectors with heavier weekly refreshes.
New commits
Review merged commits and changed files first. The job looks for security-sensitive surfaces instead of reading every file with equal weight.
- git log window
- changed files
- PR metadata when available
- high-risk commit list
- review notes
- follow-up tickets for concrete bugs
Commit risk map
24h reviewThe delta review should rank changed paths by risk before spending agent time. This keeps the report readable and makes the review bias explicit.
969284787458Finding rubric
ticket rulesThe agent should not create tickets for vague concern. It creates tickets for confirmed, actionable findings with deterministic idempotency labels.
High findings
Use for likely-reachable high-impact vulnerabilities or weakened protection on sensitive paths.
- reachable critical CVE
- missing authorization check
- unsafe file upload
- SQL injection risk
security-finding:<hash>
cve:<CVE-ID>
secret-scan:<hash>
semgrep:<rule-id>:<hash>Run contract
workpadThe Linear issue description should carry the contract and the workpad. Completion is gated on concrete evidence, not a prose summary.
Timed issue
Title: Daily security agent - YYYY-MM-DD. Labels: report:security-agent, security, observability.
Report-only route
No code edits, commits, pushes, PRs, deploys, or Sentry mutations. Allowed writes are Linear follow-up bugs, one report comment, and one Slack summary.
Code HEAD: <sha>Security window UTC: <start>..<end>Commits reviewed: <count>Security-sensitive commits: <count>Dependency audit: passed | findings | blockedSecret scan: passed | findings | blockedSAST scan: passed | findings | blockedThreat-model drift: none | findingsOwnership risk: none | findingsNew Linear findings: <ids or none>Slack summary: <url/timestamp or stdout fallback>Scanner plan
first implementationStart with portable checks that can degrade cleanly. A missing tool should be recorded as a coverage gap, not reported as a clean pass.
git log --since="$WINDOW_START" --until="$WINDOW_END" --name-only --format=fullernpm audit --audit-level=high || truegitleaks detect --source . --redact --no-git || truesemgrep scan --config auto --error --timeout 120 || true./scripts/lint-workflows.sh || actionlintIf Semgrep, gitleaks, npm auth, package manager state, or repo hydration is unavailable, the run should finish with a degraded result and a clear unblock action. A silent skip is worse than a failed check.
Rollout path
ship safelyDry run locally no writes
Generate the report issue body and scanner plan with --dry-run. Confirm labels, title, and workpad rows.
Enable daily report writes gated
Create only the report issue and Slack summary. Hold follow-up bug creation behind a config flag for the first week.
Allow focused bug tickets idempotent
Turn on bug creation for Urgent and High findings first, then expand to Normal once duplicate suppression is proven.
Add weekly audit slow loop
Regenerate ownership maps and threat-model reports weekly, using daily drift notes as the input queue.