Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
- [Scan Coverage](SCAN_COVERAGE.md) — full catalog of detections
- [Release Process](docs/release-process.md) — how releases are signed and verified
- [Deploying via SCCM](docs/deploying-via-sccm.md) — Windows fleet rollout via Microsoft Configuration Manager (signed MSI, no PowerShell)
- [macOS TCC Permissions](docs/macos-tcc-permissions.md) — how the agent handles Documents/Downloads/Mail TCC dirs, PPPC profile for MDM-pushed Full Disk Access, and the `include_tcc_protected` config field
- [macOS TCC Permissions](docs/macos-tcc-permissions.md) — how the agent handles Documents/Downloads/Mail TCC dirs and network volumes (container-runtime mounts), PPPC profile for MDM-pushed Full Disk Access and network-volume pre-approval, and the `include_tcc_protected` / `include_network_volumes` config fields
- [macOS MDM packaging](packaging/macos/README.md) — ready-made PPPC `.mobileconfig` for fleet deployment
- [Versioning](VERSIONING.md) — why the version starts at 1.8.1
- [Security Policy](SECURITY.md) — reporting vulnerabilities
- [Code of Conduct](CODE_OF_CONDUCT.md)
Expand Down
3 changes: 3 additions & 0 deletions SCAN_COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ Per extension, the scan records identity (id, name, version, manifest version),

Node.js scanning is **off by default** in community mode (it can be slow). Enable with `--enable-npm-scan`.

**Projects inside dev containers are covered on macOS.** Container runtimes (OrbStack, Docker Desktop, Colima) expose the guest filesystem through a mount under `$HOME` — `~/OrbStack`, for example — so a project living inside a running container is walked like any other. macOS classifies those mounts as *network volumes* and gates the first access behind a TCC prompt; the agent walks them anyway, because that inventory is not reachable any other way. Fleets that would rather not see the prompt turn the walk off with `include_network_volumes: false`, or pre-approve it via PPPC — see [macos-tcc-permissions.md](docs/macos-tcc-permissions.md).

## Homebrew Package Scanning (Optional)

Homebrew scanning detects installed formulae and casks with rich metadata. Enable with `--enable-brew-scan`.
Expand Down Expand Up @@ -226,6 +228,7 @@ Discovered by scanning the **search directories** for virtual environments (`pyv

- **TCC-protected user directories** — the project/venv walk skips `~/Documents`, `~/Desktop`, `~/Downloads`, and `~/Library` to avoid macOS permission prompts. (The macOS global user-site `~/Library/Python/*` is the exception: it is scanned as its own explicit global root, so global user-site packages are still covered.) A **project virtual environment** kept under one of these directories is missed unless `include_tcc_protected: true` is set **and** the agent has Full Disk Access (see [macos-tcc-permissions.md](docs/macos-tcc-permissions.md)).
- **Locations outside `$HOME`** — e.g. `/opt`, `/srv`, `/data`, `/Users/Shared`, or a separate repos volume. Add them via `search_dirs`.
- **macOS network volumes when a fleet opts out** — venvs under a container-runtime mount (`~/OrbStack`, Docker Desktop / Colima shares) *are* covered by default; they're missed only where an admin set `include_network_volumes: false` to suppress the TCC prompt. The mounts given up are named in the run's warning log.
- **Global interpreters at non-standard prefixes** not under any tree listed above. Add the prefix (or a parent) via `search_dirs`.

The set of global install roots scanned is logged once per scan at info level (full paths at debug), so field logs show exactly where the agent looked.
Expand Down
15 changes: 8 additions & 7 deletions cmd/stepsecurity-dev-machine-guard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,12 @@ import (
"github.com/step-security/dev-machine-guard/internal/wslguest"
)

// auditSkipper builds a TCC skipper if scanning into TCC-protected dirs is
// not opted in. Mirrors scan.Run / telemetry.Run so the focused *Only audits
// don't accidentally prompt the user on macOS.
// auditSkipper builds the TCC skipper for the focused *Only audits from the
// same two toggles the full scan uses (protected dirs, network volumes), so
// they don't accidentally prompt the user on macOS. Mirrors scan.Run /
// telemetry.Run; nil when neither class has anything to skip.
func auditSkipper(exec executor.Executor, cfg *cli.Config) *tcc.Skipper {
if !tcc.Enabled(cfg.IncludeTCCProtected) {
return nil
}
return tcc.New(executor.ResolveHome(exec))
return tcc.ForRun(executor.ResolveHome(exec), cfg.IncludeTCCProtected, cfg.IncludeNetworkVolumes)
}

// hookReconcileTimeout caps the entire reconcile step (fetch + cache
Expand Down Expand Up @@ -119,6 +117,9 @@ func main() {
if cfg.IncludeTCCProtected == nil && config.IncludeTCCProtected != nil {
cfg.IncludeTCCProtected = config.IncludeTCCProtected
}
if cfg.IncludeNetworkVolumes == nil && config.IncludeNetworkVolumes != nil {
cfg.IncludeNetworkVolumes = config.IncludeNetworkVolumes
}
if cfg.ColorMode == "auto" && config.ColorMode != "" {
cfg.ColorMode = config.ColorMode
}
Expand Down
3 changes: 2 additions & 1 deletion docs/launchd-troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,10 @@ launchctl kickstart -k "$DOMAIN/$LABEL" && tail -n 20 "$LOGDIR/agent.log"

## Gotchas

- **config.json is rewritten every tick.** The loader's `write_config()` keeps only a fixed set (customer_id, api_endpoint, api_key, scan_frequency_hours + optional install_dir / max_execution_duration / scan toggles); any other hand-edited or profile-pushed field (e.g. `include_tcc_protected`) is wiped within one interval. Make it stick by editing the loader heredoc before deploy.
- **config.json is rewritten every tick.** The loader's `write_config()` keeps only a fixed set (customer_id, api_endpoint, api_key, scan_frequency_hours + optional install_dir / max_execution_duration / scan toggles); any other hand-edited or profile-pushed field (e.g. `include_tcc_protected`, `include_network_volumes`) is wiped within one interval. Make it stick by editing the loader heredoc before deploy.
- **Runs only in a live GUI session.** No console user (login window, headless, SSH) → not loaded, won't fire; the loader's initial run errors `no_user`, and `launchctl … gui/<uid>` over SSH can return `Bootstrap failed: 5`.
- **TCC prompts are real.** It runs in the user's GUI session, so scanning Documents/Downloads/etc. pops permission dialogs; skipped by default. Grant Full Disk Access (PPPC profile), then set `include_tcc_protected`.
- **The network-volume prompt is the one users actually hit.** Container-runtime mounts (`~/OrbStack`, Docker Desktop / Colima shares) are network volumes to macOS, and unlike the dirs above the agent walks them by **default** — so a developer running containers sees one "would like to access files on a network volume" dialog per user. Pre-approve it with the `SystemPolicyNetworkVolumes` PPPC payload (needs a fixed system-wide install path), or set `include_network_volumes: false` to trade the container inventory for silence. See [macos-tcc-permissions.md](macos-tcc-permissions.md).
- **A wedged run blocks every tick.** The binary's lock file makes overlapping runs exit; a hung run holds the lock until the loader SIGKILLs processes older than `MAX_PROCESS_AGE_HOURS` on a later tick. Self-heals, but loses up to that window.
- **`StartInterval` quirks.** Missed fires during sleep coalesce into one run on wake; the timer also restarts on each load/login, so short sessions on a long interval can starve it.
- **`Bootstrap failed: 5`** most often means already loaded — `bootout` first, then `bootstrap`.
Loading
Loading