Watch
1
0
Fork
You've already forked RedFlag
0
Commit graph RedFlag/server
Author SHA1 Message Date
Fimeg
7427174d99 deps: lift patched public floors
CI found nine reachable Go issues and a high Axios advisory. Move to the published fixed floors and retire three stale Docker exceptions.
2026-08-20 13:14:28 -04:00
Fimeg
9f47d782af fix(BUG-001): canonicalize pip package type in gate decisions
Server gate functions only recognized 'pypi', while the agent reports
'pip'. This made NeedsCapabilityGate('pip') return false, silently
routing pip installs through the legacy un-gated path.

All four gate functions now accept 'pip' with 'pypi' retained as an
alias for existing DB records.
2026-08-12 20:36:08 -04:00
Fimeg
1f75bfd23a device-type: layered detection + laptop/vm/container types
Replaces the flat battery x display matrix (which misclassified laptops
as phones) with layered detection: container -> vm -> SMBIOS chassis ->
ARM fallback. Adds laptop, vm, container device types across agent,
migration 062, server validation, web icons/types.
2026-07-12 14:55:04 -04:00
Fimeg
ff2f30f47a v0.2.9.3: device classification + ARM support — Pixel 3 lands
DEVICE-002: ARM machine-ID fallback — device-tree model + /etc/machine-id
combo, then /proc/cpuinfo Serial (all-zero rejected), before the weak
hostname fallback. Hardware-bound IDs on DMI-less devices.

DEVICE-001: agent detects device_type (server/desktop/phone/tablet) from
/sys signals — system battery (scope=Device peripherals excluded, UPS
excluded), DRM connector state, framebuffer min-dimension for phone/tablet
split. Reports device_type/device_model/os_distro in registration and
system-info paths.

SERVER-001: migration 061 — device_type, device_type_manual (operator
override, never agent-written), device_model, os_distro on agents.
effective_device_type computed into every serialized agent.

SERVER-002: PUT /admin/agents/:id/device-type — set/clear override,
enum-validated, journaled.

WEB-001: device-type icons + fleet filter, device model in list, detail
header badge with reclassify dropdown, os_distro surfaced.

INSTALL-003: arm64 install path unblocked — helper (required manifest
component) now cross-built aarch64-unknown-linux-musl via rust-lld in the
server image, signed at boot (helperArches += arm64), listed in the release
manifest. Install template already handled uname -m and pacman.

Plus in-flight: desktop tray wiring, enrollment page polish, CI workflow
updates, RAF session-broker/pacman-scanner docs, native installer scaffold.
2026-07-06 18:21:23 -04:00
Fimeg
bf7930f1fe v0.2.9.1: wire pacman scanner, GATE-006 TODO 2026-06-29 15:11:12 -04:00
Fimeg
7035da041d build: fetch the Windows tray from release, don't cross-compile it
from-source builds have no business pulling a 9GB MSVC sysroot for one Tauri
exe. CI builds it once; the server image downloads the signed artifact and
hash-checks it against the manifest. no release / offline -> no tray, server
404-skips. also: make rebuild/up/down/logs so `up -d build` stops biting.
2026-06-15 21:46:36 -04:00
Fimeg
99d97a07ee v0.2.9.0 — Windows desktop tray ships; unified Agents & Enrollment page
Desktop:
- Windows tray cross-compiled (cargo-xwin), installed with per-user
  autostart Run key; tray actions trigger_scan/approve_update wired to
  the local API
- Linux tray off the service child-spawn path — XDG autostart only, kills
  the double-launch
- signalDesktopRestart no longer no-ops on Windows (taskkill /F /IM)
- server serves /desktop/:platform/:arch

Web:
- TokenManagement + AgentManagement folded into one Agents & Enrollment
  settings page (useRegistrationTokens hook)

Agent/server:
- platform-aware self-update staging (constants/paths.go), no more
  hardcoded /var/lib/redflag
- consumer helper gated: sudo systemd-run on Linux, child proc elsewhere
- migration 060 drops the never-used token_seats table
- droppage of dead constructors and orphaned windows.go service methods
2026-06-15 20:51:44 -04:00
Fimeg
e8cd4de44f upstream: keep drift detection-only 2026-06-15 12:24:44 -04:00
Fimeg
9e4d59695f upstream: track prereleases per row; rename gitea adapter to forgejo
Most forges hand back a "latest stable" that ignores prereleases. Fine
until something ships only prereleases for a stretch — us, through
alpha, where every tag under v0.3.0 goes out as a prerelease. Against
that, /releases/latest either freezes or returns nothing, and the
dashboard reads like nothing's moving.

Add a per-row track_prereleases flag. When it's on, the forgejo adapter
walks the full release list and considers prereleases when picking the
highest version; off (the default) keeps stable-only behavior, so the
other adapters don't change. Seed our own self-row on so we stop
looking frozen.

Renamed gitea_releases to forgejo_releases while in there — the wire
format is Forgejo's, Codeberg runs it, and the old name was a misnomer.
Legacy source="gitea" rows still resolve through an alias.
2026-06-15 12:10:12 -04:00
Fimeg
eb79af448e v0.2.8.4 — releases publish as prerelease until v0.3.0 (alpha) 2026-06-15 09:58:24 -04:00
Fimeg
cc19854941 security: latch /admin group behind RequireAdmin (SEC-026)
The /admin group rode on webAuthMW + audit only; just the securitySettings
sibling checked the role. Inert today (login mints role=admin), live the day
RBAC lands — one group-level gate beats per-route annotations to forget.
2026-06-15 09:07:19 -04:00
Fimeg
0b1b8124b0 crypto: forward-only key-path ceiling + OSV resilience + token serialization
SEC-028 -- a rotated-out server signing key must stop being trusted even when the agent cannot phone home. pubkey.go: bounded stale-cache window on public-key fetch failure; past the window (or when cache age is unknown) it fails closed instead of trusting the cached key indefinitely. Window length is operator policy (command_signing.stale_key_max_age_hours, default 168h/7d) delivered fleet-wide via GET /agents/:id/config; the [1h, 30d] clamp and the existence of the ceiling are doctrine, not knobs. verification.go: CheckKeyRotation refuses when the named key_id is not in the server active set (no primary fallback), and applies the same bounded-stale ceiling to the active-set fetch-failure path so key_id'd commands are no weaker than keyless ones. Server carries the default + 1-720h validation; web surfaces it in Security Settings.

SEC-029 -- the standalone OSV.dev client retries transient transport/5xx/429 with exponential backoff and trips a process-wide circuit breaker after a run of failures, fast-failing to 'unreachable'. Verdict semantics unchanged and still fail-closed; the resilience only stops a transient scanner blip from forcing an operator override.

GATE-004 #4 -- Consumer.ProcessToken holds a mutex so the replay-state guards are never raced by a concurrent caller. Today's single caller (the poll loop) never overlaps; this enforces the one-token-at-a-time invariant for future callers (local-API trigger, retry worker).

RAF/verification/03 and RAF/security/05 document the key-path and OSV changes. ETHOS #3, #4; forward-only doctrine.
2026-06-14 12:57:04 -04:00
Fimeg
e0765c29f4 supply-chain: honest self-attestation posture (dep-scan, build provenance, install guard)
dep-scan.sh: capture govulncheck's real exit code and feed the reachability gate only on exit 0 (clean) or 3 (vulns found). Any other exit (crash, timeout, 137) is now INFRA, so a truncated-but-parseable JSON stream can no longer parse clean and yield a falsely attested posture. npm ci / npm audit / cargo audit stderr is captured and surfaced on failure (no more 2>/dev/null).

Dockerfile: the posture-builder stage no longer curl|sh's rustup.rs. The Rust toolchain is COPY --from=rust:1-bookworm (canonical official image, same base as desktop-builder) with RUSTUP_HOME/CARGO_HOME set -- stronger provenance than TLS-TOFU rustup, no pipe-to-shell inside the stage that produces the attestation. docker compose build will validate the new layer.

linux.sh.tmpl: guard the posture check under set -e so an un-attested (exit 3) or blocked (exit 4) verdict degrades honestly instead of aborting the install before POSTURE_RC is captured.

ETHOS #1 (errors are history), #3 (assume failure). CI-002 + CI-003.
2026-06-14 12:56:42 -04:00
Fimeg
e2dab2845a supply-chain: gate our own deps, ship the verdict signed
dep-scan.sh gates go/npm/cargo on push and bakes an attested posture into the
release — embedded in the server, signed into the manifest. Reasoning and the
two Moby exceptions are in SECURITY.md.

(posture-builder runs rustup; bookworm's cargo is too old for cargo-audit.)
2026-06-14 11:43:23 -04:00
Fimeg
27fe605d78 ci: versioned releases publish to codeberg; drop orphaned gitea image push
Codeberg is the public distribution endpoint, so the release job now creates
the release there too — same artifacts as gitea, server image excluded (server
ships as docker-compose build from source). The docker job that built and pushed
redflag-server to the private gitea registry is gone: nothing pulled it once the
install checkoff started reading the running container, and as a publish gate it
could block a public release on a private-registry hiccup.

README clone URL -> codeberg; Dockerfile builds ./cmd/server/ as a package.
2026-06-13 19:58:35 -04:00
Fimeg
23e9c37953 fix(install): server checkoff reads running container, never pulls a registry
The docker-component checkoff ran 'docker run 10.10.20.120:4455/fimeg/redflag'
to read a version string — reaching the private Gitea registry from every
install, and 'grep redflag' matched unrelated containers. Verify the running
redflag-server container's --version locally instead; agent-only hosts skip
cleanly. (INSTALL-001)
2026-06-13 16:17:55 -04:00
Fimeg
9be8aba073 fix: dashboard populates Updates-by-Type and gives honest severity bars
The stats handler initialized updates_by_type but never filled it, so the
"Updates by Type" card always rendered empty (Codeberg #10). Add
UpdateQueries.GetUpdatesByType (grouped by package_type, non-terminal scope)
and wire it into GetDashboardStats.

Severity bars were sized against total_updates (all statuses) while the
severity counts are scoped to non-terminal rows — a scope mismatch. Bars now
size against the sum of the scoped severity values, so they form a true
breakdown that always sums to 100% and never overflows.

Also closed the silent error-swallows in GetDashboardStats: each sub-count
failure is now logged [ERROR] [server] [stats] instead of vanishing.
2026-06-13 08:23:20 -04:00
Fimeg
6a59d46631 fix: escape docker container check from Go template parser
docker ps --format '{{.Names}}' gets eaten by text/template before
Docker sees it. Use docker container ls + grep instead.
2026-06-12 15:38:38 -04:00
Fimeg
88b612c77e feat: component manifest drives installs, checkoff, and desktop lockstep
INSTALL-001: manifest schema with components+artifacts, CI generation
in release gate, manifest-driven install template with --guided and
--checkoff modes, post-install provisioning checks, desktop joins
version lockstep (bump-version.sh + CI build + gate enforcement).

Setup.tsx reduced to primitives (FormSection, TextField, Alert).
2026-06-12 15:23:15 -04:00
Fimeg
415f229b8f web: server status awareness — version header, disconnect overlay, update banner
X-RedFlag-Version on every response; axios interceptor tracks connection state, no dedicated health poll. Recovered from stash — never meant to be parked.
2026-06-12 13:41:11 -04:00
Fimeg
070148f1f5 test: set issuer claim in auth verify test tokens
Now mandatory after the JWT issuer grace period closed.
2026-06-11 20:58:28 -04:00
Fimeg
7670594ac3 sec: issuer claim mandatory — grace period closed
Grace shipped 2026-05-21 for pre-issuer tokens; forward-only removal.
Empty issuer now rejects like a wrong one.
2026-06-11 20:50:27 -04:00
Fimeg
2b1189aaa2 sec: hash failure now blocks approval — fail closed
Hash registry is the tamper gate; approving without it was fail-open.
Empty hash with no error (no artifact) still passes.
2026-06-11 20:48:05 -04:00
Fimeg
6a4be4d12f refactor: timeout + reconciler tickers onto bgRunner
Last two raw tickers; now they get shutdown, panic isolation,
and /health/tasks like everything else.
2026-06-11 20:48:04 -04:00
Fimeg
1af5a9a184 test: guard test learns the bgRunner.Every pattern
Ticker consolidation moved refresh_token_cleanup into the taskrunner;
the source-grep guard only knew go func/ticker.
2026-06-11 20:39:32 -04:00
Fimeg
6cec9aa8ac test: atomic counter in notifier countingSink
Dispatch fires sinks in goroutines; the plain-int counter tripped
go test -race. CI red since the notification bell landed.
2026-06-11 20:27:28 -04:00
Fimeg
dee6149c06 bump: v0.2.8.2
Version lockstep across server, agent, docker-compose, helper.
2026-06-11 20:25:08 -04:00
Fimeg
413211c657 fix: idempotent Stop() + surface discarded errors
sync.Once on TimeoutService/Reconciler/Syncer stop channels.
syncOne returns its error so admin SyncNow reports real failures.
OSV vuln unmarshal failure now logged, still fails closed.
2026-06-11 20:23:35 -04:00
Fimeg
32c195ef2f sec: stop trusting Host header for agent-facing URLs
resolveServerURL: operator-configured REDFLAG_PUBLIC_URL wins;
request Host only as fallback with a [WARN]. Applied to install
scripts, registration responses, and fleet-join. Host header is
attacker-controllable on pre-auth endpoints.
2026-06-11 20:23:35 -04:00
Fimeg
43ddaab262 cleanup: remove unwired lifecycle/build services
AgentLifecycleService, ConfigService, BuildService, ArtifactService,
AgentBuildHandler — added as a unification pass (e56888e6), never
instantiated by any commit since. ~600 lines.
2026-06-11 20:17:26 -04:00
Fimeg
c6e430225c setup: read bootstrap DB password from environment
Server container shares the bootstrap .env with postgres, so the
current password is already in our environment. Hardcoded literal
kept only as fallback for the shipped default.
2026-06-11 20:17:26 -04:00
Fimeg
230b2f9ab9 fix: comma-ok guards on agent-supplied metadata and gin context
Malformed agent metadata could panic the server (rapid_polling
fields, buffered event metadata, timeout params). scanner_config
asserted uuid.UUID on a user_id the middleware stores as string —
guaranteed panic on both admin endpoints.
2026-06-11 20:17:26 -04:00
Fimeg
86b0944551 wire: auth audit registration + background task consolidation
AUDIT-002: webAuth/agentAuth/metricsAuth registered with the route
auditor before AuditAndExit. RETAIN-001: retention sweep on bgRunner.
Offline-agent and refresh-token tickers migrated to bgRunner.Every;
upstream syncer + reconciler now stopped on shutdown.
2026-06-11 20:17:26 -04:00
Fimeg
7beb0b9478 bump: v0.2.8.1
Version lockstep across server, agent, docker-compose.
2026-06-11 17:47:42 -04:00
Fimeg
182536a1d1 retain: core retention sweep (RETAIN-001)
Prunes aged rows from append-only history tables on a schedule.
Three operator-tunable horizons (metrics/events/audit), 0 = keep
forever. ctid-based batched DELETE, closed-table whitelist.

Signed-off-by: Fimeg <casey.tunturi@gmail.com>
2026-06-11 17:47:30 -04:00
Fimeg
75346d4c68 sec: encrypt TOTP seed at rest (migration 058)
Store the fleet-join TOTP seed as AES-256-GCM ciphertext instead of
a SHA-256 hash. Hash-only can never verify a time code without the
host disclosing the seed — which made the 2FA a second cleartext
shared secret. Seed crosses the wire once, at token creation over
the admin-authenticated channel; the join request carries only the
6-digit code.
2026-06-11 17:47:23 -04:00
Fimeg
a4d585c79c code review: 12-finding fan-out — fixes across server, agent, web
HIGH:
- URL sync race: page useEffect now preserves filter params from useFilterUrl
- useFilterUrl: document two-effect pattern (state→URL and URL→state)
- Fleet-join: store nil (not &"") for absent MachineID/PublicKeyFingerprint
- agents.go: same NULL fix for standard registration path

MEDIUM:
- Test assertions: replace CSS class checks with user-visible element assertions
- Updates vuln toggle: fixed-set like other quick filters (was toggling)
- ConfigureSecrets route: restore to welcome-mode server
- Config upgrade: recursive mergeMissingKeys for nested sub-fields + test

LOW:
- LiveOperations: wire FilterBar pills/clearAll/activeCount
- HashTOTPSeed: remove dead code replaced by encrypted storage (migration 058)
- auditor.go: replace unsafe reflect with Recorder wrapper (AUDIT-002)

History filter panel kept as-is (collapsible pattern intentional).
Agents.tsx duplicate buildFilterPills was a false positive (already resolved).
2026-06-11 17:38:08 -04:00
Fimeg
d55026aa44 process scan: set Source on AgentCommand to avoid check constraint crash
TriggerProcessScan was building an AgentCommand without Source, which hit
the agent_commands_source_check constraint every time. Every other
command-creation site sets this correctly; this one got missed.

Thanks to QiTechCo for the catch during v0.2.8.0 dev testing.

Co-Authored-By: Xzaviar <user@QiTechCo.qitc.io>
2026-06-11 17:24:06 -04:00
Fimeg
44d7e3dd66 global events: notification bell feed + exclude client_error from operator alerts 2026-06-11 13:38:31 -04:00
Fimeg
34d4a02826 install template integrity test — render, config keys, scriptlet syntax 2026-06-11 13:38:17 -04:00
Fimeg
5e710f597b notifier: ntfy + SMTP event dispatch, wired through SystemEventLogger 2026-06-11 13:37:53 -04:00
Fimeg
4c1da83d8d SEC-025: wire fleet-join endpoint + notifier dispatcher (main.go) 2026-06-11 13:37:41 -04:00
Fimeg
9c43698b00 SEC-025: fleet-join 2FA — TOTP validation, host-generated seed, migration 057 2026-06-11 13:37:17 -04:00
Fimeg
6debe1f729 global events query + handler — fleet-wide notification bell feed 2026-06-11 11:33:07 -04:00
Fimeg
7d1ad50399 wire agent + global event endpoints (LIFECYCLE-006 groundwork) 2026-06-11 11:32:51 -04:00
Fimeg
2064a5035f supply chain vuln UI — backend endpoints + frontend rendering for CVE/advisory detail 2026-06-11 11:32:24 -04:00
Fimeg
da455b77a5 fix: severity stats scoped to non-terminal statuses + consistent type icons (UI-DASHBOARD-AUDIT #2, #4) 2026-06-11 08:45:27 -04:00
Fimeg
937b3a5b6d fix: desktop-self token missing ArtifactPath — stageClosureArtifact requires it 2026-06-11 08:30:11 -04:00
Fimeg
b2319e15ed feat: mint desktop-self tokens on agent update (UPDATE-002) 2026-06-11 08:24:46 -04:00
Fimeg
7269d25823 fix: route audit misclassified inlined AuthMiddleware — server refused boot
The compiler inlines middleware.AuthMiddleware across packages, renaming
its closure to "<caller>.AuthMiddleware.func1" — the package-qualified
match ("middleware.AuthMiddleware") never hit, every agent-JWT route
was flagged as naked, and AuditAndExit refused boot (122 container
restarts overnight). Classify on bare names, web before agent since
WebAuthMiddleware contains AuthMiddleware. Regression test now uses the
real production middleware instead of a same-package fake.
2026-06-11 07:59:13 -04:00