Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dec257e
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
8e9f644
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
0f60cbe
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
ca652bd
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
d548ee0
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
f5a3547
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
6f5cd9c
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
59fdf2a
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
cf8556b
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
d992a82
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
c3b3ac4
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
acd7684
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
561406f
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
e0391fd
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
2708673
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
f56331b
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
f381817
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
8f18b96
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
f04fd72
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
180218f
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
cabd6e2
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
55a7770
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
ace6544
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
1ba9630
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
66a1ccf
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
1496e90
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
c7a6ea1
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
2940880
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
0cb3ccc
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
c136451
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
6e5480e
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
68efa2f
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
d6844ef
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
37053ed
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
4e17c84
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
8a5c419
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
5538295
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
cc206dc
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
7c4b1ef
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
b770f51
fix(adhoc-sweep-fixes): 40 review findings across 40 files
flamingo[bot] Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/steps/sign-windows-package/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ runs:
echo "BINARY_PATH=$BinaryPath" >> $env:GITHUB_ENV

- name: Sign Windows Executable
uses: azure/trusted-signing-action@v0.5.0
uses: azure/trusted-signing-action@95de1e51cbb1a115f0e4f47ba193da2ba0a4a806 # v0.5.0
with:
azure-tenant-id: ${{ inputs.azure_tenant_id }}
azure-client-id: ${{ inputs.azure_client_id }}
Expand Down Expand Up @@ -83,3 +83,4 @@ runs:
}

Write-Host "Signature verified successfully!"

10 changes: 9 additions & 1 deletion .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: sync/upstream-main
MAX_CHANGED_FILES: 500
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -37,6 +38,13 @@ jobs:
exit 0
fi

CHANGED_FILES=$(git diff --name-only origin/main...upstream/main | wc -l)
echo "::notice::upstream/main differs from origin/main by $CHANGED_FILES file(s)"
if [ "$CHANGED_FILES" -gt "$MAX_CHANGED_FILES" ]; then
echo "::error::refusing to sync — $CHANGED_FILES changed files exceeds MAX_CHANGED_FILES ($MAX_CHANGED_FILES); manual review required"
exit 1
fi

git checkout -B "$BRANCH" origin/main
if ! git merge --no-ff --no-edit upstream/main; then
echo "::notice::merge has conflicts — skipping, will retry next run"
Expand All @@ -46,4 +54,4 @@ jobs:

gh pr create --repo "$GITHUB_REPOSITORY" --base main --head "$BRANCH" \
--title "Sync from Fork" \
--body "Automatic weekly sync from \`fleetdm/fleet@main\`."
--body "Automatic weekly sync from \`fleetdm/fleet@main\`."
26 changes: 16 additions & 10 deletions android/app/src/main/java/com/fleetdm/agent/KeystoreManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import android.util.Base64
import java.security.KeyStore
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference

object KeystoreManager {
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
Expand All @@ -17,28 +19,32 @@ object KeystoreManager {
private const val IV_SEPARATOR = "]"

// Test mode uses in-memory key instead of Android Keystore
private var testMode = false
private var testKey: SecretKey? = null
private val testMode = AtomicBoolean(false)
private val testKey = AtomicReference<SecretKey?>(null)

/**
* Enables test mode which uses an in-memory key instead of Android Keystore.
* This allows unit tests to run without Android's hardware-backed keystore.
*/
@Synchronized
fun enableTestMode() {
testMode = true
testKey = KeyGenerator.getInstance("AES").apply {
init(256)
}.generateKey()
testKey.set(
KeyGenerator.getInstance("AES").apply {
init(256)
}.generateKey(),
)
testMode.set(true)
}

@Synchronized
fun disableTestMode() {
testMode = false
testKey = null
testMode.set(false)
testKey.set(null)
}

private fun getOrCreateKey(): SecretKey {
if (testMode) {
return testKey ?: error("Test mode enabled but no test key available")
if (testMode.get()) {
return testKey.get() ?: error("Test mode enabled but no test key available")
}
val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply {
load(null)
Expand Down
12 changes: 7 additions & 5 deletions cmd/fleet/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"fmt"
"log/slog"

"github.com/fleetdm/fleet/v4/server/config"
Expand All @@ -20,10 +21,10 @@ func shouldForceSMTPBackend(appCfg *fleet.AppConfig, emailBackend string) bool {
emailBackend != ""
}

// initMailService configures the mail service. Mail is best-effort at startup:
// a construction failure is logged and the (possibly nil) service is returned
// rather than aborting boot.
func initMailService(ctx context.Context, cfg config.FleetConfig, appCfg *fleet.AppConfig, logger *slog.Logger) fleet.MailService {
// initMailService configures the mail service. If construction fails, the
// error is logged and returned so callers can fail fast at startup instead of
// receiving a nil service silently.
func initMailService(ctx context.Context, cfg config.FleetConfig, appCfg *fleet.AppConfig, logger *slog.Logger) (fleet.MailService, error) {
if shouldForceSMTPBackend(appCfg, cfg.Email.EmailBackend) {
// Force-load the SMTP implementation by clearing the configured backend.
cfg.Email.EmailBackend = ""
Expand All @@ -33,6 +34,7 @@ func initMailService(ctx context.Context, cfg config.FleetConfig, appCfg *fleet.
mailService, err := mail.NewService(cfg)
if err != nil {
logger.ErrorContext(ctx, "failed to configure mailing service", "err", err)
return nil, fmt.Errorf("failed to configure mailing service: %w", err)
}
return mailService
return mailService, nil
}
7 changes: 6 additions & 1 deletion cmd/osv-processor/sync-and-detect-changes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ fi
cd "$REPO_DIR"

TODAY_UTC=$(date -u +%Y-%m-%d)
YESTERDAY_UTC=$(date -u -v-1d +%Y-%m-%d 2>/dev/null || date -u -d "yesterday" +%Y-%m-%d)
YESTERDAY_UTC=$(date -u -v-1d +%Y-%m-%d 2>/dev/null || date -u -d "yesterday" +%Y-%m-%d 2>/dev/null || true)
if [ -z "$YESTERDAY_UTC" ]; then
echo "ERROR: Unable to compute yesterday's date; 'date' binary supports neither -v (BSD) nor -d (GNU) flags." >&2
exit 1
fi

# Get files changed today (since midnight UTC today)
git log --since="${TODAY_UTC}T00:00:00Z" --name-only --pretty="" -- osv/cve \
Expand Down Expand Up @@ -119,3 +123,4 @@ echo "TODAY_COUNT=$TODAY_COUNT"
echo "YESTERDAY_COUNT=$YESTERDAY_COUNT"

exit 0

11 changes: 9 additions & 2 deletions ee/cis/macos-14/test/scripts/CIS_6.1.1.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#!/bin/bash

# Get the current console user (the actual logged-in user), excluding root and loginwindow
CURRENT_USER=$(/usr/bin/stat -f "%Su" /dev/console)

if [[ -z "$CURRENT_USER" || "$CURRENT_USER" == "root" ]]; then
echo "Unable to determine a valid non-root console user. Aborting."
exit 1
fi

/usr/bin/sudo -u "$CURRENT_USER" /usr/bin/defaults write "/Users/$CURRENT_USER/Library/Preferences/.GlobalPreferences.plist" AppleShowAllExtensions -bool true

# For QA: Replace <username> with your test user
/usr/bin/sudo -u <username> /usr/bin/defaults write /Users/<username>/Library/Preferences/.GlobalPreferences.plist AppleShowAllExtensions -bool true
12 changes: 11 additions & 1 deletion ee/fleet-agent-downloader/config/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ module.exports.session = {
* Replace at your own risk in production-- you will invalidate the cookies *
* of your users, forcing them to log in again. *
* *
* The secret must be provided via the SESSION_SECRET environment variable. *
* There is no committed literal fallback -- if SESSION_SECRET is not set, *
* the app will fail to start rather than sign sessions with a known value. *
* *
***************************************************************************/
secret: 'DUMMY_SECRET_REPLACED_IN_PROD',
secret: (function () {
if (!process.env.SESSION_SECRET) {
throw new Error('SESSION_SECRET environment variable must be set (no default secret is provided).');
}
return process.env.SESSION_SECRET;
})(),


/***************************************************************************
Expand All @@ -37,3 +46,4 @@ module.exports.session = {
// },

};

Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,14 @@ hdiutil detach "$MOUNT_POINT"
# Clean up any backup files that might exist from previous failed installations
# This ensures we start with a clean slate
cleanup_backup_files() {
# Clean up backup in the installer's temp directory
# Clean up backup in the installer's temp directory only.
# Scoped to the known install/backup location actually used by this
# installer to avoid a broad, unconditional find+rm -rf across shared
# temp trees (/tmp, /var/folders, /private/var/folders).
if [ -d "$TMPDIR/Microsoft Edge.app.bkp" ]; then
echo "Removing existing backup file: $TMPDIR/Microsoft Edge.app.bkp"
sudo rm -rf "$TMPDIR/Microsoft Edge.app.bkp" 2>/dev/null || true
fi

# Search for backup files in all common temp locations
# Use -exec to avoid pipe subshell issues
for search_base in /tmp /var/folders /private/var/folders; do
if [ -d "$search_base" ]; then
find "$search_base" -type d -name "Microsoft Edge.app.bkp" -exec sudo rm -rf {} + 2>/dev/null || true
fi
done
}

# copy to the applications folder
Expand Down Expand Up @@ -92,3 +87,4 @@ else
fi



102 changes: 56 additions & 46 deletions ee/orbit/pkg/securehw/securehw_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,22 @@ func NewTestSecureHW(device transport.TPMCloser, metadataDir string, logger zero
}, nil
}

// CreateKey partially implements SecureHW.
func (t *tpm2SecureHW) CreateKey() (Key, error) {
t.logger.Info().Msg("creating new ECC key in TPM")

// withParentKey creates a transient parent key, invokes fn with its handle, and
// guarantees the parent key handle is flushed afterwards regardless of the
// outcome of fn or any future error paths added to fn.
func (t *tpm2SecureHW) withParentKey(fn func(parentKeyHandle tpm2.NamedHandle) error) error {
parentKeyHandle, err := t.createParentKey()
if err != nil {
return nil, fmt.Errorf("get or create TPM parent key: %w", err)
return err
}
defer t.flushHandle(parentKeyHandle.Handle, "parent")

return fn(parentKeyHandle)
}

// CreateKey partially implements SecureHW.
func (t *tpm2SecureHW) CreateKey() (Key, error) {
t.logger.Info().Msg("creating new ECC key in TPM")

curveID, curveName := t.selectBestECCCurve()
t.logger.Info().Str("curve", curveName).Msg("selected ECC curve for key creation")
Expand Down Expand Up @@ -88,33 +96,36 @@ func (t *tpm2SecureHW) CreateKey() (Key, error) {
),
})

// Create the key under the transient parent
t.logger.Debug().Msg("creating child key")
createKey, err := tpm2.Create{
ParentHandle: parentKeyHandle,
InPublic: eccTemplate,
}.Execute(t.device)
if err != nil {
// Flush the parent key before returning error
t.flushHandle(parentKeyHandle.Handle, "parent")
return nil, fmt.Errorf("create child key: %w", err)
}
var createKey *tpm2.CreateResponse
var loadedKey *tpm2.LoadResponse

err := t.withParentKey(func(parentKeyHandle tpm2.NamedHandle) error {
// Create the key under the transient parent
t.logger.Debug().Msg("creating child key")
var err error
createKey, err = tpm2.Create{
ParentHandle: parentKeyHandle,
InPublic: eccTemplate,
}.Execute(t.device)
if err != nil {
return fmt.Errorf("create child key: %w", err)
}

t.logger.Debug().Msg("Loading created key")
loadedKey, err := tpm2.Load{
ParentHandle: parentKeyHandle,
InPrivate: createKey.OutPrivate,
InPublic: createKey.OutPublic,
}.Execute(t.device)
t.logger.Debug().Msg("Loading created key")
loadedKey, err = tpm2.Load{
ParentHandle: parentKeyHandle,
InPrivate: createKey.OutPrivate,
InPublic: createKey.OutPublic,
}.Execute(t.device)
if err != nil {
return fmt.Errorf("load key: %w", err)
}
return nil
})
if err != nil {
// Flush the parent key before returning error
t.flushHandle(parentKeyHandle.Handle, "parent")
return nil, fmt.Errorf("load key: %w", err)
return nil, err
}

// Flush the parent key as it's no longer needed
t.flushHandle(parentKeyHandle.Handle, "parent")

t.logger.Debug().
Str("handle", fmt.Sprintf("0x%x", loadedKey.ObjectHandle)).
Msg("key loaded successfully")
Expand Down Expand Up @@ -276,31 +287,30 @@ func (t *tpm2SecureHW) LoadKey() (Key, error) {
return nil, err
}

var loadedKey *tpm2.LoadResponse

// Get the parent key handle.
//
// NOTE: createParentKey calls CreatePrimary which creates the parent key
// deterministically so this can be called when loadind a child key.
parentKeyHandle, err := t.createParentKey()
if err != nil {
return nil, fmt.Errorf("get parent key: %w", err)
}

// Load the key using the parent handle.
t.logger.Debug().Uint32("parent_handle", uint32(parentKeyHandle.Handle)).Msg("loading parent key")
loadedKey, err := tpm2.Load{
ParentHandle: parentKeyHandle,
InPrivate: *private,
InPublic: *public,
}.Execute(t.device)
err = t.withParentKey(func(parentKeyHandle tpm2.NamedHandle) error {
// Load the key using the parent handle.
t.logger.Debug().Uint32("parent_handle", uint32(parentKeyHandle.Handle)).Msg("loading parent key")
var loadErr error
loadedKey, loadErr = tpm2.Load{
ParentHandle: parentKeyHandle,
InPrivate: *private,
InPublic: *public,
}.Execute(t.device)
if loadErr != nil {
return fmt.Errorf("load parent key: %w", loadErr)
}
return nil
})
if err != nil {
// Flush the parent key before returning error
t.flushHandle(parentKeyHandle.Handle, "parent")
return nil, fmt.Errorf("load parent key: %w", err)
return nil, err
}

// Flush the parent key as it's no longer needed
t.flushHandle(parentKeyHandle.Handle, "parent")

t.logger.Info().
Str("handle", fmt.Sprintf("0x%x", loadedKey.ObjectHandle)).
Msg("key loaded successfully")
Expand Down
6 changes: 4 additions & 2 deletions ee/server/service/condaccess/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ func initAssets(ctx context.Context, ds fleet.Datastore) error {
savedAssets, err := ds.GetAllMDMConfigAssetsByName(ctx, expectedAssets, nil)
if err != nil {
// Allow not found errors or partial results (some assets exist, some don't).
// If we got some assets back, continue to create the missing ones.
if !fleet.IsNotFound(err) && len(savedAssets) == 0 {
// If we got some assets back, continue to create the missing ones. Otherwise,
// only tolerate the error if it is a not-found error; any other error
// (e.g. a transient DB error) with no assets returned must be surfaced.
if !fleet.IsNotFound(err) && len(savedAssets) != len(expectedAssets) {
return fmt.Errorf("loading existing conditional access assets from the database: %w", err)
}
}
Expand Down
13 changes: 12 additions & 1 deletion ee/server/service/embedded_scripts/linux_lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ if [ "$NEEDS_REBOOT" = "1" ]; then
# The script already uses systemctl extensively, so systemd-run should be available
# This gives us precise 10-second delay for the script to report success
echo "Scheduling system reboot in 10 seconds to complete lock process..."
systemd-run --on-active=10s --timer-property=AccuracySec=100ms /sbin/reboot
if command -v systemd-run >/dev/null 2>&1 && systemd-run --on-active=10s --timer-property=AccuracySec=100ms /sbin/reboot; then
:
else
echo "systemd-run failed or is unavailable - falling back to 'at' for delayed reboot"
if command -v at >/dev/null 2>&1 && echo "/sbin/reboot" | at now + 1 minute >/dev/null 2>&1; then
:
else
echo "'at' unavailable or failed - falling back to backgrounded sleep-based reboot"
( sleep 10 && /sbin/reboot ) >/dev/null 2>&1 &
disown 2>/dev/null || true
fi
fi
fi
exit 0
Loading