platform_security
| What it does | Secure Boot and code-integrity enforcement posture (read-only) |
| Version | 1.0.0 |
| Kind | Collector · read-only · gathered (crossplatform.platform_security.secure_boot, crossplatform.platform_security.code_integrity) |
| Platforms | Windows ✅ · macOS ✅ · Linux ✅ |
| Actions | code_integrity (definition crossplatform.platform_security.code_integrity) · secure_boot (definition crossplatform.platform_security.secure_boot) |
| Security | securable Security · operation Read · risk Low · dispatch ReadOnly · approval gate None |
| Roles | execute: endpoint-admin, endpoint-operator, security-admin · author: content-author |
How it works
Two read-only actions report whether boot-time and code-signing protections are configured on: secure_boot (is Secure Boot enforced) and code_integrity (is the OS restricting what code may run). Each writes rows of the form <action>|<os>|<key>|<raw>|<state>. Neither takes parameters, so no request text reaches a path, a registry name or a command line. Linux reads a few efivarfs and securityfs files; Windows reads registry values; macOS reports secure_boot as unsupported and runs spctl --status and csrutil status for code_integrity. Every read is a value with a state, absent (the OS definitively says it is not there) or unreadable (the read failed), and a failed read never reads as absent. enabled, disabled and partial describe the protection, not the raw number. The Windows rows report configured policy, not proof of what is running.
flowchart LR
OP["Operator or workflow"] --> SRV["Server, authz Security Read"]
SRV -->|"gRPC over mTLS"| EX["platform_security execute: secure_boot or code_integrity"]
EX --> WIN["Windows leg: RegQueryValueExW on the SecureBoot State, CI Policy, DeviceGuard and Lsa keys"]
EX --> MAC["macOS leg: secure_boot writes an unsupported row, code_integrity runs spctl and csrutil with a 5 second deadline"]
EX --> LIN["Linux leg: open and read of efivarfs SecureBoot and SetupMode, securityfs lsm and lockdown"]
WIN & MAC & LIN --> ROWS["rows with raw value and state, status OK, CONSTRAINED or PERMISSION_DENIED, UNAVAILABLE for macOS secure_boot"]
ROWS --> RS[("ResponseStore")] --> API["REST /api/responses"]
This plugin’s priority is asserted, not evidenced: it was built from a reading of a capability gap, and no capability-map requirement, parity gap, compliance control, roadmap item or named customer asks for it.
What is read
- Linux (
openandread, errno classified, no process started).secure_bootreads the efivarfsSecureBoot-*andSetupMode-*variables (a 4-byte attribute word plus one data byte).code_integrityreads/sys/kernel/security/lsm(the active LSM list) and/sys/kernel/security/lockdown(the bracketed active mode). - macOS.
secure_bootwrites oneunsupportedrow (there is no public API; SIP is reported undercode_integrity).code_integrityruns/usr/sbin/spctl --status(Gatekeeper) and/usr/bin/csrutil status(SIP) as fixed commands through a time- and output-limited subprocess runner: absolute path, no shell, no search path, a 5 second deadline and an output cap. Neither state has a library API. - Windows (
RegQueryValueExW, 64-bit view, underHKLM\SYSTEM\CurrentControlSet\Control).SecureBoot\State(UEFISecureBootEnabled);CI\Policy(every value, names enumerated first, then one read each);DeviceGuard(EnableVirtualizationBasedSecurity,RequirePlatformSecurityFeatures,HypervisorEnforcedCodeIntegrity);DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity(Enabled); andLsa(LsaCfgFlags).LsaCfgFlagsis the Credential Guard setting and lives underControl\Lsa, notControl\DeviceGuard:1is enabled with a UEFI lock (enabled_locked),2is enabled without one.
How absence is decided
A BIOS or CSM (legacy-BIOS compatibility) boot has no efivars, many kernels have no lockdown LSM (Linux Security Module) and a default Windows install has no DeviceGuard values. Each of these is an absent row that adds no reason token and leaves the status OK. The rule differs by OS, because a not-found can also mean the surface itself was never exposed.
- Linux. A leaf
ENOENTisabsentonly when the parent pseudo-filesystem is confirmed present. Forcode_integrity,/sys/kernel/securitymust be mounted as securityfs (checked by thestatfsmagic number); if it is not, theENOENTbecomesENODEVand the row readsunreadablewith<key>:errno_19. Forsecure_boot,/sys/firmware/efi/efivarsmust be efivarfs whenever/sys/firmware/efiexists; if/sys/firmware/efidoes not exist at all the host is treated as non-UEFI and the variables readabsent. A refusal (EACCES,EPERM) isunreadablewith<key>:eacces, and a file of 4096 bytes or more isunreadablewith<key>:errno_27. - macOS.
spctlandcsrutilare sealed-system-volume binaries with no legitimate “not there” case, so a tool that cannot be started (including a missing binary) readsunreadable, neverabsent. Text that parses is trusted whatever the exit status. - Windows. The decision is made per key.
SecureBoot\State,CI\Policyand bothDeviceGuardkeys can genuinely be missing on a real host and readabsent.Lsais foundational NT infrastructure with no evidenced not-found case, so a missingLsakey readsunreadablewithlsa:key_missing; aLsaCfgFlagsvalue missing from an openedLsakey is still legitimatelyabsent.
What you can decide with it
| Question | Action and key | Windows | macOS | Linux |
|---|---|---|---|---|
| Is Secure Boot enforced? | secure_boot | UEFISecureBootEnabled: 1 enabled, 0 disabled; absent on a BIOS or CSM boot | not available (unsupported) | secure_boot row: efivar data byte 1 enabled, 0 disabled |
| Is a Platform Key enrolled, or can any image load? | secure_boot, key setup_mode | not read | not available | enabled means User Mode (a Platform Key is enrolled); disabled means Setup Mode |
| Is the OS restricting kernel or code changes? | code_integrity | deviceguard.* (virtualization-based security and hypervisor-enforced code integrity, HVCI); lsa.LsaCfgFlags is Credential Guard (isolated credentials), not code or kernel protection | gatekeeper and sip | lockdown: none disabled, integrity partial, confidentiality enabled; lsm enabled when SELinux, AppArmor, Smack or TOMOYO is in the active list |
| Is Smart App Control on? | code_integrity | ci_policy.VerifiedAndReputablePolicyState: 0 disabled, 1 enabled, 2 evaluation | not available | not available |
- What the Windows states mean.
deviceguard.EnableVirtualizationBasedSecurity,deviceguard.HypervisorEnforcedCodeIntegrityanddeviceguard.hvci_scenario_enabledread0disabled,1enabled.deviceguard.RequirePlatformSecurityFeaturesreads0disabledand1or3(Secure Boot, plus DMA protection)enabled.lsa.LsaCfgFlagsreads0disabled,1enabled_locked,2enabled. Any other value isunmodelledand the raw value is kept. Every other value found underCI\Policyis listed by name asunmodelled. - What the macOS states mean.
gatekeeperisenabledforassessments enabledanddisabledforassessments disabled.sipisenabledordisabledfor the exact status line, andpartialforenabled (Custom Configuration).. Any other text isunmodelled. - Absent is an answer, not a pass. On Linux,
absentonsecure_bootmeans no efivars were visible to the agent; inside a container it does not tell you the host’s firmware mode (caveat 1). On Windows,absentfor adeviceguard.*row means the feature is not configured, not that it is running. - Not covered. Whether a Windows feature is actually running, macOS boot-security policy on Apple silicon, and TPM or measured-boot state.
- Per OS. Keys and states differ by OS, so a fleet check branches on the
<os>column.OKmeans every key was read or definitively absent; it is not a compliance verdict.
Diagrams
Linux read of one variable or file
flowchart TD
A["open the efivar or securityfs file"] --> B{"open result"}
B -->|"opened"| C["read, at most 4095 bytes"]
C -->|"expected shape"| V["decode and map to a state"]
C -->|"efivar not 4 + 1 bytes"| U1["unreadable, key:efivars:shape"]
C -->|"4096 bytes or more"| U2["unreadable, key:errno_27 (file too large)"]
B -->|"ENOENT"| D{"parent surface present?"}
D -->|"surface missing"| U3["unreadable, key:errno_19"]
D -->|"present, or no /sys/firmware/efi"| AB["absent, no token"]
B -->|"EACCES or EPERM"| U4["unreadable, key:eacces, status PERMISSION_DENIED"]
B -->|"other errno"| U5["unreadable, key:errno_n"]
“Surface missing” means securityfs is not mounted at /sys/kernel/security, or /sys/firmware/efi exists but /sys/firmware/efi/efivars is not efivarfs. A host with no /sys/firmware/efi takes the absent branch, and a container that hides that directory is indistinguishable from a BIOS boot, which is why caveat 1 exists.
macOS tool outcome
flowchart TD
R["run spctl or csrutil with a 5 second deadline"] --> Q{"outcome"}
Q -->|"could not start"| U1["unreadable, key:spawn_failed, eacces or errno_n"]
Q -->|"deadline hit"| U2["unreadable, key:timeout"]
Q -->|"output cut at the cap"| U3["unreadable, key:output_truncated"]
Q -->|"ran"| P{"first line matches a known status?"}
P -->|"yes, any exit code"| S["enabled, disabled or partial"]
P -->|"no, exit 0"| M["unmodelled, raw text kept"]
P -->|"no, non-zero exit"| U4["unreadable, key:exit_n or key:exit_signal"]
OS capability
| Action | Windows | macOS | Linux |
|---|---|---|---|
code_integrity | ✅ supported · rung 1 · HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy, Control\DeviceGuard and Control\Lsa\LsaCfgFlags registry values | ✅ supported · rung 2 · spctl —status + csrutil status via run_bounded_subprocess | ✅ supported · rung 1 · securityfs reads of /sys/kernel/security/lsm and /sys/kernel/security/lockdown (errno-classified absent/unreadable) |
secure_boot | ✅ supported · rung 1 · HKLM\SYSTEM\CurrentControlSet\Control\SecureBoot\State registry (UEFISecureBootEnabled) | ⛔ unsupported | ✅ supported · rung 1 · efivarfs reads of /sys/firmware/efi/efivars/SecureBoot-* and SetupMode-* (4-byte attributes + 1 data byte; errno-classified absent/unreadable) |
Declared limits per leg (descriptor fallback text, verbatim):
secure_boot/ macOS — no public API; SIP reported under code_integrity
Privileges and prerequisites
| OS | Runs as | Extra grant needed | Measured | If the read is refused |
|---|---|---|---|---|
| Windows | agent service account (LocalSystem today, #1442) | None documented for this plugin (docs/agent-privilege-model.md); HKLM Control reads are not expected to need elevation | NT AUTHORITY\SYSTEM on a bare-metal Windows 11 Pro host (10.0.26200), 2026-09-21: SecureBoot\State UEFISecureBootEnabled was 1 (Secure Boot on; the off state was never captured), CI\Policy held 4 REG_DWORD values, the DeviceGuard, HVCI and LsaCfgFlags values were absent (no VBS, HVCI or Credential Guard on state was ever measured); the sample is docs/samples/windows.txt | For a per-VALUE read, ERROR_FILE_NOT_FOUND/ERROR_PATH_NOT_FOUND reports the row absent with no token. For a KEY OPEN, the same two codes report absent only for SecureBoot\State, CI\Policy, DeviceGuard and its HVCI scenario subkey; Lsa has no evidenced not-found case, so the identical codes there report unreadable with lsa:key_missing. ERROR_ACCESS_DENIED reports unreadable with <name>:access_denied and the action reports PERMISSION_DENIED/PARTIAL; any other Win32 error reports unreadable with <name>:win32_<n> |
| macOS | agent daemon, root today (LaunchDaemon carries no UserName key, see the docs/agent-privilege-model.md TL;DR); both tools need no elevation to report | None; spctl --status and csrutil status are unprivileged | Bare-metal Mac, unprivileged (euid 501), 2026-09-21: real capture (docs/samples/macos.txt); no root-privileged run has been captured | A tool that cannot be started reports unreadable with <key>:spawn_failed (no errno) or <key>:eacces / <key>:errno_<n> (from the spawn errno, including a missing binary as errno_2); the other failures are <key>:timeout, <key>:output_truncated, <key>:exit_<n> and <key>:exit_signal |
| Linux | agent daemon, dedicated unprivileged account (the agent never runs as root on Linux) | None documented for this plugin (docs/agent-privilege-model.md) | aarch64 Debian 13 container, root, 2026-09-23: real capture of the built .so (docs/samples/linux.txt). secure_boot reads absent (the gap in caveat 1) and code_integrity reads unreadable with errno_19, which the plugin reports when securityfs is not mounted, so the container is inferred to have no securityfs and no EFI variables (the mounts themselves were not recorded); no real Secure Boot value or lockdown mode has been captured | EACCES/EPERM reports the row unreadable with <key>:eacces and the action reports PERMISSION_DENIED/PARTIAL. ENOENT reports absent with no token only when the parent surface is confirmed present (securityfs mounted; for efivars, /sys/firmware/efi absent or efivarfs mounted); otherwise it is remapped to ENODEV and reports unreadable with <key>:errno_19. Any other errno reports unreadable with <key>:errno_<n> |
Binaries and subprocesses: macOS code_integrity runs exactly two, /usr/sbin/spctl --status and /usr/bin/csrutil status (each by absolute path, no shell); every other leg is an in-process read and spawns nothing. Network: none.
Data contract
Inputs
Neither action takes parameters.
Outputs
Pipe-delimited rows written via write_output(), in read order. The first field is the fixed action literal (secure_boot or code_integrity), then <os>, <key>, <raw> and <state>. <raw> is - when nothing was read (absent, unreadable, unsupported); a raw value that was read passes through the shared untrusted-output escaper, which turns a backslash into / and escapes a pipe.
- Row order. Linux
secure_bootwritessecure_bootthensetup_mode; Linuxcode_integritywriteslsmthenlockdown; macOS writesgatekeeperthensip. - Windows row count. Rows can outnumber the named values, because every
CI\Policyvalue is listed, sorted by name, with the one modelled value mapped and the restunmodelled. - Return codes. The action returns 0 for every data-level outcome; an unknown action name returns 1 with the single row
unknown action: <name>, and an internal exception returns 1 with the single two-field rowconstrained|internal_error.
crossplatform.platform_security.code_integrity — row_kind|os|key|raw|state
| Field | Type | Values | Available | Example | Description |
|---|---|---|---|---|---|
row_kind | string | code_integrity constrained | Windows, Linux, macOS | code_integrity | Row family: code_integrity (one per key read) or constrained (an internal-error row: the two-field row constrained|internal_error, which carries no os, key, raw or state). |
os | string | linux macos windows | Windows, Linux, macOS | linux | The leg that produced the row. |
key | string | - | Windows, Linux, macOS | lsm | Value read: lsm and lockdown (Linux); gatekeeper and sip (macOS); ci_policy. |
raw | string | - | Windows, Linux, macOS | capability,landlock,yama,apparmor | The raw text or number read (the LSM list, the lockdown line with its bracketed active mode, the first line of tool output, or the registry value); ”-” when nothing was read (absent or unreadable). |
state | string | enabled disabled partial evaluation enabled_locked unmodelled absent unreadable | Windows, Linux, macOS | enabled | enabled, disabled and partial describe the PROTECTION, not the raw value (Linux lockdown integrity is partial, confidentiality is enabled; macOS SIP with a custom configuration is partial). evaluation (Smart App Control in evaluation mode) and enabled_locked (Credential Guard with a UEFI lock) are Windows-only. unmodelled = a value was read that this table has no interpretation for (raw keeps it). absent = the OS definitively says it is not there (no lockdown LSM, no DeviceGuard values on a default Windows install), a normal answer and not a failure. unreadable = the read failed. |
crossplatform.platform_security.secure_boot — row_kind|os|key|raw|state
| Field | Type | Values | Available | Example | Description |
|---|---|---|---|---|---|
row_kind | string | secure_boot constrained | Windows, Linux, macOS | secure_boot | Row family: secure_boot (one per key read) or constrained (an internal-error row: the two-field row constrained|internal_error, which carries no os, key, raw or state). |
os | string | linux macos windows | Windows, Linux, macOS | linux | The leg that produced the row. |
key | string | - | Windows, Linux, macOS | secure_boot | Value read: secure_boot and setup_mode (Linux efivars), secure_boot (macOS, unsupported row), UEFISecureBootEnabled (Windows). |
raw | string | - | Windows, Linux, macOS | 1 | The raw value read (the efivar data byte, or the registry DWORD as a number); ”-” when nothing was read (absent, unreadable or unsupported). |
state | string | enabled disabled unmodelled absent unreadable unsupported | Windows, Linux, macOS | enabled | enabled = Secure Boot enforcing (for setup_mode: User Mode, a Platform Key is enrolled); disabled = Secure Boot off (for setup_mode: Setup Mode, no Platform Key, any image may load). The state describes the protection, not the raw number. unmodelled = a value was read that this table has no interpretation for (raw keeps it). absent = the OS definitively says it is not there (a BIOS/CSM boot has no efivars or SecureBoot\State), a normal answer and not a failure. unreadable = the read failed. unsupported = no mechanism on this OS (macOS). |
Result status
| Status | Completeness | Provenance | When |
|---|---|---|---|
OK | FULL | — | No row was unreadable: every key was either read or absent. An absent row adds no reason token, so a BIOS host, a kernel without lockdown or a default Windows install still reports OK. |
UNAVAILABLE | FULL | no public API; SIP reported under code_integrity | macOS secure_boot: the leg is unsupported by design and reports its unsupported row. |
CONSTRAINED | PARTIAL | <key>:errno_<n>, <key>:efivars:shape, <key>:timeout, <key>:output_truncated, <key>:spawn_failed, <key>:exit_<n>, <key>:exit_signal, <name>:win32_<n>, <name>:type_<n>, <name>:size_<n>, <name>:oversized, ci_policy:enumeration_incomplete, lsa:key_missing | At least one row was unreadable and none was refused; one <key>:<cause> token per unreadable row, and <key>:efivars:shape is an efivar that is not 4 + 1 bytes. An absent row never appears here. |
CONSTRAINED | PARTIAL | internal_error | Any leg: an unexpected exception was contained in execute(); the action returns 1 with the row constrained|internal_error. |
PERMISSION_DENIED | PARTIAL | <key>:eacces, <name>:access_denied | Any leg: a read was refused, EACCES/EPERM on Linux and for a macOS spawn, ERROR_ACCESS_DENIED on Windows. A refusal outranks every other unreadable cause in the same run; the reason still lists every unreadable row’s token. |
Where the data goes
- Instruction result. Rows travel over the agent’s mTLS gRPC channel as the command response and land in the ResponseStore, queryable at
/api/responses/{id}. Both actions are gathered definitions (crossplatform.platform_security.secure_boot,crossplatform.platform_security.code_integrity, 300s TTL). - Not consumed by daily-sync, Echo, DEX, or metrics.
- Sensitivity. Rows carry boot-integrity and code-signing settings only: no username, path, process name or installed-software name. The values are a security-posture baseline of the host (for example Secure Boot off, Gatekeeper off, lockdown none), which tells an attacker where enforcement is weak, so the rows warrant the same handling as other
Securitydata. - Siblings:
vuln_scan(its configuration checks runspctl --statusandcsrutil statusthroughpopenas pass/fail findings; this plugin reports the raw text and a state over the bounded runner) andbitlocker(disk encryption posture).
Sample output
Windows — captured: windows Windows 10.0.26200 x86_64 · bare-metal · 2026-09-21 · LocalSystem (elevated) · leg-hash 9cf003de2f97
== action=secure_boot
secure_boot|windows|UEFISecureBootEnabled|1|enabled
[result_status] OK / FULL
== action=code_integrity
code_integrity|windows|ci_policy.EmodePolicyRequired|0|unmodelled
code_integrity|windows|ci_policy.SAC_PreviousState|4294967295|unmodelled
code_integrity|windows|ci_policy.SkuPolicyRequired|0|unmodelled
code_integrity|windows|ci_policy.VerifiedAndReputablePolicyState|0|disabled
code_integrity|windows|deviceguard.EnableVirtualizationBasedSecurity|-|absent
code_integrity|windows|deviceguard.HypervisorEnforcedCodeIntegrity|-|absent
code_integrity|windows|deviceguard.RequirePlatformSecurityFeatures|-|absent
code_integrity|windows|deviceguard.hvci_scenario_enabled|-|absent
code_integrity|windows|lsa.LsaCfgFlags|-|absent
[result_status] OK / FULL
macOS — captured: macos macOS 26.6.2 arm64 · bare-metal · 2026-09-21 · euid 501 · leg-hash 9cf003de2f97
== action=secure_boot
secure_boot|macos|secure_boot|-|unsupported
[result_status] UNAVAILABLE / FULL / no public API; SIP reported under code_integrity
== action=code_integrity
code_integrity|macos|gatekeeper|assessments enabled|enabled
code_integrity|macos|sip|System Integrity Protection status: enabled.|enabled
[result_status] OK / FULL
Linux — captured: linux Debian GNU/Linux 13 (trixie) aarch64 · container · 2026-09-23 · euid 0 · leg-hash 9cf003de2f97
== action=secure_boot
secure_boot|linux|secure_boot|-|absent
secure_boot|linux|setup_mode|-|absent
[result_status] OK / FULL
== action=code_integrity
code_integrity|linux|lsm|-|unreadable
code_integrity|linux|lockdown|-|unreadable
[result_status] CONSTRAINED / PARTIAL / lsm:errno_19,lockdown:errno_19
Caveats and known gaps
- A container without
/sys/firmware/efireads Secure Boot asabsent, even on a Secure Boot host. The mount check separates “mounted, but this value is absent” from “the surface is not mounted” for the securityfs reads, but not for efivarfs: a default container’s/systree omits/sys/firmware/efiwhatever the host’s firmware, and neitherdeploy/docker/Dockerfile.agentnor the repository’s compose files bind-mount it. Closing this needs a deploy-side change (bind-mounting/sys/firmware/efiread-only into the agent container), and the Linux sample, a container capture, shows exactly this on its twosecure_bootrows. - Configured policy, not runtime proof. The Windows rows read registry values, which say what is configured. Whether Device Guard, HVCI or Credential Guard is actually running is a separate runtime fact (
Win32_DeviceGuard) that this plugin does not query, and it does not call WLDP. - macOS
secure_bootis unsupported, andcode_integrityparses tool text. There is no public API for the boot-security policy andbputilis a recovery-environment tool that is not run, sosecure_bootalways reportsUNAVAILABLEon a Mac. Gatekeeper and SIP have no library API either, so both are read from a tool’s text: the parsers match an exact line and report anything else asunmodelled. Security.frameworkSecStaticCode*is not used, because every call in that family validates one named code object’s own signature, not a machine-wide enforcement setting. - Fixed reads, no discovery. The plugin reads only the keys named above; it does not walk securityfs or the registry (apart from listing the value names under
CI\Policy), and it does not report TPM or measured-boot state. - Windows
CI\Policyis listed whole but modelled for one value. Every value under the key appears as a row, but onlyVerifiedAndReputablePolicyStatehas a state mapping; the others (for exampleSkuPolicyRequired) readunmodelledwith the raw number, which says the value exists, not what it means.
Source and tests
- Plugin:
agents/plugins/platform_security/src/platform_security_legs.hpp·agents/plugins/platform_security/src/platform_security_linux.cpp·agents/plugins/platform_security/src/platform_security_macos.cpp·agents/plugins/platform_security/src/platform_security_parsers.hpp·agents/plugins/platform_security/src/platform_security_plugin.cpp·agents/plugins/platform_security/src/platform_security_win.cpp·agents/plugins/platform_security/src/platform_security_win_parsers.hpp - Definitions:
content/definitions/platform_security.yaml - Capability rows:
server/core/src/capability_decls/plugin_action_catalogue_platform_security.hpp - Tests:
tests/unit/test_platform_security_local_dispatcher.cpp·tests/unit/test_platform_security_parsers.cpp·tests/unit/test_platform_security_win_parsers.cpp - Privilege row:
docs/agent-privilege-model.md