From f25dcc14eb87ec139383c95e6a05d18e38876d3c Mon Sep 17 00:00:00 2001 From: Shubham Malik Date: Tue, 7 Apr 2026 19:23:24 +0530 Subject: [PATCH 1/2] chore(mdm): update release process & resolve minor issues --- .github/workflows/release.yml | 68 ++++------------------ .goreleaser.yml | 21 +++++-- CHANGELOG.md | 14 +++++ README.md | 2 +- cmd/stepsecurity-dev-machine-guard/main.go | 15 ++--- examples/sample-output.json | 2 +- internal/buildinfo/version.go | 4 +- internal/output/html_test.go | 2 +- internal/output/json_test.go | 2 +- internal/output/pretty_test.go | 4 +- internal/telemetry/telemetry.go | 2 +- 11 files changed, 57 insertions(+), 79 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff0be71..d155ef0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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) @@ -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 @@ -69,70 +68,25 @@ jobs: - name: Install cosign uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - - name: Prepare release artifacts for signing + - name: Sign artifacts with Sigstore 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" - - - name: Sign artifacts with Sigstore (keyless) - 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 + ARCHIVE=$(find dist -name 'stepsecurity-dev-machine-guard-*-darwin.tar.gz' | head -1) + cosign sign-blob "$ARCHIVE" --bundle "${ARCHIVE}.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 \ + dist/stepsecurity-dev-machine-guard-*-darwin.tar.gz.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 + dist/stepsecurity-dev-machine-guard-*-darwin.tar.gz stepsecurity-dev-machine-guard.sh diff --git a/.goreleaser.yml b/.goreleaser.yml index 74747e1..381ab10 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -21,14 +21,23 @@ 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 -checksum: - name_template: "{{ .ProjectName }}_{{ .Version }}_SHA256SUMS" - algorithm: sha256 +archives: + - id: archive + ids: + - universal + formats: + - tar.gz + strip_binary_directory: true + name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin" release: + draft: true extra_files: - glob: stepsecurity-dev-machine-guard.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a2343..1c6316e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` @@ -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 @@ -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 diff --git a/README.md b/README.md index 483910b..dca9199 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Go CI ShellCheck CI License: Apache 2.0 - Version 1.9.0 + Version 1.9.1

diff --git a/cmd/stepsecurity-dev-machine-guard/main.go b/cmd/stepsecurity-dev-machine-guard/main.go index 916d038..d6cc6b5 100644 --- a/cmd/stepsecurity-dev-machine-guard/main.go +++ b/cmd/stepsecurity-dev-machine-guard/main.go @@ -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 } diff --git a/examples/sample-output.json b/examples/sample-output.json index 6244830..ce950aa 100644 --- a/examples/sample-output.json +++ b/examples/sample-output.json @@ -1,5 +1,5 @@ { - "agent_version": "1.9.0", + "agent_version": "1.9.1", "scan_timestamp": 1741305600, "scan_timestamp_iso": "2026-03-07T00:00:00Z", "device": { diff --git a/internal/buildinfo/version.go b/internal/buildinfo/version.go index 1d5dfc0..949b9b7 100644 --- a/internal/buildinfo/version.go +++ b/internal/buildinfo/version.go @@ -3,14 +3,14 @@ package buildinfo import "fmt" const ( - Version = "1.9.0" + Version = "1.9.1" AgentURL = "https://github.com/step-security/dev-machine-guard" ) // Build-time variables set via -ldflags by goreleaser or Makefile. var ( GitCommit string // short commit hash (Makefile) or full commit (goreleaser) - ReleaseTag string // e.g., "v1.9.0" (goreleaser only) + ReleaseTag string // e.g., "v1.9.1" (goreleaser only) ReleaseBranch string // e.g., "main" (goreleaser only) ) diff --git a/internal/output/html_test.go b/internal/output/html_test.go index 9098a92..08fe980 100644 --- a/internal/output/html_test.go +++ b/internal/output/html_test.go @@ -13,7 +13,7 @@ func TestHTML_GeneratesFile(t *testing.T) { defer os.Remove(tmpFile) result := &model.ScanResult{ - AgentVersion: "1.9.0", + AgentVersion: "1.9.1", ScanTimestamp: 1700000000, ScanTimestampISO: "2023-11-14T22:13:20Z", Device: model.Device{ diff --git a/internal/output/json_test.go b/internal/output/json_test.go index f7cd061..22419ed 100644 --- a/internal/output/json_test.go +++ b/internal/output/json_test.go @@ -10,7 +10,7 @@ import ( func TestJSON_ValidOutput(t *testing.T) { result := &model.ScanResult{ - AgentVersion: "1.9.0", + AgentVersion: "1.9.1", AgentURL: "https://github.com/step-security/dev-machine-guard", ScanTimestamp: 1700000000, ScanTimestampISO: "2023-11-14T22:13:20Z", diff --git a/internal/output/pretty_test.go b/internal/output/pretty_test.go index 2fa656c..9f5971c 100644 --- a/internal/output/pretty_test.go +++ b/internal/output/pretty_test.go @@ -10,7 +10,7 @@ import ( func TestPretty_ContainsHeaders(t *testing.T) { result := &model.ScanResult{ - AgentVersion: "1.9.0", + AgentVersion: "1.9.1", ScanTimestamp: 1700000000, ScanTimestampISO: "2023-11-14T22:13:20Z", Device: model.Device{ @@ -40,7 +40,7 @@ func TestPretty_ContainsHeaders(t *testing.T) { func TestPretty_ContainsBanner(t *testing.T) { result := &model.ScanResult{ - AgentVersion: "1.9.0", + AgentVersion: "1.9.1", ScanTimestamp: 1700000000, Device: model.Device{Hostname: "test"}, AIAgentsAndTools: []model.AITool{}, diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index 4edc629..5d36c56 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -65,7 +65,7 @@ type PerformanceMetrics struct { // Output format matches the shell script's sample_log: // // ========================================== -// StepSecurity Device Agent v1.9.0 +// StepSecurity Device Agent v1.9.1 // ========================================== // [scanning] Lock acquired (PID: 32560) // [scanning] Device ID (Serial): ... From 3cbd1f0efa3b92361360e16fccdd9e4f69b186b6 Mon Sep 17 00:00:00 2001 From: Shubham Malik Date: Wed, 8 Apr 2026 20:15:15 +0530 Subject: [PATCH 2/2] fix(mdm): update release flow --- .github/workflows/release.yml | 19 ++++- .goreleaser.yml | 10 +-- docs/release-process.md | 151 +++++++++++++++++----------------- 3 files changed, 93 insertions(+), 87 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d155ef0..a10f152 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,10 +68,21 @@ jobs: - name: Install cosign uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 + - name: Locate binary + id: binary + run: | + 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 run: | - ARCHIVE=$(find dist -name 'stepsecurity-dev-machine-guard-*-darwin.tar.gz' | head -1) - cosign sign-blob "$ARCHIVE" --bundle "${ARCHIVE}.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 @@ -80,7 +91,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release upload "${{ steps.version.outputs.tag }}" \ - dist/stepsecurity-dev-machine-guard-*-darwin.tar.gz.bundle \ + "${{ steps.binary.outputs.path }}.bundle" \ dist/stepsecurity-dev-machine-guard.sh.bundle \ --clobber @@ -88,5 +99,5 @@ jobs: uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-path: | - dist/stepsecurity-dev-machine-guard-*-darwin.tar.gz + ${{ steps.binary.outputs.path }} stepsecurity-dev-machine-guard.sh diff --git a/.goreleaser.yml b/.goreleaser.yml index 381ab10..c5aeda9 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -26,16 +26,14 @@ universal_binaries: ids: - stepsecurity-dev-machine-guard replace: true - name_template: stepsecurity-dev-machine-guard + name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized" archives: - - id: archive - ids: + - ids: - universal formats: - - tar.gz - strip_binary_directory: true - name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin" + - binary + name_template: "stepsecurity-dev-machine-guard-{{ .Version }}-darwin_unnotarized" release: draft: true diff --git a/docs/release-process.md b/docs/release-process.md index 73c35c8..3e69ca5 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -1,6 +1,6 @@ # StepSecurity Dev Machine Guard — Release Process -This document describes how releases are created, signed, and verified. +This document describes how releases are created, signed, notarized, and verified. > Back to [README](../README.md) | See also: [CHANGELOG](../CHANGELOG.md) | [Versioning](../VERSIONING.md) @@ -8,16 +8,12 @@ This document describes how releases are created, signed, and verified. ## Overview -Releases are created via a manually triggered GitHub Actions workflow (`workflow_dispatch`) that requires approval from the `release` environment. The workflow uses [GoReleaser](https://goreleaser.com/) to: +Releases are a two-phase process: -1. Read the version from `internal/buildinfo/version.go` (`const Version = "1.9.0"`) -2. Verify the tag does not already exist (immutability) -3. Build platform-specific binaries with GoReleaser -4. Sign the binaries with [Sigstore](https://www.sigstore.dev/) cosign (keyless) -5. Generate SHA256 checksums -6. Create a Git tag and GitHub Release -7. Attach binaries, Sigstore bundles, and checksums as release assets -8. Generate SLSA build provenance attestation +1. **CI (automated)** — GitHub Actions builds the universal macOS binary, signs it with Sigstore, and creates a **draft** release with the binary named `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized`. +2. **Apple notarization (manual)** — Download the binary, sign and notarize it with an Apple Developer account, upload the notarized binary to the draft release, and publish. + +--- ## How to Create a Release @@ -26,109 +22,110 @@ Releases are created via a manually triggered GitHub Actions workflow (`workflow Update `Version` in `internal/buildinfo/version.go`: ```go -const Version = "1.9.0" +const Version = "1.9.1" ``` -Update the [CHANGELOG.md](../CHANGELOG.md) with a new section for the version. - -Commit and push to `main`. +Update [CHANGELOG.md](../CHANGELOG.md). Commit and push to `main`. ### 2. Trigger the release workflow 1. Go to [Actions > Release](https://github.com/step-security/dev-machine-guard/actions/workflows/release.yml) -2. Click **Run workflow** -3. Select the `main` branch -4. Click **Run workflow** +2. Click **Run workflow** on the `main` branch -### 3. Approve the release +The workflow will: +- Create a git tag (`v1.9.1`) +- Build a universal macOS binary (amd64 + arm64) via GoReleaser +- Sign with Sigstore cosign (keyless) +- Upload as `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized` to a **draft** release +- Record the SHA256 of the unnotarized binary in the release notes +- Generate SLSA build provenance attestation -The workflow uses a GitHub Environment called `release` that requires approval. A designated reviewer must approve the run before it proceeds. +### 3. Apple notarization (manual) -### 4. Verify the release +On a Mac with the Apple Developer certificate installed: -Once approved, the workflow will create the tag, build the binaries, sign them, create the GitHub Release, and upload the artifacts. Check the [Releases page](https://github.com/step-security/dev-machine-guard/releases) to confirm. +```bash +VERSION="1.9.1" ---- +# Download the unnotarized binary +gh release download "v${VERSION}" --repo step-security/dev-machine-guard \ + --pattern "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized" -## Release Artifacts +# Rename for signing +cp "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized" \ + "stepsecurity-dev-machine-guard-${VERSION}-darwin" -Each release includes the following artifacts: +# Sign with Apple Developer ID +codesign --sign "Developer ID Application: ()" \ + --options runtime --timestamp "stepsecurity-dev-machine-guard-${VERSION}-darwin" -| Artifact | Description | -|----------|-------------| -| `stepsecurity-dev-machine-guard_darwin_amd64` | macOS Intel binary | -| `stepsecurity-dev-machine-guard_darwin_arm64` | macOS Apple Silicon binary | -| `checksums.txt` | SHA256 checksums of all release artifacts | -| `*.bundle` | Sigstore cosign bundles (signature, certificate, and Rekor transparency log entry) | +# Notarize with Apple (~5 min) +xcrun notarytool submit "stepsecurity-dev-machine-guard-${VERSION}-darwin" \ + --apple-id --team-id \ + --password --wait ---- - -## Verifying a Release - -Anyone can verify the authenticity of a release artifact using [cosign](https://docs.sigstore.dev/cosign/system_config/installation/). +# Upload the notarized binary to the draft release +gh release upload "v${VERSION}" "stepsecurity-dev-machine-guard-${VERSION}-darwin" \ + --repo step-security/dev-machine-guard +``` -### Install cosign +### 4. Publish the release ```bash -# macOS -brew install cosign - -# Other platforms: https://docs.sigstore.dev/cosign/system_config/installation/ +gh release edit "v${VERSION}" --repo step-security/dev-machine-guard \ + --draft=false --latest ``` -### Verify the binary signature +--- -```bash -# Download the release artifacts -gh release download v1.9.0 --repo step-security/dev-machine-guard - -# Verify the Sigstore signature (example for Apple Silicon) -cosign verify-blob stepsecurity-dev-machine-guard_darwin_arm64 \ - --bundle stepsecurity-dev-machine-guard_darwin_arm64.bundle \ - --certificate-identity-regexp "github.com/step-security/dev-machine-guard" \ - --certificate-oidc-issuer "https://token.actions.githubusercontent.com" -``` +## Release Artifacts -A successful verification confirms: +Each release includes: -- The binary was signed by the `step-security/dev-machine-guard` GitHub Actions workflow -- The signature is recorded in the [Rekor transparency log](https://search.sigstore.dev/) -- The binary has not been tampered with since signing +| Artifact | Description | +|----------|-------------| +| `stepsecurity-dev-machine-guard-VERSION-darwin` | Notarized universal macOS binary (amd64 + arm64) | +| `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized` | Original CI-built binary (for provenance verification) | +| `stepsecurity-dev-machine-guard-VERSION-darwin_unnotarized.bundle` | Sigstore cosign bundle for the unnotarized binary | +| `stepsecurity-dev-machine-guard.sh` | Legacy shell script | +| `stepsecurity-dev-machine-guard.sh.bundle` | Sigstore cosign bundle for the shell script | -### Verify the checksum +--- -```bash -sha256sum -c checksums.txt -``` +## Verifying a Release -### Verify build provenance +### Verify a release ```bash -gh attestation verify stepsecurity-dev-machine-guard_darwin_arm64 \ - --repo step-security/dev-machine-guard -``` +VERSION="1.9.1" ---- +# Download release artifacts +gh release download "v${VERSION}" --repo step-security/dev-machine-guard \ + --pattern "stepsecurity-dev-machine-guard-${VERSION}-darwin*" -## Immutability Guarantees +# Verify Apple signature and notarization +codesign --verify --deep --strict "stepsecurity-dev-machine-guard-${VERSION}-darwin" +spctl --assess --type execute "stepsecurity-dev-machine-guard-${VERSION}-darwin" -Releases are designed to be immutable through multiple layers: +# Verify Sigstore signature on the unnotarized binary +cosign verify-blob "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized" \ + --bundle "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized.bundle" \ + --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ + --certificate-identity-regexp "github.com/.*/dev-machine-guard" -1. **Tag protection** — configure [tag protection rules](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/configuring-tag-protection-rules) in repository settings to prevent tag deletion or overwriting. -2. **Duplicate tag check** — the release workflow fails if the tag already exists, preventing accidental re-releases of the same version. -3. **Sigstore transparency log** — every signature is recorded in the public [Rekor](https://rekor.sigstore.dev/) transparency log. Even if an artifact were replaced, verification against the original log entry would fail. -4. **SLSA build provenance** — the attestation links the artifact to the exact workflow run, commit SHA, and build environment. +# Verify build provenance +gh attestation verify "stepsecurity-dev-machine-guard-${VERSION}-darwin_unnotarized" \ + --repo step-security/dev-machine-guard +``` --- -## Environment Setup - -The release workflow requires a GitHub Environment named `release` with required reviewers. To configure: +## Immutability Guarantees -1. Go to **Settings > Environments** in the repository -2. Create an environment named `release` -3. Enable **Required reviewers** and add the appropriate team members -4. Optionally restrict to the `main` branch under **Deployment branches** +1. **Draft → publish flow** — binaries are uploaded to a draft release, notarized manually, then published. Once published, the release is immutable. +2. **Sigstore transparency log** — the unnotarized binary signature is recorded in the public [Rekor](https://rekor.sigstore.dev/) transparency log. +3. **SLSA build provenance** — attestation links the artifact to the exact workflow run, commit SHA, and build environment. +4. **Duplicate tag check** — the release workflow fails if the tag already exists. ---