Skip to content

Bug 2051904 - Report disk encryption in device posture and about:support - #1375

Open
gcp wants to merge 5 commits into
mozilla:enterprise-mainfrom
gcp:enterprise-bug2051904-disk-encryption
Open

Bug 2051904 - Report disk encryption in device posture and about:support#1375
gcp wants to merge 5 commits into
mozilla:enterprise-mainfrom
gcp:enterprise-bug2051904-disk-encryption

Conversation

@gcp

@gcp gcp commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Description

Bugzilla: Bug 2051904

Add disk-encryption status to enterprise device posture:

{
  "diskEncryption": {
    "status": "full",
    "method": "dm-crypt"
  }
}

The first patch moves run_command_bounded out of the EDR checker and into a shared process module. Disk-encryption detection reuses this machinery to run platform probes with bounded execution time and output.

Firefox checks the operating-system volume, other mounted fixed volumes, and active Linux swap. This is not a root-only or remotely attested signal: an encrypted / with an unencrypted, separately mounted /home is reported as partial.

The same result is shown in the Security Software section of about:support for troubleshooting.

Posture values

Field Value Meaning
status full Every relevant volume was inspected and is encrypted
status enabled The operating-system volume is encrypted and no plaintext volume was found, but inspection of another relevant volume or encryption mapping was incomplete
status partial The operating-system volume is encrypted, but another fixed volume or active swap is confirmed unencrypted; this takes precedence if a different secondary volume is converting
status disabled The operating-system volume is not encrypted
status in-progress Encryption or decryption is in progress and no secondary volume is confirmed unencrypted
status unknown Encryption of the operating-system volume cannot be determined
method filevault FileVault on macOS
method bitlocker BitLocker on Windows
method dm-crypt dm-crypt on Linux
method zfs Native ZFS encryption
method null No method could be determined

Platform coverage

Platform Detection
Windows Checks BitLocker protection for the operating-system volume and other mounted fixed volumes
macOS Checks FileVault for the operating-system volume and encryption of other mounted internal volumes
Linux Walks device-mapper stacks to detect dm-crypt on the root filesystem, other mounted fixed filesystems, and active swap
Linux/Btrfs Resolves a Btrfs filesystem to its member devices and checks the storage stack beneath every member
Linux/ZFS Detects native ZFS encryption or checks every pool device for dm-crypt

Btrfs is not reported as an encryption method because Btrfs does not provide native filesystem encryption. An encrypted Btrfs filesystem is reported as dm-crypt when its member devices are protected by dm-crypt.

Detection runs off the main thread. Concurrent callers share a scan, and results are cached to limit repeated platform probes.


Testing

  • Added tests
  • Manual testing performed

Tests cover status aggregation, dm-crypt device stacks, multi-device Btrfs, native and dm-crypt-backed ZFS, Linux swap, FileVault, BitLocker, the JavaScript API, device-posture reporting, and about:support.

Copilot AI lite review requested due to automatic review settings September 1, 2026 07:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds cross-platform disk-encryption detection to enterprise builds and surfaces the aggregated status both in enterprise device posture reporting and in about:support’s Security Software section.

Changes:

  • Introduce a new DiskEncryption JS wrapper and XPCOM-backed Rust implementation to detect encryption state (Windows BitLocker, macOS FileVault, Linux dm-crypt/ZFS).
  • Extend Troubleshoot snapshot + about:support UI and tests to display disk-encryption status/method.
  • Extend enterprise device posture payload and enterprise tests to include diskEncryption.

Reviewed changes

Copilot reviewed 28 out of 29 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
toolkit/modules/Troubleshoot.sys.mjs Adds disk-encryption status to Troubleshoot security software snapshot on enterprise builds.
toolkit/modules/tests/xpcshell/xpcshell.toml Registers new xpcshell coverage for DiskEncryption module.
toolkit/modules/tests/xpcshell/test_DiskEncryption.js Adds xpcshell tests for JS wrapper timeout/normalization/error handling.
toolkit/modules/tests/browser/browser_Troubleshoot.js Extends Troubleshoot snapshot schema to include diskEncryption object.
toolkit/modules/moz.build Installs DiskEncryption enterprise JS module on enterprise builds.
toolkit/modules/DiskEncryption.sys.mjs New JS wrapper for the XPCOM disk-encryption checker with timeout handling.
toolkit/locales/en-US/toolkit/enterprise/enterprise.ftl Adds localized strings for disk-encryption row values in about:support.
toolkit/content/aboutSupport.xhtml Adds the Disk Encryption row to the Security Software table.
toolkit/content/aboutSupport.js Renders disk-encryption status into about:support via Fluent and toggles row visibility.
toolkit/components/felt/rust/src/process.rs Factors bounded subprocess execution + budget helper into a shared module.
toolkit/components/felt/rust/src/lib.rs Wires in new disk-encryption modules and shared process helper.
toolkit/components/felt/rust/src/edr_checker.rs Removes inlined process-spawn helper now shared in process.rs.
toolkit/components/felt/rust/src/edr_checker_win.rs Switches to shared run_command_bounded helper.
toolkit/components/felt/rust/src/edr_checker_macos.rs Switches to shared run_command_bounded helper.
toolkit/components/felt/rust/src/edr_checker_linux.rs Switches to shared run_command_bounded helper.
toolkit/components/felt/rust/src/disk_encryption.rs New XPCOM component coordinating async detection, caching, and concurrent callers.
toolkit/components/felt/rust/src/disk_encryption_win.rs Windows BitLocker-based detection across fixed volumes.
toolkit/components/felt/rust/src/disk_encryption_macos.rs macOS FileVault + internal fixed-volume encryption detection.
toolkit/components/felt/rust/src/disk_encryption_linux.rs Linux dm-crypt stack walking plus Btrfs/ZFS/swap handling.
toolkit/components/felt/rust/nsIDiskEncryptionChecker.idl New scriptable XPCOM interfaces for disk-encryption queries.
toolkit/components/felt/rust/moz.build Adds IDL and Windows helper source to felt build.
toolkit/components/felt/rust/FeltDiskEncryptionWin.cpp Implements unprivileged BitLocker property read via Windows shell property store.
toolkit/components/felt/rust/felt.h Exposes disk-encryption checker constructor symbol.
toolkit/components/felt/rust/components.conf Registers new disk-encryption checker XPCOM component/contract.
toolkit/components/felt/rust/Cargo.toml Adds plist (macOS) and tempfile (tests) dependencies; extends winapi features.
toolkit/components/enterprise/modules/DevicePosture.sys.mjs Adds diskEncryption field to device posture payload assembly.
testing/enterprise/test_felt_device_posture.py Validates diskEncryption shape/values in enterprise device posture tests.
Cargo.lock Locks new Rust dependency entries for plist and tempfile.
browser/base/content/test/about/browser_aboutSupport.js Adds browser test asserting the disk-encryption row renders correctly for status/method cases.
Suppressed comments (1)

toolkit/locales/en-US/toolkit/enterprise/enterprise.ftl:38

  • Like the “enabled” message, this defaults to the FileVault variant (*[filevault]), which would mislabel other platforms if $method is missing/unexpected. Consider adding an explicit [filevault] branch and an *[other] fallback with generic text.
        [bitlocker] Partial (BitLocker); some mounted fixed volumes are not encrypted
        [dm-crypt] Partial (dm-crypt); some mounted fixed volumes are not encrypted
        [zfs] Partial (ZFS); some mounted fixed volumes are not encrypted
       *[filevault] Partial (FileVault); some mounted fixed volumes are not encrypted
    }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread toolkit/locales/en-US/toolkit/enterprise/enterprise.ftl Outdated
Copilot AI review requested due to automatic review settings September 1, 2026 07:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

toolkit/modules/DiskEncryption.sys.mjs:63

  • status and method come straight from the native component and are used to build UI l10n IDs and posture payloads. If the component ever returns an unexpected status (or a non-empty method with status "unknown"), callers can end up referencing missing Fluent IDs or reporting inconsistent shapes. Consider normalizing to the documented set here (fallback to {status:"unknown", method:null} when invalid).
            onComplete(status, method) {
              finish({ status, method: method || null });
            },

toolkit/components/felt/rust/src/disk_encryption.rs:81

  • The PR description defines status: "in-progress" as "A volume is being encrypted or decrypted". In aggregate(), a converting secondary volume is currently ignored if any other secondary volume is unencrypted (it reports partial instead). That seems to contradict the documented meaning when both conditions are true. Either update the aggregation precedence to prefer InProgress when any inspected volume is converting, or adjust the documented meaning/tests to match the intended precedence.
        VolumeState::Encrypted => match others {
            None => EncryptionStatus::Unknown,
            Some(states) if states.contains(&VolumeState::Unencrypted) => EncryptionStatus::Partial,
            Some(states) if states.contains(&VolumeState::Converting) => {
                EncryptionStatus::InProgress
            }

Comment thread toolkit/content/aboutSupport.js
Copilot AI review requested due to automatic review settings September 1, 2026 08:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 29 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

toolkit/components/felt/rust/src/disk_encryption_win.rs:106

  • fixed_volumes() decides whether a volume is fixed by calling GetDriveTypeW on the mount path returned by GetVolumePathNamesForVolumeNameW. If the first mount point is a directory mount (e.g. C:\mounts\vol\), GetDriveTypeW will report the drive type of C:\, not the mounted volume, which can misclassify removable volumes as fixed (and affect the aggregated encryption status). Use the volume GUID path (\\?\Volume{...}\) when checking the drive type instead.
        let guid_path = from_wide(&name);
        match first_mount_path(&guid_path) {
            Ok(Some(mount_path)) => {
                if is_fixed_drive(&mount_path) {
                    volumes.push(Volume {

Copilot AI review requested due to automatic review settings September 1, 2026 10:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 31 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

toolkit/components/felt/rust/src/disk_encryption.rs:83

  • When the boot volume is encrypted, secondary volumes with VolumeState::Unknown are currently treated as Enabled. That can incorrectly report "enabled" even though one or more fixed volumes could not be inspected; per the documented posture values, inability to determine should surface as unknown (unless a known unencrypted or converting volume already determines partial/in-progress).
            Some(states) if states.contains(&VolumeState::Unencrypted) => EncryptionStatus::Partial,
            Some(states) if states.contains(&VolumeState::Converting) => {
                EncryptionStatus::InProgress
            }
            Some(_) => EncryptionStatus::Enabled,

toolkit/components/felt/rust/src/process.rs:101

  • This timeout test currently allows up to budget + PROBE_TIMEOUT (~5s) of runtime, which would still pass even if run_command_within accidentally waited the full probe timeout instead of the provided budget. Tightening the assertion will make the test actually catch regressions in budget handling.
        let budget = Duration::from_millis(200);
        let start = Instant::now();
        assert!(run_command_within("/bin/sleep", &["300"], budget).is_none());
        assert!(start.elapsed() < budget + PROBE_TIMEOUT);
    }

Copilot AI review requested due to automatic review settings September 1, 2026 11:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 31 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings September 1, 2026 13:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 34 changed files in this pull request and generated 2 comments.

Comment thread toolkit/modules/Troubleshoot.sys.mjs
Comment thread toolkit/components/felt/rust/src/disk_encryption.rs
Copilot AI review requested due to automatic review settings September 1, 2026 15:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 34 changed files in this pull request and generated 2 comments.

Comment thread browser/base/content/test/about/browser_aboutSupport.js Outdated
Comment thread toolkit/components/felt/rust/FeltDiskEncryptionWin.cpp
Copilot AI review requested due to automatic review settings September 1, 2026 15:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 34 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

browser/base/content/test/about/browser_aboutSupport.js:112

  • This test case also uses an unrecognized method: "unexpected", but the JS wrapper rejects unknown methods and will report status: "unknown" instead. That means this case will never render the expected "Partial …" string.

Remove this case so the test matches DiskEncryption.getStatus() normalization.

      {
        status: "in-progress",
        method: "bitlocker",
        text: "Encryption or decryption in progress",
      },

Comment thread browser/base/content/test/about/browser_aboutSupport.js Outdated
Comment thread toolkit/components/felt/rust/src/disk_encryption.rs
Copilot AI review requested due to automatic review settings September 1, 2026 16:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 34 changed files in this pull request and generated no new comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

browser/base/content/test/about/browser_aboutSupport.js:154

  • This test calls Troubleshoot.snapshot() once per CASE, which also triggers the enterprise EDR probe on enterprise builds. That adds unnecessary external probing (and potential 30s timeouts) for a test that only validates disk-encryption rendering, making it slower/flakier than needed.

Consider taking the snapshot with includeEnterpriseSecurity: false and explicitly populating snapshot.securitySoftware.diskEncryption via DiskEncryption.getStatus() (which will use the mocked XPCOM component), so the test stays focused and avoids EDR detection entirely.

                const snapshot = await Troubleshoot.snapshot();
                content.wrappedJSObject.snapshotFormatters.securitySoftware(
                  Cu.cloneInto(snapshot.securitySoftware, content)
                );

… r?#firefox-desktop-core-reviewers

Move `run_command_bounded` into a shared process module.
Copilot AI review requested due to automatic review settings September 2, 2026 09:24
@gcp
gcp force-pushed the enterprise-bug2051904-disk-encryption branch from 6f99c80 to 1bb1448 Compare September 2, 2026 09:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A new browser test references AppConstants without importing it, which will cause a ReferenceError and break the test run.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 31/32 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread browser/base/content/test/about/browser_aboutSupport.js
gcp added 4 commits September 2, 2026 20:14
…eviewers r?#supply-chain-reviewers

Inspect the operating-system volume and other mounted fixed volumes, including active Linux swap.

Use unprivileged platform APIs and expose full, enabled, partial, disabled, in-progress, and unknown through an asynchronous XPCOM service. Run detection off the main thread, coalesce concurrent callers, and cache results. A volume that cannot be inspected keeps the result at enabled rather than full.

Resolve btrfs and ZFS through the mount source, since a pooled filesystem reports an anonymous device number rather than the storage beneath it, and report ZFS native encryption as a method of its own. Trust LUKS mappings without reading the mapping table, which the kernel only discloses to root, and attest plain dm-crypt mappings when possible. A loop-backed root, as seen by a snap-confined browser, is unknown.

The plist crate only parses diskutil output on macOS and is vetted safe-to-run for felt.
…ore-reviewers

Wrap the callback-based disk-encryption checker in a Promise with a 30-second timeout. Resolve failures and timeouts as an unknown status.
…fox-desktop-core-reviewers

Collect disk-encryption status with the other slow probes and include it in every device-posture payload.
…-desktop-core-reviewers r?#webcompat-reviewers

Expose disk-encryption status through Troubleshoot and show it in about:support. Keep inconclusive results visible.

Remote troubleshooting and Report Broken Site opt out of the enterprise probes.
Copilot AI review requested due to automatic review settings September 2, 2026 18:16
@gcp
gcp force-pushed the enterprise-bug2051904-disk-encryption branch from 1bb1448 to 4bf7d47 Compare September 2, 2026 18:16
@gcp

gcp commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Accepted limitations

  • The signal is self-reported. A user who controls the Firefox install or profile can fabricate any status. Only remote attestation would change this.
  • enabled means "boot volume encrypted, inspection incomplete or unattested". It covers plain dm-crypt mappings Firefox cannot verify without root, sweeps that ran out of time, and volumes it could not classify. Consoles that need proof should treat only full as compliant and enabled as needing attention.
  • unknown is cheap to induce and is the expected result for snap/flatpak installs, whose root is a loop device. Alerting on transitions into unknown and on fleet-anomalous unknown is more useful than treating it as a fixed verdict.
  • Results are point-in-time and cached (10 min for full/partial/disabled, 60 s otherwise). A change right after a sweep is reported up to a TTL plus one poll later.
  • Root or admin can hide a data volume: on Linux by mounting it at exactly /boot or /efi, on Windows by removing its mount point (unmounted fixed volumes are skipped because the EFI and recovery partitions are always unmounted and unencrypted), on macOS possibly by mounting under /System/Volumes/ (unverified under the sealed system volume). Same trust class as the first item.
  • On Windows the BitLocker state comes from Explorer's System.Volume.BitLockerProtection shell property, which is readable without elevation. Per-user COM class hijacking of the shell could spoof it, again equivalent to controlling the process.
  • plist 1.7.0 to 1.10.0 deltas are audited safe-to-run only; a safe-to-deploy delta audit should be filed so the [policy.felt] exemption can be removed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It introduces substantial new cross-platform native detection logic (Rust + Windows C++) and a new XPCOM surface area that warrants final human review for correctness and platform-specific edge cases.

Review details
  • Files reviewed: 31/32 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@gcp
gcp marked this pull request as ready for review September 3, 2026 14:55
@gcp

gcp commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Verified on macOS and Linux, need to test on Windows still.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants