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).
---

View file

@ -16,7 +16,12 @@ RedFlag's supply chain gate inverts the traditional defense model: instead of **
token. Standalone pacman approval uses a signed `MutationEnvelope`, exact closure archives,
detached package signatures, helper custody, and a joined receipt. Docker, Winget, and
Windows Update still use the signed-command path. Fleet pacman envelope delivery and kernel
enforcement against out-of-band root mutation are not wired.
enforcement against out-of-band root mutation are not wired. The native Windows Helper
can verify binaries and mutation envelopes but refuses mint and backend execution. A fixed
task transport now joins Agent requests to Helper results using handle-relative custody and
installer-owned authority selection. It has no admitted Windows mutation backend and lacks
native acceptance; service privilege migration and execution remain open. Verification does
not consume replay authority. See [Helper invocation](../components/04-helper.md).
---
@ -51,12 +56,12 @@ the unprivileged agent-side consumer. Its *role* as a runtime allow/deny RPC is
### Why this model (two tests it has to pass)
**Cross-platform.** Linux eBPF and macOS ESF can pause an exec and ask a daemon "may this
proceed?" Windows WDAC cannot — it is signature-based, with no runtime callback. A
decision-daemon model therefore has no Windows mapping. A capability token maps onto all
three identically: in every case the enforcement layer only needs to answer "is this
execution authorized," and a verified token + a privileged executor that the OS trusts is
platform-agnostic. We build for the platform with the tightest constraint.
**Cross-platform.** Linux eBPF and macOS ESF can observe or mediate execution at a kernel
boundary. Windows App Control (WDAC) instead decides which code may execute from policy; it
does not authorize one package transaction or protect every file and registry mutation that
an already-trusted process can make. The common primitive is the signed mutation
authorization consumed by a privileged executor. Platform enforcement narrows bypasses
around that executor, but each platform must state exactly what its kernel mechanism covers.
**Protects people.** Protection comes down to where the trust root lives. The signing key
lives at the server (the human-approval authority), off the host. An attacker who fully owns
@ -76,12 +81,15 @@ The two tests converge on the same answer, which is the signal it's right.
| Platform | Enforcement Mechanism | What It Blocks |
|----------|----------------------|----------------|
| **Linux** | eBPF (syscalls/sys_enter_execve) or AppArmor | apt, dnf, yum, pip, npm, bun, docker (CLI) |
| **Windows** | WDAC (Windows Defender Application Control) | winget, npm.cmd, pip.exe, choco, scoop |
| **Windows** | App Control (WDAC) for executable trust; RedFlag kernel driver for covered mutation transactions | Untrusted mutation code; covered file/registry writes when the driver exists |
| **macOS** | Endpoint Security Framework (ESF) | brew, pip, npm, bun, cargo |
**Target distinction:** kernel enforcement sits below userspace wrappers. The current eBPF
scaffold is not connected to the capability model, so RedFlag does not yet claim that an
out-of-band package-manager invocation is blocked.
**Target distinction:** App Control and transaction enforcement are different Windows
layers. WDAC constrains executable trust; it does not prove that trusted code performed an
authorized RedFlag transaction. SEC-030 owns the driver and transaction context needed to
deny covered file and registry mutations outside a verified envelope. Neither that driver
nor the current Linux eBPF scaffold is connected to the capability model, so RedFlag does
not yet claim kernel-level blocking on either platform.
### Trust Chain
@ -252,9 +260,10 @@ The token extends the existing Ed25519 infrastructure rather than introducing ne
verifies the local Arch keyring, and refuses downgrades or operation-name lies. Legacy
registry entries without local paths are not rehashed. The current unit is not
network-isolated.
- **Kernel layer (where applicable).** Linux eBPF / Windows WDAC / macOS ESF deny
package-manager execution except via the trusted executor. This is defense-in-depth design;
the present eBPF scaffold is not wired to the capability model.
- **Kernel layer (where applicable).** Linux eBPF and macOS ESF mediate covered execution.
Windows WDAC constrains executable trust, while a separate RedFlag driver is required for
covered transaction writes. This is defense-in-depth design; none of these transaction
enforcement paths is wired to the capability model today.
---
@ -283,9 +292,10 @@ runtime evidence support them.
rotated, replicated, or held by a federation/guild node without touching the agent↔helper
interface. This is the long-term cross-platform answer hidden in a one-line design choice.
5. **Kernel stops are defense-in-depth, not a prerequisite.** The capability model protects on
a host where eBPF/WDAC/ESF cannot be deployed (locked-down managed box, constrained
container). Kernel enforcement raises the cost of bypass; it does not gate whether the model
means anything. Partial deployment still moves a host out of the soft-target category.
a host where eBPF, a Windows transaction driver, or ESF cannot be deployed (locked-down
managed box, constrained container). Kernel enforcement raises the cost of bypass; it does
not gate whether the model means anything. Partial deployment still moves a host out of the
soft-target category.
6. **No doctrinal knobs.** Signing required and forward-only (no downgrade) are ETHOS doctrine,
not configurable. The token has no "skip verification" path.
@ -476,8 +486,9 @@ and kernel adapters remain.
## Footer: Assumptions & Connections
**Assumption:** The capability model is the floor; kernel-level primitives (eBPF, WDAC, ESF)
are defense-in-depth on top, not a prerequisite. Userspace wrappers alone are bypassable.
**Assumption:** The capability model is the floor; kernel-level transaction primitives
(eBPF, a Windows driver, ESF) are defense-in-depth on top, not a prerequisite. WDAC is a
separate executable-trust layer. Userspace wrappers alone are bypassable.
**Current:** The privileged executor has a narrow argv-only API, no shell, and a stripped
environment; the Agent that hands it capabilities is unprivileged and holds no signing key.
@ -494,6 +505,4 @@ the helper unit. Neither property applies globally until each migrated backend p
---
*Last reviewed: 2026-09-01*
*Last reviewed: 2026-08-26*
*Last reviewed: 2026-09-14*