source: advance the checked projection

Source-Sha: 194a59adfd6c901635da7a9c18253bb2210e2426
Policy-Sha: b9b0be0ee74e55f561a803b8aef6be3c2134f5a83b46816a069a372f2eb04f4d
Tree-Digest: 4697f2e281c1c6ecafff17cf6eeb391a0a1ac0fa6e5fc429f5b473f4579c252c
This commit is contained in:
Source publisher 2026-09-21 14:39:08 +00:00
commit ffdccba10f
42 changed files with 2516 additions and 554 deletions

View file

@ -43,7 +43,7 @@ web/src/
│ ├── polling.ts # POLL.* constants — all intervals centralized
│ ├── store.ts, queryParser.ts, vulnerabilities.ts
│ └── client-logger.ts # Client errors ship to the server log (ETHOS #1)
├── desktop/ # Tauri tray-app variant (vite.desktop.config.ts)
├── desktop/ # Retained legacy Tauri web shell; not RedFlag Desktop
└── types/ # Shared TS types mirroring server models
```
@ -65,6 +65,10 @@ web/src/
- Mobile layout usable, not optimized
- Several UI coverage gaps tracked as `UI-*` tasks (not architecture — task tier)
The native RedFlag Desktop is the Qt/QML component described in
[components/05-desktop](05-desktop.md). `web/src/desktop` is retained legacy source, not a
second supported Desktop implementation or an architectural authority.
---
*Last reviewed: 2026-06-14*
*Last reviewed: 2026-09-14*

View file

@ -20,6 +20,28 @@ On Linux the agent invokes it through `sudo systemd-run --wait --property=Protec
### Windows Invocation (SEC-030, decided 2026-07-01)
**Current source boundary:** the native Windows payload now builds the Rust Helper. It
answers its version and supports verification-only binary and `MutationEnvelope` checks.
Windows-only source now opens each trust-path component without following reparse points,
checks SYSTEM/Administrators ownership and denies write grants to other principals, rejects
hard-linked trust files, and creates write-through, one-shot authorization claims. The fixed
task dispatcher reads an envelope from its protected exchange, pins the installed authority
placement, and uses the shared verifier. The wrapper carrying that envelope is decoded
before the envelope itself, so a request the Helper could read but whose envelope it could
not is refused under its own validated request id instead of leaving the caller an unknown
outcome; a wrapper too malformed to establish an identity still writes no result, because
the Helper does not invent one. A shared Go/Rust fixture pins that wrapper on both sides.
The Agent reaches this path read-only through `redflag-agent -verify-envelope`, which mints
nothing, admits no backend, and passes no command or path to the privileged process.
Windows mint and backend execution still return typed refusals.
Native acceptance of the *installed* rail — ACL enforcement, service identity, task
definition, and exchange roots — remains open: the transport is covered by tests, not by a
witness on a provisioned host. Replay refusal is likewise open and deliberately unexercised,
because verification must not consume an authorization; the claim belongs at an execution
boundary that no admitted backend has yet. Low-privilege service migration and WUA / Winget
executors remain required before mutation authority is enabled.
Windows has no `systemd-run` equivalent for spinning up an ad-hoc transient privileged unit, so the helper is invoked through a **Scheduled Task, configured to run once as SYSTEM**. The agent's own service account has no standing right to mutate anything — it only holds a delegated "AllowedToRun" ACE on this one task definition (`schtasks /run /tn RedFlagHelper`), the direct Windows analogue of the Linux sudoers line that grants exactly one `systemd-run` invocation and nothing else.
Two elevation-model alternatives were considered and rejected:
@ -28,6 +50,54 @@ Two elevation-model alternatives were considered and rejected:
The task definition itself is provisioned **at agent-install time** by the (already-elevated) install script, the same moment Linux drops its sudoers entry — not self-provisioned by the agent on first gated operation, which would just relocate the "who grants the first elevation" problem rather than solve it.
`installer/windows/provision-helper.ps1` now prepares that installer-owned boundary. It
creates one triggerless `\RedFlag\Helper` task with a fixed Helper path and `run-request`
argument, grants the Agent service SID read/run rather than task mutation, and places the
Agent-writable request child beneath a non-Agent-writable Helper root beside SYSTEM-owned
trust, replay, and result directories. The staged
payload validates this description but does not run the provisioning script.
`run-request` accepts no command-line arguments. Its machine exchange is fixed at
`C:\ProgramData\RedFlag\helper`; the installer refuses alternate data roots for this
transport. It opens each component relative to a retained directory handle, rejects
reparse points and hard-linked files, bounds input size, and denies concurrent write/delete
sharing while reading. The RedFlag and Helper roots, authority file, and result directory
must pass owner/DACL checks. A handle lock serializes dispatcher instances.
The Agent writes `requests\pending.json`: `version`, a fresh UUID `request_id`, an action
(`verify-envelope` or `execute-envelope`), and the unchanged `envelope`. Unknown envelope
fields and duplicate struct fields are refused. Installer-owned `authority.json` contains
`version`, `target_id`, `authority_kind` (`standalone` or `fleet-server`), `authority_id`, and
`public_keys` (Ed25519 hex). This is local trust configuration. Provisioning accepts it
through `-AuthorityFile`, but refuses to replace an existing different authority; enrollment
and rotation need their own explicit transaction.
The result is a create-new `results\<request_id>.json` containing `version`, `request_id`,
`envelope_verified`, and the common joined `receipt`. Read-only verification reports
`decision=verified`, `executed=false`, and zero verified backend actions. Backend execution
currently reports `backend_not_migrated`; neither path consumes replay authorization.
Protected results are flushed and closed before the Agent can read them. Results stay as
local evidence; retention and timed-out request reconciliation remain follow-up work.
The Agent's Windows envelope executor triggers only `\RedFlag\Helper`, serializes its one
pending slot across processes, validates the full receipt join, and leaves an unresolved
slot intact after task-start failure, timeout, cancellation, or an invalid response. Task
start never counts as execution success. A later call first reads any pending request and
its protected result. A valid joined result retires the slot and is returned when the caller
retried the same action, authorization, and manifest; a different call may proceed while the
old result stays as evidence. Missing, malformed, or mismatched results keep the slot blocked
with its request ID for reconciliation. Windows standalone mint explicitly refuses until
fresh StepUp exists. New Agent service installs select the virtual
`NT SERVICE\RedFlagAgent` account and enable its service SID. Helper provisioning grants
that SID modify access to Agent state/logs, modify access to the untrusted request directory,
read access to results, and no access to authority/replay state. Existing SYSTEM services
use the explicit `-MigrateAgentService` transaction: enable the service SID, prepare ACL/task
ownership while LocalSystem is still available, stop the service, change its account, verify
SCM state, and restore its prior running state. Only an observed LocalSystem service is
migrated; another operator-owned identity is refused. If the new identity cannot start, the
script restores LocalSystem and its prior running state while retaining the prepared narrow
ACLs for a later retry. Native transport acceptance remains open.
The ACL lockdown described here is the v1 cut, not the final word — Casey's call ("that'll do for now"). Revisit if a real gap in the ACE-delegation model surfaces (see `docs/tasks/SEC-030-windows-privileged-mutation-helper.md` Open Questions for what's still unresolved: WUA's COM-driven install path, rollback ownership parity with Linux's `.bak` handling).
---