Watch
1
0
Fork
You've already forked RedFlag
0
Commit graph RedFlag/server/internal
Author SHA1 Message Date
Fimeg
86da7471ec feat: install/sync/RL hardening + upstream version sync subsystem
Agent + install:
- linux installer adds redflag-agent to docker group (idempotent;
  unblocks container scanner detection)
- install.sh / linux.sh.tmpl / windows.ps1.tmpl: detect existing
  refresh_token and skip --register (Flow 2: upgrade in place)
- agent_update.go: remove dead post-restart watchdog; add
  CleanupPostUpdateBackup() called after first successful check-in

Server + token model:
- 409 machine-already-registered now returns existing_agent_id,
  hostname, last_seen, remediation guidance
- RevokeAgent admin handler (invalidates refresh_tokens only)
- GetAgentsBoundToToken query + no-cascade invariant test
- Two-axis revocation locked in: registration_token revoke does NOT
  cascade to agent refresh_tokens

Supply chain (Shai-Hulud defense feature 1):
- services/package_age.go: npm + PyPI registry probes,
  EvaluatePackageAgeGate decision matrix (warn/block/off x
  above/below/unknown)
- ApproveUpdate / ApproveUpdates wired to age gate; stores
  package_published_at + supply_chain_age_check in metadata
- security_settings_service: supply_chain category defaults
  (min_package_age_hours=24, gate_enforcement=warn)

Rate limiting UI rewrite:
- Frontend was expecting per-endpoint configs + usage/summary
  routes that don't exist; backend has 6 named categories
- Rewrote RateLimiting.tsx, useRateLimits.ts, api.ts, types,
  Settings overview card to the real shape (Requests + Window in
  seconds + Enabled per category)

Agent Management UI:
- Replaced auto-pick-first-token with explicit dropdown showing
  prefix, label, seats_used/max_seats, expiry
- One-liner panel only renders when a token is selected; no more
  YOUR_REGISTRATION_TOKEN placeholder in copyable command
- Zero-token state surfaces "Generate Registration Token" CTA

Upstream version sync (new subsystem):
- Migration 035: tracked_software + upstream_drift_events
- ReleaseSource interface + Registry + Repology adapter +
  endoflife.date adapter
- Periodic Syncer goroutine with on-demand SyncOne; classifies
  drift severity (minor/major/eol); appends drift events
- /admin/upstream CRUD + drift + sync-now routes
- Dashboard "Stack Drift" panel (count + worst-3 + EOL flag)

Migration 035 runs idempotently on next startup.
2026-05-23 15:12:20 -04:00
Fimeg
f3893f7850 fix: add security_settings.created_at column
Migration 020 created the security_settings table with updated_at /
updated_by but no created_at / created_by columns. Query code
(database/queries/security_settings.go) SELECTs and INSERTs both
create-side fields, causing "failed to initialize default security
settings" at server startup — the dashboard's security panel then
shows hardcoded defaults instead of DB-backed values.

Adds both columns with NOT NULL + DEFAULT NOW() on created_at and a
nullable FK to users on created_by, matching the updated_by shape.
No backfill scaffolding (no live clients per release stance).

Closes AUDIT_TASKS.md §4.
2026-05-22 14:20:20 -04:00
Fimeg
df81229aba fix: run OSV.dev supply-chain check on bulk approve; drop dead UnifiedUpdateHandler
Single-approve at /updates/:id/approve has always run the OSV.dev
ecosystem check for npm/PyPI packages, but the bulk endpoint
/updates/approve called BulkApproveUpdates directly — a single DB
write with no vulnerability lookup. So selecting N items in the UI
silently bypassed a check the README advertises.

ApproveUpdates now mirrors the single-approve loop: GetUpdateByID,
NeedsSupplyChainCheck, CheckOSVVulnerabilities, then
ApproveUpdateWithVulns when the OSV query returns CVEs (preserving
supply_chain_vulns / supply_chain_checked_at in metadata) or plain
ApproveUpdate when clean. Per-package warnings are aggregated and
returned to the caller. Fail-open semantics from the single-approve
path carry through — OSV unreachable does not block approval.

Also removed server/internal/api/handlers/update_handler.go.
UnifiedUpdateHandler was a parallel implementation of every
UpdateHandler method but NewUnifiedUpdateHandler was never called
from main.go or anywhere else. The file was confusing on grep and
masked which approve path was actually wired.
2026-05-22 14:20:11 -04:00
Fimeg
7fb61a36ba fix: remove updates subsystem resurrection paths
The legacy "updates" virtual subsystem was deprecated (scheduler.go:159
skips it), but five paths kept re-creating the row in agent_subsystems:

- agent migration detection flagged missing "updates" subsystem as a
  missing security feature, prompting the executor to re-add it to the
  local config on every startup migration
- server install-config template, scanner-timeout list, and intervals
  map all kept "updates" alive in the config artifact sent to agents

Removed at all five sites. Scheduler skip logic, per-scanner mapping
helper (subsystems.go:236), and update-report data path remain — they
are not subsystem-row creators.

Historical migration 024_disable_updates_subsystem left intact.
2026-05-22 13:33:50 -04:00
Fimeg
01c8879157 fix: add ON CONFLICT DO NOTHING to subsystem INSERT to prevent tx poisoning
Registration retries hit the (agent_id, subsystem) unique constraint,
which errored out the INSERT and poisoned the entire transaction. The
handler treated this as non-fatal, but PostgreSQL doesn't allow any
further statements in an aborted tx.

Adds ON CONFLICT DO NOTHING + treats sql.ErrNoRows as success.
2026-05-22 12:10:55 -04:00
Fimeg
7abe331bf8 command-lifecycle v2: add received state, disk-persisted dedup, authenticated download, timeout reconciler
Migration 033 adds the 'received' status to agent_commands so the server can
distinguish "agent confirmed receipt" from "sent but may be lost in flight."
Stuck-command re-issuance now excludes received commands — the TimeoutService
handles the longer timeout for those (default 30m) vs the per-poll re-issuer
(sent/pending at 5m).

The agent side: disk-persists executed command IDs to survive restart (closes
the in-memory-only dedup gap), reports received_command_ids on each check-in so
the server transitions sent→received before issuing new work, and authenticates
binary downloads with JWT+X-Machine-ID (was unauthenticated http.Get — would
401 in production).

TimeoutService extended with reconcileAgentUpdates: clears is_updating when
current_version matches updating_to_version (success), or after a 15m threshold
(timeout, with system_event) so the dashboard never shows "updating" forever.
isVersionUpgrade replaced with utils.IsNewerVersion (no panic on 2-part
versions, no false-reject on 4-part).

MarkCommand* failures elevated from [WARNING] to [ERROR] + should_retry
response hint so agents know to re-deliver results (silent drops were ETHOS #1
violations).

Fixes: build broken on public since eac8a012 (command.go accidentally emptied).
2026-05-22 08:45:16 -04:00
Fimeg
eac8a012bf audit: add ETHOS time handling and updated_at NULL issues to tracking 2026-05-21 15:57:27 -04:00
Fimeg
0e26defa1d fix: resolve type mismatch and zero-UUID bugs, add audit task tracking 2026-05-21 14:42:45 -04:00
Fimeg
c8b0ee1040 refactor: rename aggregator-agent to agent for public release 2026-05-21 13:19:18 -04:00