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
75 changes: 20 additions & 55 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ permissions: {}

jobs:
release:
name: Build, Sign & Release
name: Build & Draft Release
runs-on: ubuntu-latest
permissions:
contents: write # create tag, release, and upload assets
id-token: write # OIDC token for cosign keyless signing and build provenance
attestations: write # SLSA build provenance
contents: write
id-token: write
attestations: write

steps:
- name: Harden the runner (Audit all outbound calls)
Expand All @@ -36,12 +36,11 @@ jobs:
tag="v${version}"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "Detected version: ${version} (tag: ${tag})"

- name: Check tag does not already exist
run: |
if git rev-parse "refs/tags/${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
echo "::error::Tag ${{ steps.version.outputs.tag }} already exists. Bump Version in internal/buildinfo/version.go before releasing."
echo "::error::Tag ${{ steps.version.outputs.tag }} already exists."
exit 1
fi

Expand Down Expand Up @@ -69,70 +68,36 @@ jobs:
- name: Install cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0

- name: Prepare release artifacts for signing
- name: Locate binary
id: binary
run: |
# Copy binaries to match the exact names users download from the release.
# GoReleaser uploads as name_template (e.g. stepsecurity-dev-machine-guard_darwin_amd64)
# but keeps them in build subdirs locally. We copy to dist/ with release names
# so cosign signs the same bytes users verify against.
AMD64_SRC=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*darwin_amd64*' | head -1)
ARM64_SRC=$(find dist -type f -name 'stepsecurity-dev-machine-guard' -path '*darwin_arm64*' | head -1)

for label in "amd64:${AMD64_SRC}" "arm64:${ARM64_SRC}"; do
name="${label%%:*}"
path="${label#*:}"
if [ -z "$path" ] || [ ! -f "$path" ]; then
echo "::error::Binary not found for ${name}"
find dist -type f
exit 1
fi
done

cp "$AMD64_SRC" dist/stepsecurity-dev-machine-guard_darwin_amd64
cp "$ARM64_SRC" dist/stepsecurity-dev-machine-guard_darwin_arm64
echo "Prepared release artifacts for signing"
BINARY=$(find dist -type f -name '*darwin_unnotarized' | head -1)
if [ -z "$BINARY" ] || [ ! -f "$BINARY" ]; then
echo "::error::Binary not found"
find dist -type f
exit 1
fi
echo "path=$BINARY" >> "$GITHUB_OUTPUT"

- name: Sign artifacts with Sigstore (keyless)
- name: Sign artifacts with Sigstore
run: |
cosign sign-blob dist/stepsecurity-dev-machine-guard_darwin_amd64 \
--bundle dist/stepsecurity-dev-machine-guard_darwin_amd64.bundle --yes
cosign sign-blob dist/stepsecurity-dev-machine-guard_darwin_arm64 \
--bundle dist/stepsecurity-dev-machine-guard_darwin_arm64.bundle --yes
cosign sign-blob "${{ steps.binary.outputs.path }}" \
--bundle "${{ steps.binary.outputs.path }}.bundle" --yes
cosign sign-blob stepsecurity-dev-machine-guard.sh \
--bundle dist/stepsecurity-dev-machine-guard.sh.bundle --yes

- name: Generate checksums
run: |
# Separate checksum file for cosign-signed artifacts (script + bundles).
# GoReleaser already generates checksums for the Go binaries in its own SHA256SUMS file.
sha256sum dist/stepsecurity-dev-machine-guard_darwin_amd64 > dist/cosign-checksums.txt
sha256sum dist/stepsecurity-dev-machine-guard_darwin_arm64 >> dist/cosign-checksums.txt
sha256sum stepsecurity-dev-machine-guard.sh >> dist/cosign-checksums.txt

- name: Upload signature bundles and checksums to release
- name: Upload cosign bundles
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ steps.version.outputs.tag }}" \
dist/stepsecurity-dev-machine-guard_darwin_amd64.bundle \
dist/stepsecurity-dev-machine-guard_darwin_arm64.bundle \
"${{ steps.binary.outputs.path }}.bundle" \
dist/stepsecurity-dev-machine-guard.sh.bundle \
dist/cosign-checksums.txt \
--clobber

- name: Mark release as immutable (not a draft, not a prerelease)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release edit "${{ steps.version.outputs.tag }}" \
--draft=false \
--prerelease=false \
--latest

- name: Attest build provenance
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: |
dist/stepsecurity-dev-machine-guard_darwin_amd64
dist/stepsecurity-dev-machine-guard_darwin_arm64
${{ steps.binary.outputs.path }}
stepsecurity-dev-machine-guard.sh
19 changes: 13 additions & 6 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,21 @@ builds:
env:
- CGO_ENABLED=0

archives:
- format: binary
name_template: "{{ .Binary }}_{{ .Os }}_{{ .Arch }}"
universal_binaries:
- id: universal
ids:
- stepsecurity-dev-machine-guard
replace: true
name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized"

checksum:
name_template: "{{ .ProjectName }}_{{ .Version }}_SHA256SUMS"
algorithm: sha256
archives:
- ids:
- universal
formats:
- binary
name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized"

release:
draft: true
extra_files:
- glob: stepsecurity-dev-machine-guard.sh
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

See [VERSIONING.md](VERSIONING.md) for why the version starts at 1.8.1.

## [1.9.1] - 2026-04-07

### Fixed

- Config `quiet: false` now correctly shows progress (was ignored previously).
- Enterprise auto-detect mode respects the configured quiet setting instead of overriding it.
- Release now produces a single universal macOS binary (amd64 + arm64).

## [1.9.0] - 2026-04-03

Migrated from shell script to a compiled Go binary. All existing scanning features, detection logic, CLI flags, output formats, and enterprise telemetry are preserved — this release changes the implementation, not the functionality.

### Added

- **Go binary**: Single compiled binary (`stepsecurity-dev-machine-guard`) replaces the shell script. Zero external dependencies, no runtime required.
- **`configure` / `configure show` commands**: Interactive setup and display of enterprise credentials, search directories, and preferences. Saved to `~/.stepsecurity/config.json`.

## [1.8.2] - 2026-03-17

### Added

- `--search-dirs DIR [DIR...]` flag to scan specific directories instead of `$HOME` (replaces default; repeatable)
- Accepts multiple directories in a single flag: `--search-dirs /tmp /opt /var`
- Supports repeated use: `--search-dirs /tmp --search-dirs /opt`
Expand All @@ -28,6 +38,7 @@ Migrated from shell script to a compiled Go binary. All existing scanning featur
First open-source release. The scanning engine was previously an internal enterprise tool (v1.0.0-v1.8.1) running in production. This release adds community mode for local-only scanning while keeping the enterprise codebase intact.

### Added

- **Community mode** with three output formats: pretty terminal, JSON, and HTML report
- **AI agent and CLI tool detection**: Claude Code, Codex, Gemini CLI, Kiro, Aider, OpenCode, and more
- **General-purpose AI agent detection**: OpenClaw, ClawdBot, GPT-Engineer, Claude Cowork
Expand All @@ -41,17 +52,20 @@ First open-source release. The scanning engine was previously an internal enterp
- ShellCheck CI workflow with Harden-Runner

### Changed

- Enterprise config variables are now clearly labeled and placed below the community-facing header
- Progress messages suppressed by default in community mode (enable with `--verbose`)
- Node.js scanning off by default in community mode (enable with `--enable-npm-scan`)

### Enterprise (unchanged from v1.8.1)

- `install`, `uninstall`, and `send-telemetry` commands
- Launchd scheduling (LaunchDaemon for root, LaunchAgent for user)
- S3 presigned URL upload with backend notification
- Execution log capture and base64 encoding
- Instance locking to prevent concurrent runs

[1.9.1]: https://github.com/step-security/dev-machine-guard/compare/v1.9.0...v1.9.1
[1.9.0]: https://github.com/step-security/dev-machine-guard/compare/v1.8.2...v1.9.0
[1.8.2]: https://github.com/step-security/dev-machine-guard/compare/v1.8.1...v1.8.2
[1.8.1]: https://github.com/step-security/dev-machine-guard/releases/tag/v1.8.1
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<a href="https://github.com/step-security/dev-machine-guard/actions/workflows/go.yml"><img src="https://github.com/step-security/dev-machine-guard/actions/workflows/go.yml/badge.svg" alt="Go CI"></a>
<a href="https://github.com/step-security/dev-machine-guard/actions/workflows/shellcheck.yml"><img src="https://github.com/step-security/dev-machine-guard/actions/workflows/shellcheck.yml/badge.svg" alt="ShellCheck CI"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License: Apache 2.0"></a>
<a href="https://github.com/step-security/dev-machine-guard/releases"><img src="https://img.shields.io/badge/version-1.9.0-purple.svg" alt="Version 1.9.0"></a>
<a href="https://github.com/step-security/dev-machine-guard/releases"><img src="https://img.shields.io/badge/version-1.9.1-purple.svg" alt="Version 1.9.1"></a>
</p>

<p align="center">
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 @@ -37,26 +37,27 @@ func main() {
}
if !cfg.OutputFormatSet && config.OutputFormat != "" {
cfg.OutputFormat = config.OutputFormat
cfg.OutputFormatSet = true // treat saved format as explicitly set
// Note: do NOT set OutputFormatSet here — saved config is a default preference,
// not an explicit CLI flag. Enterprise auto-detection should still work
// when no CLI flags are passed.
if config.OutputFormat == "html" && cfg.HTMLOutputFile == "" && config.HTMLOutputFile != "" {
cfg.HTMLOutputFile = config.HTMLOutputFile
}
}

exec := executor.NewReal()
quiet := !cfg.Verbose
// Apply saved quiet preference
if config.Quiet != nil && *config.Quiet {
quiet = true

// Quiet resolution: config is the base, CLI overrides.
quiet := true
if config.Quiet != nil {
quiet = *config.Quiet
}
// --verbose always overrides quiet config
if cfg.Verbose {
quiet = false
}
if cfg.OutputFormat == "json" {
quiet = true
}
// Enterprise commands (send-telemetry, install) always show progress
if cfg.Command == "send-telemetry" || cfg.Command == "install" {
quiet = false
}
Expand Down
Loading
Loading