Skip to content

Commit 612eb90

Browse files
merge: resolve conflicts with upstream/main (v1.10.0)
Adopt upstream's IDEType naming (intellij_idea, pycharm instead of intellij_idea_ultimate, pycharm_professional). Keep our Windows enhancements (glob paths, product-info.json, .eclipseproduct, RunInDir, JetBrains plugin detection). Incorporate upstream additions (Fleet, Xcode, Homebrew, Python scanning, UserAwareExecutor).
2 parents bd373c5 + eab8f68 commit 612eb90

34 files changed

Lines changed: 3217 additions & 266 deletions

.github/workflows/release.yml

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,32 @@ jobs:
6565
env:
6666
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6767

68+
- name: Resolve draft release tag
69+
id: release
70+
env:
71+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72+
run: |
73+
# GoReleaser creates draft releases under an "untagged-*" slug,
74+
# so gh release upload by version tag returns 404. Look up the
75+
# actual tag GitHub assigned to the draft.
76+
release_tag=$(gh api "repos/${{ github.repository }}/releases" \
77+
--jq '[.[] | select(.draft and .tag_name == "${{ steps.version.outputs.tag }}")] | first | .tag_name')
78+
if [ -z "$release_tag" ] || [ "$release_tag" = "null" ]; then
79+
echo "::error::Could not find draft release for ${{ steps.version.outputs.tag }}"
80+
exit 1
81+
fi
82+
echo "tag=$release_tag" >> "$GITHUB_OUTPUT"
83+
echo "Resolved draft release tag: $release_tag"
84+
6885
- name: Install cosign
6986
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
7087

7188
- name: Locate binaries
7289
id: binaries
7390
run: |
7491
DARWIN=$(find dist -type f -name '*darwin_unnotarized' | head -1)
75-
WIN_AMD64=$(find dist -type f -name '*windows_amd64.exe' | head -1)
76-
WIN_ARM64=$(find dist -type f -name '*windows_arm64.exe' | head -1)
92+
WIN_AMD64=$(find dist -type f -name '*.exe' -path '*windows_amd64*' | head -1)
93+
WIN_ARM64=$(find dist -type f -name '*.exe' -path '*windows_arm64*' | head -1)
7794
7895
for label in "darwin:${DARWIN}" "windows_amd64:${WIN_AMD64}" "windows_arm64:${WIN_ARM64}"; do
7996
name="${label%%:*}"
@@ -91,22 +108,23 @@ jobs:
91108
92109
- name: Sign artifacts with Sigstore
93110
run: |
94-
for artifact in \
95-
"${{ steps.binaries.outputs.darwin }}" \
96-
"${{ steps.binaries.outputs.win_amd64 }}" \
97-
"${{ steps.binaries.outputs.win_arm64 }}" \
98-
stepsecurity-dev-machine-guard.sh; do
99-
cosign sign-blob "$artifact" --bundle "${artifact}.bundle" --yes
100-
done
111+
cosign sign-blob "${{ steps.binaries.outputs.darwin }}" \
112+
--bundle dist/stepsecurity-dev-machine-guard-darwin_unnotarized.bundle --yes
113+
cosign sign-blob "${{ steps.binaries.outputs.win_amd64 }}" \
114+
--bundle dist/stepsecurity-dev-machine-guard-windows_amd64.exe.bundle --yes
115+
cosign sign-blob "${{ steps.binaries.outputs.win_arm64 }}" \
116+
--bundle dist/stepsecurity-dev-machine-guard-windows_arm64.exe.bundle --yes
117+
cosign sign-blob stepsecurity-dev-machine-guard.sh \
118+
--bundle dist/stepsecurity-dev-machine-guard.sh.bundle --yes
101119
102120
- name: Upload cosign bundles
103121
env:
104122
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105123
run: |
106-
gh release upload "${{ steps.version.outputs.tag }}" \
107-
"${{ steps.binaries.outputs.darwin }}.bundle" \
108-
"${{ steps.binaries.outputs.win_amd64 }}.bundle" \
109-
"${{ steps.binaries.outputs.win_arm64 }}.bundle" \
124+
gh release upload "${{ steps.release.outputs.tag }}" \
125+
dist/stepsecurity-dev-machine-guard-darwin_unnotarized.bundle \
126+
dist/stepsecurity-dev-machine-guard-windows_amd64.exe.bundle \
127+
dist/stepsecurity-dev-machine-guard-windows_arm64.exe.bundle \
110128
dist/stepsecurity-dev-machine-guard.sh.bundle \
111129
--clobber
112130

CHANGELOG.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,33 @@ See [VERSIONING.md](VERSIONING.md) for why the version starts at 1.8.1.
1111

1212
### Added
1313

14-
- **JetBrains IDE detection** (macOS + Windows): IntelliJ IDEA (Ultimate & CE), PyCharm (Professional & CE), WebStorm, GoLand, PhpStorm, CLion, Rider, RubyMine, DataGrip, and Android Studio.
15-
- **Eclipse IDE detection** (macOS + Windows): Detects standard install paths including Eclipse Installer (`%USERPROFILE%\eclipse\*\eclipse`) and manual installs.
16-
- **Glob-based Windows path matching**: `detectWindows` now supports wildcard patterns in `WinPaths` for IDEs that embed version numbers in folder names (e.g., `C:\Program Files\JetBrains\GoLand 2025.1.3\`). Picks the newest installation when multiple versions are present.
17-
- **`product-info.json` version extraction**: Reads the JetBrains `product-info.json` file for accurate marketing version numbers (avoids registry build numbers).
18-
- **`.eclipseproduct` version extraction**: Reads Eclipse's `.eclipseproduct` properties file for version detection on Windows (Eclipse does not register in the Windows registry).
19-
- **JetBrains plugin detection** (macOS + Windows): Detects user-installed plugins for all JetBrains IDEs by reading `product-info.json` to resolve the config directory, then scanning the plugins directory. Version extracted from JAR filenames.
14+
- **Glob-based Windows path matching**: `detectWindows` supports wildcard patterns in `WinPaths` for JetBrains IDEs that embed version numbers in folder names. Picks the newest installation when multiple versions are present.
15+
- **`product-info.json` version extraction**: Reads JetBrains `product-info.json` for accurate marketing version numbers on Windows (avoids registry build numbers).
16+
- **`.eclipseproduct` version extraction**: Reads Eclipse's `.eclipseproduct` properties file for version detection on Windows.
17+
- **JetBrains plugin detection enhancements**: Reads `productVendor` from `product-info.json` for correct config paths (handles Android Studio's `Google` vendor). Checks `idea.plugins.path` override in `idea.properties`.
18+
19+
### Fixed
20+
21+
- **Windows project package scanning**: Added `RunInDir` to Executor interface to bypass `cmd.exe` quote escaping issues. Fixes project-level NPM packages not being collected on Windows.
22+
23+
## [1.10.0] - 2026-04-20
24+
25+
### Added
26+
27+
- Windows support: cross-platform detection for IDEs, extensions, AI tools, frameworks, MCP configs, and Node.js scanning on Windows.
28+
- Homebrew scanning: detects formulae and casks with raw output capture for enterprise telemetry.
29+
- Python scanning: detects package managers, global packages, and projects with virtual environments.
30+
- User-aware executor: commands like `brew`, `pip3`, and `npm` now run in the logged-in user's context when the agent runs as root.
31+
- IDE plugin detection: JetBrains IDEs, Xcode Source Editor extensions, and Eclipse plugins with bundled/user-installed source tagging.
32+
- Project-level MCP configuration discovery and filtering.
33+
- S3 upload retry mechanism with exponential backoff and extended timeout for large payloads.
34+
- Enhanced user shell resolution for macOS `RunAsUser`.
35+
36+
### Fixed
37+
38+
- Populated missing performance metrics fields (brew formulae/cask counts, Python global packages/project counts).
39+
- S3 retry logging now includes the actual error value for easier debugging.
40+
- Retry backoff respects context cancellation during shutdown.
2041

2142
## [1.9.2] - 2026-04-15
2243

@@ -83,6 +104,7 @@ First open-source release. The scanning engine was previously an internal enterp
83104
- Execution log capture and base64 encoding
84105
- Instance locking to prevent concurrent runs
85106

107+
[1.10.0]: https://github.com/step-security/dev-machine-guard/compare/v1.9.2...v1.10.0
86108
[1.9.2]: https://github.com/step-security/dev-machine-guard/compare/v1.9.1...v1.9.2
87109
[1.9.1]: https://github.com/step-security/dev-machine-guard/compare/v1.9.0...v1.9.1
88110
[1.9.0]: https://github.com/step-security/dev-machine-guard/compare/v1.8.2...v1.9.0

cmd/stepsecurity-dev-machine-guard/main.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ func main() {
3434
if cfg.EnableNPMScan == nil && config.EnableNPMScan != nil {
3535
cfg.EnableNPMScan = config.EnableNPMScan
3636
}
37+
if cfg.EnableBrewScan == nil && config.EnableBrewScan != nil {
38+
cfg.EnableBrewScan = config.EnableBrewScan
39+
}
40+
if cfg.EnablePythonScan == nil && config.EnablePythonScan != nil {
41+
cfg.EnablePythonScan = config.EnablePythonScan
42+
}
3743
if cfg.ColorMode == "auto" && config.ColorMode != "" {
3844
cfg.ColorMode = config.ColorMode
3945
}
@@ -60,9 +66,8 @@ func main() {
6066
if cfg.OutputFormat == "json" {
6167
quiet = true
6268
}
63-
if cfg.Command == "send-telemetry" || cfg.Command == "install" {
64-
quiet = false
65-
}
69+
// Note: send-telemetry and bare command (auto-detected enterprise) both
70+
// respect the same quiet logic — config value wins, default is true.
6671
log := progress.NewLogger(quiet)
6772

6873
switch cfg.Command {

examples/sample-output.json

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"agent_version": "1.9.2",
2+
"agent_version": "1.10.0",
33
"scan_timestamp": 1741305600,
44
"scan_timestamp_iso": "2026-03-07T00:00:00Z",
55
"device": {
@@ -70,6 +70,13 @@
7070
"install_path": "/Applications/Claude.app",
7171
"vendor": "Anthropic",
7272
"is_installed": true
73+
},
74+
{
75+
"ide_type": "goland",
76+
"version": "2024.3.1",
77+
"install_path": "/Applications/GoLand.app",
78+
"vendor": "JetBrains",
79+
"is_installed": true
7380
}
7481
],
7582
"ide_extensions": [
@@ -126,11 +133,56 @@
126133
}
127134
],
128135
"node_packages": [],
136+
"node_projects": [
137+
{ "path": "/Users/developer/projects/my-app", "package_manager": "npm" },
138+
{ "path": "/Users/developer/projects/api-server", "package_manager": "yarn" },
139+
{ "path": "/Users/developer/projects/frontend", "package_manager": "pnpm" }
140+
],
141+
"brew_package_manager": {
142+
"name": "homebrew",
143+
"version": "4.3.5",
144+
"path": "/opt/homebrew/bin/brew"
145+
},
146+
"brew_formulae": [
147+
{ "name": "ca-certificates", "version": "2024.2.2" },
148+
{ "name": "curl", "version": "8.4.0" },
149+
{ "name": "git", "version": "2.43.0" },
150+
{ "name": "openssl@3", "version": "3.2.0" }
151+
],
152+
"brew_casks": [
153+
{ "name": "visual-studio-code", "version": "1.85.0" },
154+
{ "name": "firefox", "version": "120.0" }
155+
],
156+
"python_package_managers": [
157+
{
158+
"name": "python3",
159+
"version": "3.12.0",
160+
"path": "/usr/local/bin/python3"
161+
},
162+
{
163+
"name": "pip",
164+
"version": "24.0",
165+
"path": "/usr/local/bin/pip3"
166+
}
167+
],
168+
"python_packages": [
169+
{ "name": "requests", "version": "2.31.0" },
170+
{ "name": "numpy", "version": "1.26.2" },
171+
{ "name": "pip", "version": "24.0" }
172+
],
173+
"python_projects": [
174+
{ "path": "/Users/developer/projects/ml-pipeline", "package_manager": "poetry" },
175+
{ "path": "/Users/developer/projects/data-analysis", "package_manager": "pip" },
176+
{ "path": "/Users/developer/projects/web-scraper", "package_manager": "uv" }
177+
],
129178
"summary": {
130179
"ai_agents_and_tools_count": 5,
131-
"ide_installations_count": 3,
180+
"ide_installations_count": 4,
132181
"ide_extensions_count": 4,
133182
"mcp_configs_count": 2,
134-
"node_projects_count": 0
183+
"node_projects_count": 3,
184+
"brew_formulae_count": 42,
185+
"brew_casks_count": 15,
186+
"python_projects_count": 3
135187
}
136188
}

internal/buildinfo/version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package buildinfo
33
import "fmt"
44

55
const (
6-
Version = "1.9.2"
6+
Version = "1.10.0"
77
AgentURL = "https://github.com/step-security/dev-machine-guard"
88
)
99

internal/cli/cli.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import (
1111

1212
// Config holds all parsed CLI flags.
1313
type Config struct {
14-
Command string // "", "install", "uninstall", "send-telemetry", "configure", "configure show"
15-
OutputFormat string // "pretty", "json", "html"
16-
OutputFormatSet bool // true if --pretty/--json/--html was explicitly passed (not persisted)
17-
HTMLOutputFile string // set by --html (not persisted)
18-
ColorMode string // "auto", "always", "never"
19-
Verbose bool // --verbose
20-
EnableNPMScan *bool // nil=auto, true/false=explicit
21-
SearchDirs []string // defaults to ["$HOME"]
14+
Command string // "", "install", "uninstall", "send-telemetry", "configure", "configure show"
15+
OutputFormat string // "pretty", "json", "html"
16+
OutputFormatSet bool // true if --pretty/--json/--html was explicitly passed (not persisted)
17+
HTMLOutputFile string // set by --html (not persisted)
18+
ColorMode string // "auto", "always", "never"
19+
Verbose bool // --verbose
20+
EnableNPMScan *bool // nil=auto, true/false=explicit
21+
EnableBrewScan *bool // nil=auto, true/false=explicit
22+
EnablePythonScan *bool // nil=auto, true/false=explicit
23+
SearchDirs []string // defaults to ["$HOME"]
2224
}
2325

2426
// Parse parses CLI arguments and returns a Config.
@@ -69,6 +71,18 @@ func Parse(args []string) (*Config, error) {
6971
case arg == "--disable-npm-scan":
7072
v := false
7173
cfg.EnableNPMScan = &v
74+
case arg == "--enable-brew-scan":
75+
v := true
76+
cfg.EnableBrewScan = &v
77+
case arg == "--disable-brew-scan":
78+
v := false
79+
cfg.EnableBrewScan = &v
80+
case arg == "--enable-python-scan":
81+
v := true
82+
cfg.EnablePythonScan = &v
83+
case arg == "--disable-python-scan":
84+
v := false
85+
cfg.EnablePythonScan = &v
7286
case strings.HasPrefix(arg, "--color="):
7387
mode := strings.TrimPrefix(arg, "--color=")
7488
if mode != "auto" && mode != "always" && mode != "never" {
@@ -127,12 +141,16 @@ Output formats (community mode, mutually exclusive):
127141
128142
Options:
129143
--search-dirs DIR [DIR...] Search DIRs instead of $HOME (replaces default; repeatable)
130-
--enable-npm-scan Enable Node.js package scanning
131-
--disable-npm-scan Disable Node.js package scanning
132-
--verbose Show progress messages (suppressed by default)
133-
--color=WHEN Color mode: auto | always | never (default: auto)
134-
-v, --version Show version
135-
-h, --help Show this help
144+
--enable-npm-scan Enable Node.js package scanning
145+
--disable-npm-scan Disable Node.js package scanning
146+
--enable-brew-scan Enable Homebrew package scanning
147+
--disable-brew-scan Disable Homebrew package scanning
148+
--enable-python-scan Enable Python package scanning
149+
--disable-python-scan Disable Python package scanning
150+
--verbose Show progress messages (suppressed by default)
151+
--color=WHEN Color mode: auto | always | never (default: auto)
152+
-v, --version Show version
153+
-h, --help Show this help
136154
137155
Examples:
138156
%s # Pretty terminal output

0 commit comments

Comments
 (0)