feat(reports): add GET /reports/scans endpoint for defenseur-auto #6
No reviewers
Labels
No labels
source:analyste
source:defenseur
source:human
source:medic
status:approved
status:blocked
status:in-progress
status:needs-fix
status:ready
status:review
status:triage
type:bug
type:feature
type:infra
type:refactor
type:schema
type:security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: maximus/vps-health-api#6
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/reports-scans-endpoint"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds
GET /reports/scans?date=YYYY-MM-DDsodefenseur-auto(workstation cron) can fetch scan reports over HTTPS instead of SSH/rsync — the current Tailscale SSH session expiry breaksrun-auto.shpre-rsync intermittently./healthand/defenseursHEALTH_TOKEN(no new secret)<REPORTS_DIR>/defenseur-*_<date>*.jsonwithisScanReportfilter (excludesdefenseur-auto_*.json— same guard asdefenseurs/src/report.ts)test-curl.sh(run locally — no test runner installed in this repo)Spike validated upstream :
~/claude-code/.spikes/archived/endpoint-reports-sur-vps-health-api-pour/(Phase 3 implementation of P4d).Coolify changes (manual, post-merge)
Apply via Coolify UI before redeploy is meaningful (default fallback
/data/defenseurs/reportswill work in absence, but explicit is better) :REPORTS_DIR=/data/defenseurs/reports(is_runtime=true,is_buildtime=false)/data/defenseurs/recursively (the Sergent writesstatus.jsonthere) — no change neededValidation post-merge
Expected : a positive count (Defenseurs scanners run at 01:00 UTC) and a list of agents (no
defenseur-auto).Risks
From the spike HANDOFF :
defenseur:defenseuron the VPS. The container runsUSER node(Dockerfile L5). If 200 +count=0post-deploy, check container can read the bind-mount (workaround :chmod 644cote sergent or relaxUSER nodewith a supplementary group).reports=[](date inexistante or mount cassé), the workstation pipeline runs with 0 findings without a warning. Mitigation pending in the consumer PR (logs).vps-health-apireachability. Same dependency as/defenseurs(already prod-critical for the home dashboard).Test plan
test-curl.shpasses locally (11/11)Replaces the SSH/rsync canal between Max's workstation cron and the VPS for fetching defenseur scan reports. The defenseur-auto orchestrator now pulls reports/defenseur-X_<date>*.json over HTTPS, reusing HEALTH_TOKEN. The handler mirrors the style of index.js (HTTP native, no framework), includes the same isScanReport guard as defenseurs/src/report.ts (filters out defenseur-auto_*.json run reports), and validates the date param against /^\d{4}-\d{2}-\d{2}$/ to short-circuit path traversal before any filesystem access. Validated by test-curl.sh — 11 cases covering auth, validation, date filter, isScanReport filter, sort order, GET-only and 404 paths. Spike: ~/claude-code/.spikes/archived/endpoint-reports-sur-vps-health-api-pour/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Review: APPROVE
Summary : implementation propre et bien delimitee. Le regex date court-circuite avant tout acces FS (path traversal bloque),
isScanReportreproduit fidelement le guard dedefenseurs/src/report.ts, et le double-checkparsed.timestamp.startsWith(date)evite les faux positifs si un nom de fichier est forge. Style coherent avec/healthet/defenseurs.Verifications passees
^\d{4}-\d{2}-\d{2}$bloque traversal/injection ; bearer auth reutilise ; fail-closed preserve ; pas de secrets dans le diff.validRoutes.includes(pathname)filtre apres strip de la query string ; sort timestamp asc conforme a la convention amont ; defenseur-auto exclu viaisScanReport(pas defindings[]).test-curl.shcouvre 10 scenarios (auth, missing/invalid date, traversal, POST, sort, filter auto, date filter, unknown date). Pas de test runner dans le repo — script bash assume comme regression suite, c'est documente dans le header..env.example+CLAUDE.mda jour.Suggestions non-bloquantes
Fixes #Ndans le PR body — empeche le label automation cote issue. Pour la suite, ajouter le numero d'issue de provenance (probablement issue 4 ou 5 dans defenseurs).existsSyncetreaddirSync: si quelqu'un supprime le dir entre les deux, ca jettera. Letry/catchautour du JSON.parse ne couvre pas lereaddirSync. En pratique le bind-mount Coolify ne disparait pas — mais on pourrait juste droper leexistsSyncet laisser letryglobal au handler attraper l'ENOENT (if (err.code === 'ENOENT') return []). Pas urgent.count=0qui serait suspect (le sergent ecritdefenseur:defenseur, le container tourneUSER node).Rien ne bloque le merge. Bon a deployer.