diff --git a/.github/WORKFLOW_ARCHITECTURE.md b/.github/WORKFLOW_ARCHITECTURE.md index d5f1dfe5..29e1f813 100644 --- a/.github/WORKFLOW_ARCHITECTURE.md +++ b/.github/WORKFLOW_ARCHITECTURE.md @@ -217,7 +217,7 @@ FOR EACH provider in [docker, fly, devpod-aws, devpod-do, ...]: ### ci-v3.yml - v3 Rust CI -**Triggers**: Changes to `v3/` directory +**Triggers**: every push and pull request to `main`, `develop`, `feature/v3`, `v3.1.x` (no path filters), plus `workflow_dispatch` **Jobs**: @@ -225,7 +225,7 @@ FOR EACH provider in [docker, fly, devpod-aws, devpod-do, ...]: - **rust-clippy**: `cargo clippy` linting - **rust-test**: `cargo test` unit tests - **rust-build**: Release build -- **security-audit**: `cargo audit` +- **dependency-audit** ("Dependency Audit"): `cargo audit --deny warnings` in `v3/`, ignores in `v3/.cargo/audit.toml` - **docs-build**: `cargo doc` - **test-extensions**: Extension validation - **ci-required/ci-status**: Status gates diff --git a/.github/scripts/v3/cargo-audit.sh b/.github/scripts/v3/cargo-audit.sh index 4b308ac0..769d1c9e 100755 --- a/.github/scripts/v3/cargo-audit.sh +++ b/.github/scripts/v3/cargo-audit.sh @@ -1,36 +1,10 @@ #!/usr/bin/env bash -# Run cargo audit with exceptions from v3/audit-ignore +# Run the v3 dependency audit exactly as CI's "Dependency Audit" job does. +# Ignored advisories live only in v3/.cargo/audit.toml (read by cargo audit itself). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" -IGNORE_FILE="$REPO_ROOT/v3/audit-ignore" - -if [[ ! -f "$IGNORE_FILE" ]]; then - echo "No audit-ignore file found at $IGNORE_FILE" - echo "Running cargo audit without exceptions..." - cd "$REPO_ROOT/v3" - exec cargo audit --deny warnings "$@" -fi - -# Build --ignore flags from the exceptions file -IGNORE_FLAGS=() -ADVISORY_IDS=() -while IFS= read -r line; do - # Skip empty lines and comments - line="${line%%#*}" # strip inline comments - line="$(echo "$line" | xargs)" # trim whitespace - [[ -z "$line" ]] && continue - IGNORE_FLAGS+=(--ignore "$line") - ADVISORY_IDS+=("$line") -done < "$IGNORE_FILE" - -if [[ ${#ADVISORY_IDS[@]} -gt 0 ]]; then - echo "Ignoring ${#ADVISORY_IDS[@]} advisory exception(s):" - for id in "${ADVISORY_IDS[@]}"; do - echo " - $id" - done -fi cd "$REPO_ROOT/v3" -exec cargo audit --deny warnings "${IGNORE_FLAGS[@]}" "$@" +exec cargo audit --deny warnings "$@" diff --git a/.github/workflows/ci-v3.yml b/.github/workflows/ci-v3.yml index 65e9f561..e3bac82d 100644 --- a/.github/workflows/ci-v3.yml +++ b/.github/workflows/ci-v3.yml @@ -4,22 +4,13 @@ name: "v3: CI" # Triggers on changes to v3 directory and related configurations on: + # No path filters: every push and pull request on these branches runs the + # workflow, so the "Dependency Audit" required check always reports and a + # push to v3.1.x always produces the CI image that release-v3.yml promotes. push: branches: [main, develop, feature/v3, v3.1.x] - paths: - - 'v3/**' - - '.github/workflows/ci-v3.yml' - - '.github/workflows/v3-*.yml' - - '.github/actions/v3/**' - - '.github/scripts/v3/**' pull_request: branches: [main, develop, feature/v3, v3.1.x] - paths: - - 'v3/**' - - '.github/workflows/ci-v3.yml' - - '.github/workflows/v3-*.yml' - - '.github/actions/v3/**' - - '.github/scripts/v3/**' workflow_dispatch: inputs: test-extensions: @@ -27,11 +18,6 @@ on: required: false default: true type: boolean - run-audit: - description: 'Run security audit' - required: false - default: true - type: boolean test-cloud-providers: description: 'Run cloud provider integration tests (RunPod/Northflank)' required: false @@ -444,8 +430,9 @@ jobs: - name: Scan with Trivy id: trivy continue-on-error: true - uses: aquasecurity/trivy-action@0.35.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: + version: v0.76.0 image-ref: ghcr.io/${{ github.repository }}:v3-ci-${{ github.sha }}-ubuntu format: sarif output: trivy-results.sarif @@ -475,17 +462,20 @@ jobs: # Mark Promotion Candidate # ============================================ + # Runs for pushes to main and to the v3.1.x maintenance branch, so the commit + # a v3.1.x release tag points at has the v3-ci-passed-- images + # that release-v3.yml's verify-ci-image job looks up. mark-passed: name: Mark Promotion Candidates needs: [build-image, security-scan, k8s-cluster-lifecycle] - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' + if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/v3.1.x') runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -510,28 +500,35 @@ jobs: echo "- **Status**: Ready for release promotion" >> $GITHUB_STEP_SUMMARY # ============================================ - # Security Audit (Cargo) + # Dependency Audit (cargo audit) # ============================================ + # Standard dependency audit: `cargo audit --deny warnings` in v3/. + # Ignored advisories live only in v3/.cargo/audit.toml, each documented there. + # Runs on every push, pull_request and workflow_dispatch (no `if:`), and + # CI v3 Required Checks / CI v3 Status fail when it fails. - security-audit: - name: Security Audit + dependency-audit: + name: Dependency Audit runs-on: ubuntu-latest - # Runs on push and pull_request; on manual dispatch only when run-audit is true. - if: github.event_name != 'workflow_dispatch' || github.event.inputs.run-audit == 'true' + permissions: + contents: read steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - name: Setup Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable - name: Install cargo-binstall - uses: cargo-bins/cargo-binstall@v1.17.7 + uses: cargo-bins/cargo-binstall@1800853f2578f8c34492ec76154caef8e163fbca # v1.17.7 - - name: Install cargo-audit - run: cargo binstall cargo-audit --no-confirm + - name: Install cargo-audit 0.22.2 + run: | + cargo binstall --no-confirm --locked cargo-audit@0.22.2 + cargo audit --version - - name: Run cargo audit - run: .github/scripts/v3/cargo-audit.sh + - name: cargo audit --deny warnings + working-directory: v3 + run: cargo audit --deny warnings # ============================================ # Documentation Build @@ -1191,6 +1188,7 @@ jobs: - rust-build - build-image - security-scan + - dependency-audit if: ${{ always() }} steps: - name: Check required jobs @@ -1203,12 +1201,14 @@ jobs: BUILD="${{ needs.rust-build.result }}" IMAGE="${{ needs.build-image.result }}" SECURITY="${{ needs.security-scan.result }}" + AUDIT="${{ needs.dependency-audit.result }}" if [[ "$FORMAT" != "success" ]] || \ [[ "$CLIPPY" != "success" ]] || \ [[ "$TEST" != "success" ]] || \ [[ "$BUILD" != "success" ]] || \ - [[ "$IMAGE" != "success" ]]; then + [[ "$IMAGE" != "success" ]] || \ + [[ "$AUDIT" != "success" ]]; then echo "❌ Required CI v3 checks failed" echo "Results:" echo " rust-format: $FORMAT" @@ -1217,6 +1217,7 @@ jobs: echo " rust-build: $BUILD" echo " build-image: $IMAGE" echo " security-scan: $SECURITY" + echo " dependency-audit: $AUDIT" exit 1 fi @@ -1233,7 +1234,7 @@ jobs: needs: - ci-required - coverage - - security-audit + - dependency-audit - docs-build - test-extensions - test-providers @@ -1249,7 +1250,7 @@ jobs: - name: Check overall status run: | REQUIRED="${{ needs.ci-required.result }}" - AUDIT="${{ needs.security-audit.result }}" + AUDIT="${{ needs.dependency-audit.result }}" DOCS="${{ needs.docs-build.result }}" EXTENSIONS="${{ needs.test-extensions.result }}" PROVIDERS="${{ needs.test-providers.result }}" @@ -1264,10 +1265,9 @@ jobs: exit 1 fi - # The dependency audit is a gate: anything but success fails CI - # (skipped is allowed only for a manual dispatch with run-audit=false). - if [[ "$AUDIT" != "success" ]] && ! [[ "$AUDIT" == "skipped" && "${{ github.event_name }}" == "workflow_dispatch" ]]; then - echo "❌ Security audit (cargo audit) did not pass: $AUDIT" + # The Dependency Audit is a gate: anything but success fails CI. + if [[ "$AUDIT" != "success" ]]; then + echo "❌ Dependency Audit (cargo audit --deny warnings) did not pass: $AUDIT" exit 1 fi @@ -1282,7 +1282,7 @@ jobs: echo "✅ CI v3 passed" echo "Results:" echo " ci-required: $REQUIRED" - echo " security-audit: $AUDIT" + echo " dependency-audit: $AUDIT" echo " docs-build: $DOCS" echo " test-extensions: $EXTENSIONS" echo " test-providers: $PROVIDERS" @@ -1312,7 +1312,7 @@ jobs: echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY echo "| Required Checks | ${{ needs.ci-required.result }} |" >> $GITHUB_STEP_SUMMARY - echo "| Security Audit | ${{ needs.security-audit.result }} |" >> $GITHUB_STEP_SUMMARY + echo "| Dependency Audit | ${{ needs.dependency-audit.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Documentation Build | ${{ needs.docs-build.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Extension Tests | ${{ needs.test-extensions.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Provider Unit Tests (RunPod/Northflank) | ${{ needs.test-providers.result }} |" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release-v3.yml b/.github/workflows/release-v3.yml index eba0c799..6ec6cf4b 100644 --- a/.github/workflows/release-v3.yml +++ b/.github/workflows/release-v3.yml @@ -53,7 +53,7 @@ jobs: changelog: ${{ steps.changelog.outputs.changelog }} changelog_file: ${{ steps.changelog.outputs.changelog_file }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: fetch-depth: 0 @@ -76,7 +76,7 @@ jobs: echo "changelog_file=changelog.md" >> "$GITHUB_OUTPUT" - name: Upload changelog artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: changelog-v3 path: changelog.md @@ -108,10 +108,10 @@ jobs: platform: windows-x86_64 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - name: Setup Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable with: targets: ${{ matrix.target }} @@ -121,7 +121,7 @@ jobs: cargo install cross --git https://github.com/cross-rs/cross - name: Cache Cargo dependencies - uses: actions/cache@v5 + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0 with: path: | ~/.cargo/bin/ @@ -167,7 +167,7 @@ jobs: shell: pwsh - name: Upload binary artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: binary-${{ matrix.platform }} path: | @@ -188,7 +188,7 @@ jobs: commit_sha: ${{ steps.verify.outputs.commit_sha }} all_distros_available: ${{ steps.check-distros.outputs.all_distros_available }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: fetch-depth: 0 @@ -200,7 +200,7 @@ jobs: echo "Tag ${{ github.ref_name }} points to commit: $SHA" - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -335,7 +335,7 @@ jobs: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} steps: - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -343,7 +343,7 @@ jobs: - name: Login to Docker Hub (optional) if: env.DOCKERHUB_TOKEN != '' - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ env.DOCKERHUB_USERNAME }} password: ${{ env.DOCKERHUB_TOKEN }} @@ -361,7 +361,7 @@ jobs: - name: Generate tags for ${{ matrix.distro }} id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: ${{ steps.images.outputs.list }} tags: | @@ -438,10 +438,10 @@ jobs: attestations: write # Required for provenance attestation steps: - name: Install Cosign - uses: sigstore/cosign-installer@v4.1.0 + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -482,7 +482,7 @@ jobs: attestations: write steps: - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -501,7 +501,7 @@ jobs: echo "Image digest for ${{ matrix.distro }}: $DIGEST_HASH" - name: Attest build provenance - uses: actions/attest-build-provenance@v4 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-name: ghcr.io/${{ github.repository }} subject-digest: ${{ steps.digest.outputs.digest }} @@ -517,14 +517,14 @@ jobs: id-token: write # Required for cosign 3.x keyless operations steps: - name: Login to GHCR - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Install Cosign - uses: sigstore/cosign-installer@v4.1.0 + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Get image digest id: digest @@ -538,7 +538,7 @@ jobs: echo "digest=$DIGEST_HASH" >> $GITHUB_OUTPUT - name: Generate SBOM - uses: anchore/sbom-action@v0.22.2 + uses: anchore/sbom-action@28d71544de8eaf1b958d335707167c5f783590ad # v0.22.2 with: image: ghcr.io/${{ github.repository }}:${{ needs.validate-tag.outputs.version }} format: spdx-json @@ -558,7 +558,7 @@ jobs: echo " cosign download sbom ghcr.io/${REPO}:${{ needs.validate-tag.outputs.version }}" - name: Upload SBOM artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: sbom-v${{ needs.validate-tag.outputs.version }} path: sbom.spdx.json @@ -572,16 +572,16 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: "24" registry-url: "https://registry.npmjs.org" - name: Download binary artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: path: artifacts @@ -671,23 +671,43 @@ jobs: npm publish --provenance --access public --tag "${tag}" # Job 9: Update CHANGELOG.md and Cargo.toml + # v3.1.x is the v3 maintenance branch: main has no v3 product code and is + # protected, so this job works on v3.1.x. v3.1.x is protected too, and the + # release PR already carries the CHANGELOG entry and the version, so the job + # skips (without committing or pushing) when both are already present. update-version-files: runs-on: ubuntu-latest needs: [validate-tag, generate-changelog] permissions: contents: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: - ref: main + ref: v3.1.x fetch-depth: 0 + - name: Check whether the release PR already recorded this version + id: guard + run: | + version="${{ needs.validate-tag.outputs.version }}" + cargo_version=$(sed -n '/^\[workspace\.package\]/,/^\[/ s/^version = "\(.*\)"$/\1/p' v3/Cargo.toml | head -n 1) + echo "Released version: $version; v3/Cargo.toml [workspace.package] version: $cargo_version" + + if grep -qF "## [$version]" v3/CHANGELOG.md && [[ "$cargo_version" == "$version" ]]; then + echo "skip=true" >> "$GITHUB_OUTPUT" + echo "::notice title=Version files already up to date::v3/CHANGELOG.md has [$version] and v3/Cargo.toml is $cargo_version on v3.1.x; nothing to commit or push" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + - name: Download changelog artifact - uses: actions/download-artifact@v8 + if: steps.guard.outputs.skip != 'true' + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: changelog-v3 - name: Update v3/CHANGELOG.md + if: steps.guard.outputs.skip != 'true' run: | version="${{ needs.validate-tag.outputs.version }}" changelog_header='# Changelog - Sindri v3 @@ -731,6 +751,7 @@ jobs: - name: Bump v3/Cargo.toml to NEXT version for development id: bump-version + if: steps.guard.outputs.skip != 'true' run: | released_version="${{ needs.validate-tag.outputs.version }}" @@ -769,15 +790,18 @@ jobs: echo "Updated v3/Cargo.toml to version $next_version for next development cycle" - name: Setup Rust toolchain - uses: dtolnay/rust-toolchain@stable + if: steps.guard.outputs.skip != 'true' + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable - name: Regenerate Cargo.lock + if: steps.guard.outputs.skip != 'true' run: | cd v3 cargo generate-lockfile echo "Regenerated v3/Cargo.lock" - name: Commit version updates + if: steps.guard.outputs.skip != 'true' run: | released_version="${{ needs.validate-tag.outputs.version }}" next_version="${{ steps.bump-version.outputs.next_version }}" @@ -798,7 +822,7 @@ jobs: Next development version: v$next_version [skip ci]" - git push origin main + git push origin HEAD:v3.1.x # Job 9: Create GitHub Release create-release: @@ -809,20 +833,20 @@ jobs: outputs: release_url: ${{ steps.release.outputs.url }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - name: Download all binary artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: path: artifacts - name: Download changelog - uses: actions/download-artifact@v8 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: changelog-v3 - name: Download SBOM - uses: actions/download-artifact@v8 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: sbom-v${{ needs.validate-tag.outputs.version }} @@ -1017,7 +1041,7 @@ jobs: - name: Create GitHub Release id: release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 with: name: Sindri v${{ needs.validate-tag.outputs.version }} (Rust) body_path: changelog.md @@ -1034,7 +1058,7 @@ jobs: contents: read issues: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 - name: Notify success if: needs.create-release.result == 'success' @@ -1062,7 +1086,7 @@ jobs: - name: Create failure issue if: failure() - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: | const version = '${{ needs.validate-tag.outputs.version }}'; diff --git a/Makefile b/Makefile index 7da30da5..12fa111c 100644 --- a/Makefile +++ b/Makefile @@ -888,7 +888,7 @@ v3-audit: @echo "$(BLUE)Running v3 security audit...$(RESET)" @command -v cargo-audit >/dev/null 2>&1 || { \ echo "$(BLUE)Installing cargo-audit...$(RESET)"; \ - cargo install cargo-audit; \ + cargo install cargo-audit --version 0.22.2 --locked; \ } .github/scripts/v3/cargo-audit.sh @echo "$(GREEN)✓ Security audit complete$(RESET)" diff --git a/packages/@pacphi/sindri-cli-darwin-arm64/package.json b/packages/@pacphi/sindri-cli-darwin-arm64/package.json index b268e3bc..e02e5fc5 100644 --- a/packages/@pacphi/sindri-cli-darwin-arm64/package.json +++ b/packages/@pacphi/sindri-cli-darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli-darwin-arm64", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI binary — macOS Apple Silicon (arm64)", "license": "MIT", "repository": { diff --git a/packages/@pacphi/sindri-cli-darwin-x64/package.json b/packages/@pacphi/sindri-cli-darwin-x64/package.json index d161ed4d..c4d57d78 100644 --- a/packages/@pacphi/sindri-cli-darwin-x64/package.json +++ b/packages/@pacphi/sindri-cli-darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli-darwin-x64", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI binary — macOS Intel (x64)", "license": "MIT", "repository": { diff --git a/packages/@pacphi/sindri-cli-linux-arm64/package.json b/packages/@pacphi/sindri-cli-linux-arm64/package.json index 6594d559..54ba2379 100644 --- a/packages/@pacphi/sindri-cli-linux-arm64/package.json +++ b/packages/@pacphi/sindri-cli-linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli-linux-arm64", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI binary — Linux arm64", "license": "MIT", "repository": { diff --git a/packages/@pacphi/sindri-cli-linux-x64/package.json b/packages/@pacphi/sindri-cli-linux-x64/package.json index d1f06ed4..1826015a 100644 --- a/packages/@pacphi/sindri-cli-linux-x64/package.json +++ b/packages/@pacphi/sindri-cli-linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli-linux-x64", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI binary — Linux x64", "license": "MIT", "repository": { diff --git a/packages/@pacphi/sindri-cli-win32-x64/package.json b/packages/@pacphi/sindri-cli-win32-x64/package.json index fa815a6b..3baba7db 100644 --- a/packages/@pacphi/sindri-cli-win32-x64/package.json +++ b/packages/@pacphi/sindri-cli-win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli-win32-x64", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI binary — Windows x64", "license": "MIT", "repository": { diff --git a/packages/@pacphi/sindri-cli/package.json b/packages/@pacphi/sindri-cli/package.json index 3ff968c4..f63e77f0 100644 --- a/packages/@pacphi/sindri-cli/package.json +++ b/packages/@pacphi/sindri-cli/package.json @@ -1,6 +1,6 @@ { "name": "@pacphi/sindri-cli", - "version": "3.1.0", + "version": "3.1.4", "description": "Sindri CLI — declarative cloud development environment manager", "homepage": "https://github.com/pacphi/sindri", "license": "MIT", @@ -19,11 +19,11 @@ "postinstall": "node lib/install.js" }, "optionalDependencies": { - "@pacphi/sindri-cli-darwin-arm64": "3.1.0", - "@pacphi/sindri-cli-darwin-x64": "3.1.0", - "@pacphi/sindri-cli-linux-x64": "3.1.0", - "@pacphi/sindri-cli-linux-arm64": "3.1.0", - "@pacphi/sindri-cli-win32-x64": "3.1.0" + "@pacphi/sindri-cli-darwin-arm64": "3.1.4", + "@pacphi/sindri-cli-darwin-x64": "3.1.4", + "@pacphi/sindri-cli-linux-x64": "3.1.4", + "@pacphi/sindri-cli-linux-arm64": "3.1.4", + "@pacphi/sindri-cli-win32-x64": "3.1.4" }, "engines": { "node": ">=24" diff --git a/v3/.cargo/audit.toml b/v3/.cargo/audit.toml new file mode 100644 index 00000000..ebcc65ad --- /dev/null +++ b/v3/.cargo/audit.toml @@ -0,0 +1,44 @@ +# cargo-audit configuration for the v3 line (read by `cargo audit` run from v3/). +# +# CI's "Dependency Audit" job runs `cargo audit --deny warnings` in v3/, so warnings +# (unmaintained, unsound, yanked) fail the build unless they are ignored here. +# This file is the ONLY place advisories are ignored on this line. +# +# Each entry documents: advisory and link, dependency path, why it can't be fixed +# in-major, reachability, and the exit condition. Only "unmaintained" warnings are +# listed; no vulnerability is ignored. + +[advisories] +ignore = [ + # RUSTSEC-2026-0374: basic-toml is unmaintained (no vulnerability, no patched release) + # https://rustsec.org/advisories/RUSTSEC-2026-0374 + # Path: sindri-secrets -> age 0.11.2 -> i18n-embed-fl 0.9.4 / i18n-embed-impl 0.8.4 + # (proc-macros) -> i18n-config 0.4.8 -> basic-toml 0.1.10 + # Cannot fix in-major: i18n-config 0.4.8 is the latest 0.4.x and still depends on basic-toml. + # Reachability: compile-time only (read by proc-macros while building age); not in the + # sindri binary's runtime paths. + # Exit: remove once an age 0.11.x / i18n-config 0.4.x release drops basic-toml. + "RUSTSEC-2026-0374", + + # RUSTSEC-2026-0173: proc-macro-error2 is unmaintained (no vulnerability, no patched release) + # https://rustsec.org/advisories/RUSTSEC-2026-0173 + # Path: sindri -> tabled 0.20.0 -> tabled_derive 0.11.0 (proc-macro), and + # sindri-secrets -> age 0.11.2 -> i18n-embed-fl 0.9.4 (proc-macro) -> proc-macro-error2 2.0.1 + # Cannot fix in-major: tabled_derive 0.11.0 and i18n-embed-fl 0.9.4 are the latest in their + # 0.x minors. + # Reachability: compile-time only (proc-macro error reporting); nothing of it ships in the binary. + # Exit: remove once tabled 0.20.x and i18n-embed-fl 0.9.x drop it, or when this line moves + # to tabled 0.21+. + "RUSTSEC-2026-0173", + + # RUSTSEC-2024-0449: unsafe-libyaml is unmaintained (no vulnerability, no patched release) + # https://rustsec.org/advisories/RUSTSEC-2024-0449 + # Path: serde_yaml_ng 0.10.0 -> unsafe-libyaml 0.2.11 (sindri, sindri-core, sindri-clusters, ...) + # Cannot fix in-major: every serde_yaml_ng 0.10.x uses unsafe-libyaml; replacing the YAML + # crate is an API change. + # Reachability: used at runtime to parse sindri.yaml and extension YAML (local, user-supplied + # files). + # Exit: remove when the v3 line replaces serde_yaml_ng, or a serde_yaml_ng 0.10.x drops + # unsafe-libyaml. + "RUSTSEC-2024-0449", +] diff --git a/v3/CHANGELOG.md b/v3/CHANGELOG.md index 1dd81499..21721a59 100644 --- a/v3/CHANGELOG.md +++ b/v3/CHANGELOG.md @@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## [3.1.4] - 2026-10-23 + +Patch release from the `v3.1.x` maintenance branch: the security fixes from #385 plus the standard dependency audit. No new features and no breaking changes. + +### Security + +`cargo audit` on `v3/Cargo.lock` goes from 23 vulnerabilities and 12 warnings to 0 vulnerabilities. The 3 remaining unmaintained-crate warnings are ignored, with justification, in `v3/.cargo/audit.toml`. All fixes stay within each dependency's current major (#385): + +- **rustls 0.21 / rustls-webpki 0.101 / hyper 0.14 / h2 0.3 chain removed:** `aws-sdk-s3` 1.127.0 → 1.152.0 without its legacy `rustls` feature. HTTPS still uses rustls 0.23. This clears RUSTSEC-2026-0346, -0349, -0098, -0099, -0104, -0382 and -0258, and moves `lru` to 0.18.5 (RUSTSEC-2026-0253). +- **rustls** 0.23.46 (RUSTSEC-2026-0285, -0346, -0348, -0349) +- **rustls-webpki** 0.103.16 (RUSTSEC-2026-0098, -0099, -0104, -0382) +- **quinn-proto** 0.11.19 (RUSTSEC-2026-0185, -0338, -0339, -0340, -0342) +- **h2** 0.4.20 (RUSTSEC-2026-0258) +- **webpki-root-certs** 1.0.10 (RUSTSEC-2026-0383) +- **crossbeam-epoch** 0.9.21 (RUSTSEC-2026-0204) +- **ctutils** 0.4.4 (RUSTSEC-2026-0385) +- **Unsound advisories:** tokio 1.53.3 (RUSTSEC-2026-0386), anyhow 1.0.104 (RUSTSEC-2026-0190), rand 0.8.8 / 0.9.5 / 0.10.3 (RUSTSEC-2026-0097) +- **serial_test** 3.5 (dev-only), which drops `scc` 2 (RUSTSEC-2026-0205) +- **Advisories only in GHSA/OSV:** tar 0.4.46 (GHSA-3pv8-6f4r-ffg2), aws-smithy-json 0.62.7 (GHSA-8ffr-xgwf-xj56) +- **Yanked versions replaced:** chacha20 0.10.2, spin 0.10.1 + +### Changed + +- **Toolchain:** Rust 1.94.0 → 1.94.1 (`v3/rust-toolchain.toml`), and `rust-version` 1.94 → 1.94.1. The AWS SDK crates already require rustc 1.94.1. +- **CI dependency audit:** CI runs on pull requests to and pushes on `v3.1.x`. The `Dependency Audit` job (`cargo audit --deny warnings`, cargo-audit 0.22.2) is a blocking gate, and ignored advisories live only in `v3/.cargo/audit.toml`. +- **Release workflow:** releases run from `v3.1.x`, with CI images marked as promotion candidates on `v3.1.x` pushes, and version bookkeeping that targets `v3.1.x` instead of `main`. + +### Fixed + +- **Log-retention test:** `sindri-extensions`' log-retention test dates its "recent" log file at run time. The hard-coded date had fallen outside the 90-day window. + ## [3.1.2] - 2026-03-21 ### Fixed @@ -16,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Installation #### Docker Image + ```bash # Pull the Docker image docker pull ghcr.io/pacphi/sindri:3.1.2 @@ -28,6 +60,7 @@ docker run -d --name sindri \ ``` #### CLI Binary + ```bash # Download and install from release assets # Linux (x86_64) @@ -43,8 +76,6 @@ sudo mv sindri /usr/local/bin/ **Full Changelog**: https://github.com/pacphi/sindri/compare/v3.1.1...v3.1.2 - - ## [3.1.1] - 2026-03-21 ### Added @@ -65,6 +96,7 @@ sudo mv sindri /usr/local/bin/ ### Installation #### Docker Image + ```bash # Pull the Docker image docker pull ghcr.io/pacphi/sindri:3.1.1 @@ -77,6 +109,7 @@ docker run -d --name sindri \ ``` #### CLI Binary + ```bash # Download and install from release assets # Linux (x86_64) @@ -92,8 +125,6 @@ sudo mv sindri /usr/local/bin/ **Full Changelog**: https://github.com/pacphi/sindri/compare/v3.1.0...v3.1.1 - - ## [3.1.0] - 2026-02-25 ### Added diff --git a/v3/Cargo.lock b/v3/Cargo.lock index 6b996703..76ff451f 100644 --- a/v3/Cargo.lock +++ b/v3/Cargo.lock @@ -4345,7 +4345,7 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "sindri" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "base64 0.22.1", @@ -4394,7 +4394,7 @@ dependencies = [ [[package]] name = "sindri-backup" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "async-trait", @@ -4419,7 +4419,7 @@ dependencies = [ [[package]] name = "sindri-clusters" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "async-trait", @@ -4438,7 +4438,7 @@ dependencies = [ [[package]] name = "sindri-core" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "camino", @@ -4463,7 +4463,7 @@ dependencies = [ [[package]] name = "sindri-doctor" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "dialoguer", @@ -4483,7 +4483,7 @@ dependencies = [ [[package]] name = "sindri-extensions" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "camino", @@ -4521,7 +4521,7 @@ dependencies = [ [[package]] name = "sindri-image" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "base64 0.22.1", @@ -4543,7 +4543,7 @@ dependencies = [ [[package]] name = "sindri-packer" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "async-trait", @@ -4570,7 +4570,7 @@ dependencies = [ [[package]] name = "sindri-projects" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "camino", @@ -4593,7 +4593,7 @@ dependencies = [ [[package]] name = "sindri-providers" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "async-trait", @@ -4622,7 +4622,7 @@ dependencies = [ [[package]] name = "sindri-secrets" -version = "3.1.3" +version = "3.1.4" dependencies = [ "age", "anyhow", @@ -4654,7 +4654,7 @@ dependencies = [ [[package]] name = "sindri-update" -version = "3.1.3" +version = "3.1.4" dependencies = [ "anyhow", "bytes", diff --git a/v3/Cargo.toml b/v3/Cargo.toml index bd07b584..a4e03ff9 100644 --- a/v3/Cargo.toml +++ b/v3/Cargo.toml @@ -16,9 +16,9 @@ members = [ ] [workspace.package] -version = "3.1.3" +version = "3.1.4" edition = "2021" -rust-version = "1.94" +rust-version = "1.94.1" license = "MIT" repository = "https://github.com/pacphi/sindri" authors = ["Sindri Team"] diff --git a/v3/audit-ignore b/v3/audit-ignore deleted file mode 100644 index cdc64d04..00000000 --- a/v3/audit-ignore +++ /dev/null @@ -1,24 +0,0 @@ -# Advisories ignored by .github/scripts/v3/cargo-audit.sh (cargo audit --deny warnings). -# Each entry: dependency path, why it can't be fixed on this line, reachability, exit condition. -# Only "unmaintained" warnings are listed; no vulnerability is ignored. - -# RUSTSEC-2026-0374: basic-toml is unmaintained (no vulnerability, no patched release) -# Path: age 0.11 -> i18n-embed-fl 0.9.4 / i18n-embed-impl 0.8.4 (proc-macros) -> i18n-config 0.4.8 -> basic-toml 0.1.10 -# Cannot fix in-major: i18n-config 0.4.8 is the latest 0.4.x and still depends on basic-toml. -# Reachability: compile-time only (read by proc-macros while building age); not in the sindri binary's runtime paths. -# Exit: remove once an age 0.11.x / i18n-config 0.4.x release drops basic-toml. -RUSTSEC-2026-0374 - -# RUSTSEC-2026-0173: proc-macro-error2 is unmaintained (no vulnerability, no patched release) -# Path: tabled 0.20 -> tabled_derive 0.11.0, and age 0.11 -> i18n-embed-fl 0.9.4 (both proc-macros) -# Cannot fix in-major: tabled_derive 0.11.0 and i18n-embed-fl 0.9.4 are the latest in their 0.x minors. -# Reachability: compile-time only (proc-macro error reporting); nothing of it ships in the binary. -# Exit: remove once tabled 0.20.x and i18n-embed-fl 0.9.x drop it, or when this line moves to tabled 0.21+. -RUSTSEC-2026-0173 - -# RUSTSEC-2024-0449: unsafe-libyaml is unmaintained (no vulnerability, no patched release) -# Path: serde_yaml_ng 0.10 -> unsafe-libyaml 0.2.11 (sindri-core, sindri, sindri-clusters, ...) -# Cannot fix in-major: every serde_yaml_ng 0.10.x uses unsafe-libyaml; replacing the YAML crate is an API change. -# Reachability: used at runtime to parse sindri.yaml and extension YAML (local, user-supplied files). -# Exit: remove when the v3 line replaces serde_yaml_ng, or a serde_yaml_ng 0.10.x drops unsafe-libyaml. -RUSTSEC-2024-0449