Skip to content

feat: add linux support#36

Merged
ashishkurmi merged 10 commits intostep-security:mainfrom
swarit-stepsecurity:swarit/feat/linux-support
Apr 24, 2026
Merged

feat: add linux support#36
ashishkurmi merged 10 commits intostep-security:mainfrom
swarit-stepsecurity:swarit/feat/linux-support

Conversation

@swarit-stepsecurity
Copy link
Copy Markdown
Member

What does this PR do?

Type of change

  • Bug fix
  • Enhancement
  • Documentation

Testing

  • Tested on macOS (version: ___)
  • Binary runs without errors: ./stepsecurity-dev-machine-guard --verbose
  • JSON output is valid: ./stepsecurity-dev-machine-guard --json | python3 -m json.tool
  • No secrets or credentials included
  • Lint passes: make lint
  • Tests pass: make test

Related Issues

Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
Signed-off-by: Swarit Pandey <swarit@stepsecurity.io>
@swarit-stepsecurity swarit-stepsecurity marked this pull request as ready for review April 23, 2026 17:35
@ashishkurmi ashishkurmi requested a review from Copilot April 23, 2026 19:08
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Linux support across scanning, output, and installation workflows, while also refactoring some Windows-specific implementations to use native APIs instead of shelling out.

Changes:

  • Introduce platform constants and shared platform display naming.
  • Add Linux scanning coverage (system package managers + Linux IDE path detection) and surface results in pretty/HTML outputs.
  • Add Linux installer/uninstaller via systemd user timer and improve Windows implementations using golang.org/x/sys/windows.

Reviewed changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
internal/telemetry/telemetry.go Uses platform constants for Windows-only filtering logic.
internal/systemd/systemd.go Adds systemd user service/timer installation for Linux.
internal/scan/scanner.go Adds Linux system package scanning and includes results in ScanResult.
internal/output/pretty_test.go Adds coverage for platform label display in pretty output.
internal/output/pretty.go Uses platform display name helper; prints Linux system/snap/flatpak package sections.
internal/output/html_test.go Adds coverage for platform label display in HTML output.
internal/output/html.go Uses platform display name helper in HTML template rendering.
internal/model/platform.go Adds platform constants and PlatformDisplayName.
internal/model/model.go Extends ScanResult/Summary with Linux package manager + package lists.
internal/lock/lock_windows.go Switches Windows PID liveness check to native OpenProcess.
internal/executor/executor_windows.go Implements elevated check via Windows token API.
internal/device/device_windows.go Adds native Windows serial/OS version collection via registry + RtlGetVersion.
internal/device/device_test.go Adds Linux device info unit tests (including fallbacks).
internal/device/device_other.go Provides non-Windows stubs for Windows-only helpers to support mock tests.
internal/device/device.go Adds Linux serial + OS version collection and uses platform constants.
internal/detector/xcode_extensions.go Prevents macOS-only detector from running on non-darwin platforms.
internal/detector/syspkg.go Adds Linux system package manager + snap/flatpak detection and parsing.
internal/detector/shellcmd.go Uses platform constants for quoting behavior.
internal/detector/registry_windows.go Adds native Windows registry implementation for version/install location lookup.
internal/detector/registry_other.go Adds non-Windows stub for registry lookup to support mock tests.
internal/detector/process_windows.go Adds native Windows process enumeration implementation.
internal/detector/process_other.go Adds non-Windows stub for Windows process matching to support mock tests.
internal/detector/process.go Adds Linux /proc-based process matching and routes by platform constants.
internal/detector/nodescan.go Uses platform constants for RunAsUser gating.
internal/detector/mcp.go Adds Linux config-path variants for MCP config discovery.
internal/detector/jetbrains_plugins.go Makes zip close defers ignore close errors consistently.
internal/detector/jetbrains.go Adds Linux JetBrains config dir resolution and product-info path logic.
internal/detector/ide.go Adds Linux IDE detection (paths, PATH, .desktop fallback) + moves registry logic behind build tags.
internal/detector/framework.go Adds Linux detection path for LM Studio.
internal/detector/eclipse_plugins_test.go Refactors test counting logic (switch).
internal/detector/eclipse_plugins.go Uses platform constants for Windows-only code path selection.
internal/detector/aicli.go Uses platform constants for Windows .exe handling.
internal/detector/agent.go Explicitly handles Linux unsupported Claude Desktop case.
go.sum Adds golang.org/x/sys checksums.
go.mod Adds dependency on golang.org/x/sys.
cmd/stepsecurity-dev-machine-guard/main.go Uses systemd install/uninstall on non-Windows/darwin platforms.
.gitignore Ignores Linux binary artifact name.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +160 to +163
[Service]
Type=oneshot
ExecStart={{.BinaryPath}} send-telemetry
StandardOutput=append:{{.LogDir}}/agent.log
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

ExecStart is rendered without any quoting/escaping. If the installed binary path contains spaces or characters that need escaping for systemd unit parsing, the service will fail to start. Consider using systemd-compatible escaping (or a helper/template func) so the path is safe in ExecStart.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +21
h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
if err != nil {
return false
}
return !strings.Contains(string(output), "No tasks")
_ = windows.CloseHandle(h)
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

windows.OpenProcess can fail with access denied for processes owned by another user/session even when the PID is alive. Returning false in that case can break the lock (allowing multiple instances). Consider treating access-denied as “alive” (or using a different liveness check).

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/syspkg.go Outdated
Comment on lines +212 to +214
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 30*time.Second,
"flatpak", "list", "--app", "--columns=application,version")
if err != nil {
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

ListFlatpakPackages only checks err, but Executor returns err == nil for non-zero exit codes. Please also check exitCode before parsing stdout so a failing flatpak list doesn’t get treated as valid/empty output.

Suggested change
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 30*time.Second,
"flatpak", "list", "--app", "--columns=application,version")
if err != nil {
stdout, _, exitCode, err := d.exec.RunWithTimeout(ctx, 30*time.Second,
"flatpak", "list", "--app", "--columns=application,version")
if err != nil || exitCode != 0 {

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +65
// Detect finds the active system package manager and returns its info.
// Returns nil on non-Linux platforms or if no known PM is found.
func (d *SystemPkgDetector) Detect(ctx context.Context) *model.PkgManager {
if d.exec.GOOS() != model.PlatformLinux {
return nil
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

System package detection/parsing is newly added here but doesn’t appear to have mock-based unit tests (other detectors in this package do). Adding tests for rpm/dpkg/pacman/apk outputs and snap/flatpak parsing would help prevent regressions across distros.

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/ide.go Outdated
Comment on lines +606 to +610
if base == "bin" {
return filepath.Dir(dir)
}
return "unknown"
// /usr/share/cursor/resources/app/bin/cursor -> /usr/share/cursor
if base == "bin" || filepath.Base(filepath.Dir(dir)) == "app" {
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

resolveInstallDirFromBinary returns early whenever base == "bin". That means paths like "/usr/share/cursor/resources/app/bin/cursor" will resolve to "/usr/share/cursor/resources/app" and never reach the intended resources/app/bin stripping logic below. Please fix the control flow so resources/app/bin is handled correctly.

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/syspkg.go Outdated
Comment on lines +104 to +105
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 60*time.Second, spec.Binary, spec.ListCmd...)
if err != nil {
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

Executor.RunWithTimeout/Run return err == nil for non-zero exit codes. ListPackages only checks err, so failures can be treated as success and stdout parsed incorrectly. Please also check exitCode == 0 before parsing stdout.

Suggested change
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 60*time.Second, spec.Binary, spec.ListCmd...)
if err != nil {
stdout, _, exitCode, err := d.exec.RunWithTimeout(ctx, 60*time.Second, spec.Binary, spec.ListCmd...)
if err != nil || exitCode != 0 {

Copilot uses AI. Check for mistakes.
Comment thread internal/detector/syspkg.go Outdated
Comment on lines +184 to +185
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 30*time.Second, "snap", "list")
if err != nil {
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

ListSnapPackages only checks err, but Executor returns err == nil for non-zero exit codes. Please also check exitCode before parsing stdout so a failing snap list doesn’t get treated as valid/empty output.

Suggested change
stdout, _, _, err := d.exec.RunWithTimeout(ctx, 30*time.Second, "snap", "list")
if err != nil {
stdout, _, exitCode, err := d.exec.RunWithTimeout(ctx, 30*time.Second, "snap", "list")
if err != nil || exitCode != 0 {

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +115
default:
if err := systemd.Install(exec, log); err != nil {
log.Error("%v", err)
os.Exit(1)
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The default branch treats any non-Windows/non-darwin platform as Linux and attempts a systemd install. That will break on unsupported Unix targets (e.g., freebsd). Please gate systemd to runtime.GOOS == "linux" and return a clear unsupported-platform error otherwise.

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +141
default:
if err := systemd.Uninstall(exec, log); err != nil {
log.Error("%v", err)
os.Exit(1)
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The default branch will run systemd uninstall on any non-Windows/non-darwin OS. Please restrict this to runtime.GOOS == "linux" and return a clear unsupported-platform error otherwise.

Copilot uses AI. Check for mistakes.
Comment thread internal/systemd/systemd.go Outdated
Comment on lines +73 to +75
if _, _, _, err := exec.Run(ctx, "systemctl", "--user", "daemon-reload"); err != nil {
return fmt.Errorf("daemon-reload failed: %w", err)
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

exec.Run returns err == nil even when the command exits non-zero (it surfaces the exit code separately). Here daemon-reload only checks err, so failures with a non-zero exit code will be treated as success. Please check exitCode and include stderr in the returned error (similar to the enable --now handling below).

Copilot uses AI. Check for mistakes.
- Gate systemd install/uninstall to runtime.GOOS == "linux" with
  unsupported-platform error for other Unix targets
- Fix lock_windows.go: treat ERROR_ACCESS_DENIED as process alive
- Fix systemd.go: check daemon-reload exit code, escape paths with
  spaces in unit files
- Fix syspkg.go: check exit codes on all RunWithTimeout calls
- Fix ide.go resolveInstallDirFromBinary: correct control flow for
  resources/app/bin layout
- Fix ide.go parseDesktopExec: handle wrapper commands (env VAR=...)
  by scanning for first absolute path
- Fix ide.go resolveLinuxVersion: prefer binary inside installDir
  over PATH to avoid version mismatch
@swarit-stepsecurity swarit-stepsecurity force-pushed the swarit/feat/linux-support branch from 27b4082 to 8055c59 Compare April 24, 2026 03:45
@ashishkurmi ashishkurmi merged commit 201fb79 into step-security:main Apr 24, 2026
5 checks passed
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.

3 participants