diff --git a/.github/renovate.json b/.github/renovate.json index 23aaa0f40..92f8c882b 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -64,6 +64,19 @@ "datasourceTemplate": "go", "versioningTemplate": "semver" }, + { + "customType": "regex", + "description": "Track Go major-version module patches in Dockerfile via github-tags (workaround: Renovate go datasource cannot resolve /vN paths from custom managers)", + "managerFilePatterns": [ + "/^Dockerfile$/" + ], + "matchStrings": [ + "#\\s*renovate:\\s*datasource=github-tags\\s+depName=(?[^\\s]+)\\s*\\n\\s*go get [^@]+@v(?[^\\s|]+)" + ], + "datasourceTemplate": "github-tags", + "versioningTemplate": "semver", + "extractVersionTemplate": "^v(?.+)$" + }, { "customType": "regex", "description": "Track Alpine base image digest in Dockerfile for security updates", @@ -242,6 +255,20 @@ "depNameTemplate": "golang/go", "datasourceTemplate": "golang-version", "versioningTemplate": "semver" + }, + { + "customType": "regex", + "description": "Track golangci-lint version in quality checks workflow", + "managerFilePatterns": [ + "/^\\.github/workflows/quality-checks\\.yml$/" + ], + "matchStrings": [ + "# renovate: datasource=github-releases depName=golangci/golangci-lint\\n\\s+version: v(?[^\\s]+)" + ], + "depNameTemplate": "golangci/golangci-lint", + "datasourceTemplate": "github-releases", + "versioningTemplate": "semver", + "extractVersionTemplate": "^v(?.*)" } ], @@ -289,22 +316,31 @@ "allowedVersions": "<3.0.0" }, { - "description": "Go: keep pgx within v4 (CrowdSec requires pgx/v4 module path)", + "description": "Go: keep pgx within v4 (CrowdSec requires pgx/v4 module path) - applies to go.mod lookups", "matchDatasources": ["go"], "matchPackageNames": ["github.com/jackc/pgx/v4"], - "allowedVersions": "<5.0.0" + "allowedVersions": "<5.0.0", + "sourceUrl": "https://github.com/jackc/pgx" + }, + { + "description": "jackc/pgx via github-tags: constrain to v4.x.x patch releases (Dockerfile CVE pin)", + "matchDatasources": ["github-tags"], + "matchPackageNames": ["jackc/pgx"], + "allowedVersions": ">=4.0.0 <5.0.0" }, { "description": "Go: keep go-jose/v3 within v3 (v4 is a different Go module path)", "matchDatasources": ["go"], "matchPackageNames": ["github.com/go-jose/go-jose/v3"], - "allowedVersions": "<4.0.0" + "allowedVersions": "<4.0.0", + "sourceUrl": "https://github.com/go-jose/go-jose" }, { "description": "Go: keep go-jose/v4 within v4 (v5 would be a different Go module path)", "matchDatasources": ["go"], "matchPackageNames": ["github.com/go-jose/go-jose/v4"], - "allowedVersions": "<5.0.0" + "allowedVersions": "<5.0.0", + "sourceUrl": "https://github.com/go-jose/go-jose" }, { "description": "Safety: Keep MAJOR updates separate and require manual review", @@ -323,6 +359,60 @@ "matchDatasources": ["go"], "matchPackageNames": ["github.com/google/uuid"], "sourceUrl": "https://github.com/google/uuid" + }, + { + "description": "Fix Renovate lookup for golang-jwt/jwt v5 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/golang-jwt/jwt/v5"], + "sourceUrl": "https://github.com/golang-jwt/jwt" + }, + { + "description": "Fix Renovate lookup for robfig/cron v3 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/robfig/cron/v3"], + "sourceUrl": "https://github.com/robfig/cron" + }, + { + "description": "Fix Renovate lookup for oschwald/maxminddb-golang v2 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/oschwald/maxminddb-golang/v2"], + "sourceUrl": "https://github.com/oschwald/maxminddb-golang" + }, + { + "description": "Fix Renovate lookup for cespare/xxhash v2 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/cespare/xxhash/v2"], + "sourceUrl": "https://github.com/cespare/xxhash" + }, + { + "description": "Fix Renovate lookup for klauspost/cpuid v2 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/klauspost/cpuid/v2"], + "sourceUrl": "https://github.com/klauspost/cpuid" + }, + { + "description": "Fix Renovate lookup for pelletier/go-toml v2 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/pelletier/go-toml/v2"], + "sourceUrl": "https://github.com/pelletier/go-toml" + }, + { + "description": "Fix Renovate lookup for go-playground/validator v10 module path", + "matchDatasources": ["go"], + "matchPackageNames": ["github.com/go-playground/validator/v10"], + "sourceUrl": "https://github.com/go-playground/validator" + }, + { + "description": "Fix Renovate lookup for gorm.io/gorm (vanity domain maps to go-gorm/gorm)", + "matchDatasources": ["go"], + "matchPackageNames": ["gorm.io/gorm"], + "sourceUrl": "https://github.com/go-gorm/gorm" + }, + { + "description": "Fix Renovate lookup for gorm.io/driver/sqlite (vanity domain maps to go-gorm/sqlite)", + "matchDatasources": ["go"], + "matchPackageNames": ["gorm.io/driver/sqlite"], + "sourceUrl": "https://github.com/go-gorm/sqlite" } ] } diff --git a/.github/skills/examples/gorm-scanner-ci-workflow.yml b/.github/skills/examples/gorm-scanner-ci-workflow.yml index 3144c1cd4..d5045d725 100644 --- a/.github/skills/examples/gorm-scanner-ci-workflow.yml +++ b/.github/skills/examples/gorm-scanner-ci-workflow.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: - go-version: "1.26.2" + go-version: "1.26.3" - name: Run GORM Security Scanner id: gorm-scan diff --git a/.github/skills/security-scan-docker-image-scripts/run.sh b/.github/skills/security-scan-docker-image-scripts/run.sh index e678af795..4ec8eae4a 100755 --- a/.github/skills/security-scan-docker-image-scripts/run.sh +++ b/.github/skills/security-scan-docker-image-scripts/run.sh @@ -35,7 +35,7 @@ fi # Check Grype if ! command -v grype >/dev/null 2>&1; then log_error "Grype not found - install from: https://github.com/anchore/grype" - log_error "Installation: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.111.1" + log_error "Installation: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.112.0" error_exit "Grype is required for vulnerability scanning" 2 fi @@ -50,8 +50,8 @@ SYFT_INSTALLED_VERSION=$(syft version | grep -oP 'Version:\s*\Kv?[0-9]+\.[0-9]+\ GRYPE_INSTALLED_VERSION=$(grype version | grep -oP 'Version:\s*\Kv?[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown") # Set defaults matching CI workflow -set_default_env "SYFT_VERSION" "v1.43.0" -set_default_env "GRYPE_VERSION" "v0.111.1" +set_default_env "SYFT_VERSION" "v1.44.0" +set_default_env "GRYPE_VERSION" "v0.112.0" set_default_env "IMAGE_TAG" "charon:local" set_default_env "FAIL_ON_SEVERITY" "Critical,High" diff --git a/.github/workflows/auto-add-to-project.yml b/.github/workflows/auto-add-to-project.yml index fa74c1464..e53d6be07 100644 --- a/.github/workflows/auto-add-to-project.yml +++ b/.github/workflows/auto-add-to-project.yml @@ -26,7 +26,7 @@ jobs: - name: Add issue or PR to project if: steps.project_check.outputs.has_project == 'true' - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 + uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0 continue-on-error: true with: project-url: ${{ secrets.PROJECT_URL }} diff --git a/.github/workflows/auto-changelog.yml b/.github/workflows/auto-changelog.yml index e9bf4756f..cd3fc914f 100644 --- a/.github/workflows/auto-changelog.yml +++ b/.github/workflows/auto-changelog.yml @@ -24,6 +24,6 @@ jobs: with: ref: ${{ github.event.workflow_run.head_sha || github.sha }} - name: Draft Release - uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7 + uses: release-drafter/release-drafter@c2e2804cc59f45f57076a99af580d0fedb697927 # v7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index aca4b0ca0..8619215b2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -12,7 +12,7 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' GOTOOLCHAIN: auto # Minimal permissions at workflow level; write permissions granted at job level for push only @@ -52,7 +52,7 @@ jobs: # This avoids gh-pages branch errors and permission issues on fork PRs if: github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' # Security: Pinned to full SHA for supply chain security - uses: benchmark-action/github-action-benchmark@a60cea5bc7b49e15c1f58f411161f99e0df48372 # v1.22.0 + uses: benchmark-action/github-action-benchmark@52576c92bccf6ac60c8223ec7eb2565637cae9ba # v1.22.1 with: name: Go Benchmark tool: 'go' diff --git a/.github/workflows/codecov-upload.yml b/.github/workflows/codecov-upload.yml index 1f3034e0e..8fced573f 100644 --- a/.github/workflows/codecov-upload.yml +++ b/.github/workflows/codecov-upload.yml @@ -23,7 +23,7 @@ concurrency: cancel-in-progress: true env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' NODE_VERSION: '24.12.0' GOTOOLCHAIN: auto diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 57e446519..003ef2112 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,7 +4,7 @@ on: pull_request: branches: [main, nightly, development] push: - branches: [main] + branches: [main, nightly, development] workflow_dispatch: schedule: - cron: '0 3 * * 1' # Mondays 03:00 UTC @@ -15,7 +15,7 @@ concurrency: env: GOTOOLCHAIN: auto - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' permissions: contents: read @@ -52,7 +52,7 @@ jobs: run: bash scripts/ci/check-codeql-parity.sh - name: Initialize CodeQL - uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 + uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4 with: languages: ${{ matrix.language }} queries: security-and-quality @@ -92,16 +92,17 @@ jobs: run: mkdir -p sarif-results - name: Autobuild - uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 + uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 + id: codeql_analyze + uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4 with: category: "/language:${{ matrix.language }}" output: sarif-results/${{ matrix.language }} - name: Check CodeQL Results - if: always() + if: always() && steps.codeql_analyze.conclusion != 'skipped' run: | set -euo pipefail SARIF_DIR="sarif-results/${{ matrix.language }}" @@ -194,7 +195,7 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" - name: Fail on High-Severity Findings - if: always() + if: always() && steps.codeql_analyze.conclusion != 'skipped' run: | set -euo pipefail SARIF_DIR="sarif-results/${{ matrix.language }}" diff --git a/.github/workflows/container-prune.yml b/.github/workflows/container-prune.yml index 8c2a9d0cf..ba48bb1ed 100644 --- a/.github/workflows/container-prune.yml +++ b/.github/workflows/container-prune.yml @@ -25,9 +25,13 @@ permissions: jobs: prune-ghcr: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: [charon, orthrus] env: OWNER: ${{ github.repository_owner }} - IMAGE_NAME: charon + IMAGE_NAME: ${{ matrix.image }} KEEP_DAYS: ${{ github.event.inputs.keep_days || '30' }} KEEP_LAST_N: ${{ github.event.inputs.keep_last_n || '30' }} DRY_RUN: ${{ github.event_name == 'pull_request' && 'true' || github.event.inputs.dry_run || 'false' }} @@ -47,14 +51,21 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | chmod +x scripts/prune-ghcr.sh - ./scripts/prune-ghcr.sh 2>&1 | tee prune-ghcr-${{ github.run_id }}.log + ./scripts/prune-ghcr.sh 2>&1 | tee prune-ghcr-${{ matrix.image }}-${{ github.run_id }}.log + + - name: Namespace summary file + if: always() + run: | + if [ -f prune-summary-ghcr.env ]; then + mv prune-summary-ghcr.env prune-summary-ghcr-${{ matrix.image }}.env + fi - name: Summarize GHCR results if: always() run: | set -euo pipefail - SUMMARY_FILE=prune-summary-ghcr.env - LOG_FILE=prune-ghcr-${{ github.run_id }}.log + SUMMARY_FILE=prune-summary-ghcr-${{ matrix.image }}.env + LOG_FILE=prune-ghcr-${{ matrix.image }}-${{ github.run_id }}.log human() { local bytes=${1:-0} @@ -72,7 +83,7 @@ jobs: TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0) { - echo "## GHCR prune summary" + echo "## GHCR prune summary — ${{ matrix.image }}" echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))" echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))" } >> "$GITHUB_STEP_SUMMARY" @@ -81,7 +92,7 @@ jobs: deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true) { - echo "## GHCR prune summary" + echo "## GHCR prune summary — ${{ matrix.image }}" echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))" } >> "$GITHUB_STEP_SUMMARY" fi @@ -90,16 +101,20 @@ jobs: if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: prune-ghcr-log-${{ github.run_id }} + name: prune-ghcr-${{ matrix.image }}-log-${{ github.run_id }} path: | - prune-ghcr-${{ github.run_id }}.log - prune-summary-ghcr.env + prune-ghcr-${{ matrix.image }}-${{ github.run_id }}.log + prune-summary-ghcr-${{ matrix.image }}.env prune-dockerhub: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: [charon, orthrus] env: OWNER: ${{ github.repository_owner }} - IMAGE_NAME: charon + IMAGE_NAME: ${{ matrix.image }} KEEP_DAYS: ${{ github.event.inputs.keep_days || '30' }} KEEP_LAST_N: ${{ github.event.inputs.keep_last_n || '30' }} DRY_RUN: ${{ github.event_name == 'pull_request' && 'true' || github.event.inputs.dry_run || 'false' }} @@ -118,14 +133,21 @@ jobs: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | chmod +x scripts/prune-dockerhub.sh - ./scripts/prune-dockerhub.sh 2>&1 | tee prune-dockerhub-${{ github.run_id }}.log + ./scripts/prune-dockerhub.sh 2>&1 | tee prune-dockerhub-${{ matrix.image }}-${{ github.run_id }}.log + + - name: Namespace summary file + if: always() + run: | + if [ -f prune-summary-dockerhub.env ]; then + mv prune-summary-dockerhub.env prune-summary-dockerhub-${{ matrix.image }}.env + fi - name: Summarize Docker Hub results if: always() run: | set -euo pipefail - SUMMARY_FILE=prune-summary-dockerhub.env - LOG_FILE=prune-dockerhub-${{ github.run_id }}.log + SUMMARY_FILE=prune-summary-dockerhub-${{ matrix.image }}.env + LOG_FILE=prune-dockerhub-${{ matrix.image }}-${{ github.run_id }}.log human() { local bytes=${1:-0} @@ -143,7 +165,7 @@ jobs: TOTAL_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' "$SUMMARY_FILE" | cut -d= -f2 || echo 0) { - echo "## Docker Hub prune summary" + echo "## Docker Hub prune summary — ${{ matrix.image }}" echo "- candidates: ${TOTAL_CANDIDATES} (≈ $(human "${TOTAL_CANDIDATES_BYTES}"))" echo "- deleted: ${TOTAL_DELETED} (≈ $(human "${TOTAL_DELETED_BYTES}"))" } >> "$GITHUB_STEP_SUMMARY" @@ -152,7 +174,7 @@ jobs: deleted_count=$(grep -cE 'deleting |DRY RUN: would delete' "$LOG_FILE" || true) { - echo "## Docker Hub prune summary" + echo "## Docker Hub prune summary — ${{ matrix.image }}" echo "- deleted (approx): ${deleted_count} (≈ $(human "${deleted_bytes}"))" } >> "$GITHUB_STEP_SUMMARY" fi @@ -161,10 +183,10 @@ jobs: if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: prune-dockerhub-log-${{ github.run_id }} + name: prune-dockerhub-${{ matrix.image }}-log-${{ github.run_id }} path: | - prune-dockerhub-${{ github.run_id }}.log - prune-summary-dockerhub.env + prune-dockerhub-${{ matrix.image }}-${{ github.run_id }}.log + prune-summary-dockerhub-${{ matrix.image }}.env summarize: runs-on: ubuntu-latest @@ -190,35 +212,51 @@ jobs: awk -v b="$bytes" 'BEGIN { split("B KiB MiB GiB TiB",u," "); i=0; x=b; while(x>1024){x/=1024;i++} printf "%0.2f %s", x, u[i+1] }' } - GHCR_CANDIDATES=0 GHCR_CANDIDATES_BYTES=0 GHCR_DELETED=0 GHCR_DELETED_BYTES=0 - if [ -f prune-summary-ghcr.env ]; then - GHCR_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0) - GHCR_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0) - GHCR_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-ghcr.env | cut -d= -f2 || echo 0) - GHCR_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-ghcr.env | cut -d= -f2 || echo 0) - fi - - HUB_CANDIDATES=0 HUB_CANDIDATES_BYTES=0 HUB_DELETED=0 HUB_DELETED_BYTES=0 - if [ -f prune-summary-dockerhub.env ]; then - HUB_CANDIDATES=$(grep -E '^TOTAL_CANDIDATES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0) - HUB_CANDIDATES_BYTES=$(grep -E '^TOTAL_CANDIDATES_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0) - HUB_DELETED=$(grep -E '^TOTAL_DELETED=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0) - HUB_DELETED_BYTES=$(grep -E '^TOTAL_DELETED_BYTES=' prune-summary-dockerhub.env | cut -d= -f2 || echo 0) - fi + read_field() { + local file="$1" field="$2" + if [ -f "$file" ]; then + grep -E "^${field}=" "$file" | cut -d= -f2 | head -n1 + else + echo 0 + fi + } - TOTAL_CANDIDATES=$((GHCR_CANDIDATES + HUB_CANDIDATES)) - TOTAL_CANDIDATES_BYTES=$((GHCR_CANDIDATES_BYTES + HUB_CANDIDATES_BYTES)) - TOTAL_DELETED=$((GHCR_DELETED + HUB_DELETED)) - TOTAL_DELETED_BYTES=$((GHCR_DELETED_BYTES + HUB_DELETED_BYTES)) + TOTAL_CANDIDATES=0 + TOTAL_CANDIDATES_BYTES=0 + TOTAL_DELETED=0 + TOTAL_DELETED_BYTES=0 { echo "## Combined container prune summary" echo "" - echo "| Registry | Candidates | Deleted | Space Reclaimed |" - echo "|----------|------------|---------|-----------------|" - echo "| GHCR | ${GHCR_CANDIDATES} | ${GHCR_DELETED} | $(human "${GHCR_DELETED_BYTES}") |" - echo "| Docker Hub | ${HUB_CANDIDATES} | ${HUB_DELETED} | $(human "${HUB_DELETED_BYTES}") |" - echo "| **Total** | **${TOTAL_CANDIDATES}** | **${TOTAL_DELETED}** | **$(human "${TOTAL_DELETED_BYTES}")** |" + echo "| Registry | Image | Candidates | Deleted | Space Reclaimed |" + echo "|----------|-------|------------|---------|-----------------|" + } >> "$GITHUB_STEP_SUMMARY" + + for registry in ghcr dockerhub; do + for image in charon orthrus; do + file="prune-summary-${registry}-${image}.env" + cands=$(read_field "$file" TOTAL_CANDIDATES) + cands_b=$(read_field "$file" TOTAL_CANDIDATES_BYTES) + dels=$(read_field "$file" TOTAL_DELETED) + dels_b=$(read_field "$file" TOTAL_DELETED_BYTES) + + cands=${cands:-0}; cands_b=${cands_b:-0}; dels=${dels:-0}; dels_b=${dels_b:-0} + + TOTAL_CANDIDATES=$((TOTAL_CANDIDATES + cands)) + TOTAL_CANDIDATES_BYTES=$((TOTAL_CANDIDATES_BYTES + cands_b)) + TOTAL_DELETED=$((TOTAL_DELETED + dels)) + TOTAL_DELETED_BYTES=$((TOTAL_DELETED_BYTES + dels_b)) + + registry_label="GHCR" + [ "$registry" = "dockerhub" ] && registry_label="Docker Hub" + + echo "| ${registry_label} | ${image} | ${cands} | ${dels} | $(human "${dels_b}") |" >> "$GITHUB_STEP_SUMMARY" + done + done + + { + echo "| **Total** | — | **${TOTAL_CANDIDATES}** | **${TOTAL_DELETED}** | **$(human "${TOTAL_DELETED_BYTES}")** |" } >> "$GITHUB_STEP_SUMMARY" printf 'PRUNE_SUMMARY: candidates=%s candidates_bytes=%s deleted=%s deleted_bytes=%s\n' \ diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index f3e967dc3..da7e2b75d 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -47,6 +47,7 @@ env: TRIGGER_HEAD_REF: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.head_ref }} TRIGGER_PR_NUMBER: ${{ github.event_name == 'workflow_run' && join(github.event.workflow_run.pull_requests.*.number, '') || format('{0}', github.event.pull_request.number) }} TRIGGER_ACTOR: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.actor.login || github.actor }} + TRIGGER_BASE_REF: ${{ github.event_name == 'workflow_run' && '' || github.base_ref }} jobs: build-and-push: @@ -215,6 +216,7 @@ jobs: type=raw,value=latest,enable=${{ env.TRIGGER_REF == 'refs/heads/main' }} type=raw,value=dev,enable=${{ env.TRIGGER_REF == 'refs/heads/development' }} type=raw,value=nightly,enable=${{ env.TRIGGER_REF == 'refs/heads/nightly' }} + type=raw,value=beta,enable=${{ env.TRIGGER_EVENT == 'pull_request' && env.TRIGGER_BASE_REF == 'development' }} type=raw,value=${{ steps.branch-tags.outputs.pr_feature_branch_sha_tag }},enable=${{ env.TRIGGER_EVENT == 'pull_request' && steps.branch-tags.outputs.pr_feature_branch_sha_tag != '' }} type=raw,value=${{ steps.branch-tags.outputs.feature_branch_tag }},enable=${{ env.TRIGGER_EVENT != 'pull_request' && startsWith(env.TRIGGER_REF, 'refs/heads/feature/') && steps.branch-tags.outputs.feature_branch_tag != '' }} type=raw,value=${{ steps.branch-tags.outputs.branch_sha_tag }},enable=${{ env.TRIGGER_EVENT != 'pull_request' && steps.branch-tags.outputs.branch_sha_tag != '' }} @@ -568,7 +570,7 @@ jobs: - name: Upload Trivy results if: env.TRIGGER_EVENT != 'pull_request' && steps.skip.outputs.skip_build != 'true' && steps.trivy-check.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-results.sarif' category: '.github/workflows/docker-build.yml:build-and-push' @@ -583,6 +585,7 @@ jobs: image: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }} format: cyclonedx-json output-file: sbom.cyclonedx.json + syft-version: v1.44.0 # Create verifiable attestation for the SBOM - name: Attest SBOM @@ -597,7 +600,7 @@ jobs: # Install Cosign for keyless signing - name: Install Cosign if: env.TRIGGER_EVENT != 'pull_request' && steps.skip.outputs.skip_build != 'true' && steps.skip.outputs.is_feature_push != 'true' - uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 # Sign GHCR image with keyless signing (Sigstore/Fulcio) - name: Sign GHCR Image @@ -727,14 +730,14 @@ jobs: - name: Upload Trivy scan results if: always() && steps.trivy-pr-check.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-pr-results.sarif' category: 'docker-pr-image' - name: Upload Trivy compatibility results (docker-build category) if: always() && steps.trivy-pr-check.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-pr-results.sarif' category: '.github/workflows/docker-build.yml:build-and-push' @@ -742,7 +745,7 @@ jobs: - name: Upload Trivy compatibility results (docker-publish alias) if: always() && steps.trivy-pr-check.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-pr-results.sarif' category: '.github/workflows/docker-publish.yml:build-and-push' @@ -750,7 +753,7 @@ jobs: - name: Upload Trivy compatibility results (nightly alias) if: always() && steps.trivy-pr-check.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-pr-results.sarif' category: 'trivy-nightly' diff --git a/.github/workflows/e2e-tests-split.yml b/.github/workflows/e2e-tests-split.yml index 94a5abf2b..278604a28 100644 --- a/.github/workflows/e2e-tests-split.yml +++ b/.github/workflows/e2e-tests-split.yml @@ -83,7 +83,7 @@ on: env: NODE_VERSION: '20' - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' GOTOOLCHAIN: auto DOCKERHUB_REGISTRY: docker.io IMAGE_NAME: ${{ github.repository_owner }}/charon diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index 5b5219208..2d19b7d29 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -15,7 +15,7 @@ on: default: "false" env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' NODE_VERSION: '24.12.0' GOTOOLCHAIN: auto GHCR_REGISTRY: ghcr.io @@ -271,7 +271,7 @@ jobs: image: ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.resolve_digest.outputs.digest }} format: cyclonedx-json output-file: sbom-nightly.json - syft-version: v1.42.1 + syft-version: v1.44.0 - name: Generate SBOM fallback with pinned Syft if: always() @@ -285,7 +285,7 @@ jobs: echo "Primary SBOM generation failed or produced missing/invalid output; using deterministic Syft fallback" - SYFT_VERSION="v1.43.0" + SYFT_VERSION="v1.44.0" OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH="$(uname -m)" case "$ARCH" in @@ -336,7 +336,7 @@ jobs: # Install Cosign for keyless signing - name: Install Cosign - uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1 + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 # Sign GHCR image with keyless signing (Sigstore/Fulcio) - name: Sign GHCR Image @@ -468,7 +468,7 @@ jobs: trivyignores: '.trivyignore' - name: Upload Trivy results - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-nightly.sarif' category: 'trivy-nightly' diff --git a/.github/workflows/orthrus-build.yml b/.github/workflows/orthrus-build.yml new file mode 100644 index 000000000..d6045785f --- /dev/null +++ b/.github/workflows/orthrus-build.yml @@ -0,0 +1,181 @@ +name: Orthrus Agent — Build & Publish + +# Mirrors the trigger and tagging strategy from docker-build.yml so that the +# Orthrus agent image always receives the same identifiable tags as the main +# Charon image (e.g. feature-hecate-abc1234), enabling coordinated manual +# testing on remote servers. + +"on": + pull_request: + paths: + - 'agent/**' + - '.github/workflows/orthrus-build.yml' + push: + branches: [main, development] + tags: ['v*'] + paths: + - 'agent/**' + - '.github/workflows/orthrus-build.yml' + workflow_dispatch: + inputs: + reason: + description: 'Why are you running this manually?' + required: false + default: 'manual trigger' + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +env: + GHCR_REGISTRY: ghcr.io + DOCKERHUB_REGISTRY: docker.io + IMAGE_NAME: wikid82/orthrus + GO_VERSION: '1.26.3' + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + env: + HAS_DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN != '' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Normalize image name + run: | + IMAGE_NAME=$(echo "${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]') + echo "IMAGE_NAME=${IMAGE_NAME}" >> "$GITHUB_ENV" + + # Reuse the exact same tag-computation logic as docker-build.yml so that + # feature branches (feature/hecate → feature-hecate-abc1234) and PRs + # (pr-983-abc1234) get matching, pull-able tags on both images. + - name: Compute branch tags + id: branch-tags + run: | + if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + BRANCH_NAME="$GITHUB_HEAD_REF" + else + BRANCH_NAME="$GITHUB_REF_NAME" + fi + SHORT_SHA="$(echo "$GITHUB_SHA" | cut -c1-7)" + + sanitize_tag() { + local raw="$1" + local max_len="$2" + local sanitized + sanitized=$(echo "$raw" | tr '[:upper:]' '[:lower:]') + sanitized=${sanitized//[^a-z0-9-]/-} + while [[ "$sanitized" == *"--"* ]]; do + sanitized=${sanitized//--/-} + done + sanitized=${sanitized##[^a-z0-9]*} + sanitized=${sanitized%%[^a-z0-9-]*} + if [ -z "$sanitized" ]; then sanitized="branch"; fi + sanitized=$(echo "$sanitized" | cut -c1-"$max_len") + sanitized=${sanitized##[^a-z0-9]*} + if [ -z "$sanitized" ]; then sanitized="branch"; fi + echo "$sanitized" + } + + SANITIZED_BRANCH=$(sanitize_tag "${BRANCH_NAME}" 128) + BASE_BRANCH=$(sanitize_tag "${BRANCH_NAME}" 120) + BRANCH_SHA_TAG="${BASE_BRANCH}-${SHORT_SHA}" + + if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then + if [[ "$BRANCH_NAME" == feature/* ]]; then + echo "pr_feature_branch_sha_tag=${BRANCH_SHA_TAG}" >> "$GITHUB_OUTPUT" + fi + else + echo "branch_sha_tag=${BRANCH_SHA_TAG}" >> "$GITHUB_OUTPUT" + if [[ "$GITHUB_REF" == refs/heads/feature/* ]]; then + echo "feature_branch_tag=${SANITIZED_BRANCH}" >> "$GITHUB_OUTPUT" + echo "feature_branch_sha_tag=${BRANCH_SHA_TAG}" >> "$GITHUB_OUTPUT" + fi + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + + - name: Log in to Docker Hub + if: env.HAS_DOCKERHUB_TOKEN == 'true' + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 + with: + registry: ${{ env.GHCR_REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate Docker metadata + id: meta + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0 + with: + images: | + ${{ env.DOCKERHUB_REGISTRY }}/${{ env.IMAGE_NAME }} + ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=raw,value=dev,enable=${{ github.ref == 'refs/heads/development' }} + type=raw,value=beta,enable=${{ github.event_name == 'pull_request' && github.base_ref == 'development' }} + type=raw,value=${{ steps.branch-tags.outputs.pr_feature_branch_sha_tag }},enable=${{ github.event_name == 'pull_request' && steps.branch-tags.outputs.pr_feature_branch_sha_tag != '' }} + type=raw,value=${{ steps.branch-tags.outputs.feature_branch_tag }},enable=${{ github.event_name != 'pull_request' && startsWith(github.ref, 'refs/heads/feature/') && steps.branch-tags.outputs.feature_branch_tag != '' }} + type=raw,value=${{ steps.branch-tags.outputs.branch_sha_tag }},enable=${{ github.event_name != 'pull_request' && steps.branch-tags.outputs.branch_sha_tag != '' }} + type=raw,value=pr-${{ github.event.pull_request.number }}-{{sha}},enable=${{ github.event_name == 'pull_request' }},prefix=,suffix= + flavor: | + latest=false + labels: | + org.opencontainers.image.title=Orthrus Agent + org.opencontainers.image.description=Lightweight reverse-proxy agent for Charon remote server connectivity + org.opencontainers.image.vendor=Wikid82 + org.opencontainers.image.revision=${{ github.sha }} + io.charon.component=orthrus-agent + io.charon.pr.number=${{ github.event.pull_request.number }} + + - name: Build and push Orthrus agent image + uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + with: + context: ./agent + file: ./agent/Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ steps.meta.outputs.version }} + GIT_COMMIT=${{ github.sha }} + BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Print pull instructions + run: | + echo "" + echo "✅ Orthrus agent image published. To test on your remote server:" + echo "" + echo " # Pull the image:" + FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) + echo " docker pull ${FIRST_TAG}" + echo "" + echo " # Or use the short SHA tag:" + echo " docker pull ${{ env.GHCR_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.branch-tags.outputs.branch_sha_tag || steps.branch-tags.outputs.pr_feature_branch_sha_tag }}" + echo "" + echo "All published tags:" + echo "${{ steps.meta.outputs.tags }}" diff --git a/.github/workflows/propagate-changes.yml b/.github/workflows/propagate-changes.yml index 0986616e4..4ba1aac21 100644 --- a/.github/workflows/propagate-changes.yml +++ b/.github/workflows/propagate-changes.yml @@ -183,27 +183,5 @@ jobs: core.info('Push originated from development (excluded). Skipping propagation back to development.'); } } else if (currentBranch === 'development') { - // Development -> Feature/Hotfix branches (The Pittsburgh Model) - // We propagate changes from dev DOWN to features/hotfixes so they stay up to date. - - const branches = await github.paginate(github.rest.repos.listBranches, { - owner: context.repo.owner, - repo: context.repo.repo, - }); - - // Filter for feature/* and hotfix/* branches using regex - // AND exclude the branch that just got merged in (if any) - const targetBranches = branches - .map(b => b.name) - .filter(name => { - const isTargetType = /^feature\/|^hotfix\//.test(name); - const isExcluded = (name === excludedBranch); - return isTargetType && !isExcluded; - }); - - core.info(`Found ${targetBranches.length} target branches (excluding '${excludedBranch || 'none'}'): ${targetBranches.join(', ')}`); - - for (const targetBranch of targetBranches) { - await createPR('development', targetBranch); - } + core.info('Push to development detected. No downstream propagation configured.'); } diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index a7b255d7a..3093ac90a 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -16,7 +16,7 @@ permissions: checks: write env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' NODE_VERSION: '24.12.0' GOTOOLCHAIN: auto @@ -193,7 +193,8 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: - version: latest + # renovate: datasource=github-releases depName=golangci/golangci-lint + version: v2.12.2 working-directory: backend args: --timeout=5m continue-on-error: true diff --git a/.github/workflows/release-goreleaser.yml b/.github/workflows/release-goreleaser.yml index 93f8489ce..f2017ef8f 100644 --- a/.github/workflows/release-goreleaser.yml +++ b/.github/workflows/release-goreleaser.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: false env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' NODE_VERSION: '24.12.0' GOTOOLCHAIN: auto diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index f91adf860..e9a721caf 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -15,7 +15,7 @@ permissions: issues: write env: - GO_VERSION: '1.26.2' + GO_VERSION: '1.26.3' jobs: renovate: @@ -33,7 +33,7 @@ jobs: go-version: ${{ env.GO_VERSION }} - name: Run Renovate - uses: renovatebot/github-action@83ec54fee49ab67d9cd201084c1ff325b4b462e4 # v46.1.10 + uses: renovatebot/github-action@693b9ef15eec82123529a37c782242f091365961 # v46.1.14 with: configurationFile: .github/renovate.json token: ${{ secrets.RENOVATE_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/security-pr.yml b/.github/workflows/security-pr.yml index 5f1491385..cb4888d7f 100644 --- a/.github/workflows/security-pr.yml +++ b/.github/workflows/security-pr.yml @@ -226,11 +226,12 @@ jobs: ARTIFACT_ID=$(printf '%s' "${ARTIFACTS_JSON}" | jq -r --arg name "${ARTIFACT_NAME}" '.artifacts[] | select(.name == $name) | .id' | head -n 1) if [[ -z "${ARTIFACT_ID}" ]]; then - echo "❌ reason_category=not_found" - echo "reason=Required artifact was not found" + echo "⚠️ reason_category=not_found" + echo "reason=Artifact not found — build was likely skipped (e.g., renovate/chore PR)" echo "upstream_run_id=${RUN_ID}" echo "artifact_name=${ARTIFACT_NAME}" - exit 1 + echo "artifact_exists=false" >> "$GITHUB_OUTPUT" + exit 0 fi { @@ -241,7 +242,7 @@ jobs: echo "✅ Found artifact: ${ARTIFACT_NAME} (ID: ${ARTIFACT_ID})" - name: Download PR image artifact - if: github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' + if: (github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch') && steps.check-artifact.outputs.artifact_exists == 'true' # actions/download-artifact v4.1.8 uses: actions/download-artifact@484a0b528fb4d7bd804637ccb632e47a0e638317 with: @@ -250,7 +251,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Load Docker image - if: github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch' + if: (github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch') && steps.check-artifact.outputs.artifact_exists == 'true' id: load-image run: | echo "📦 Loading Docker image..." @@ -364,14 +365,17 @@ jobs: - name: Run Trivy filesystem scan (SARIF output) if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' - # aquasecurity/trivy-action 0.35.0 - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 + # aquasecurity/trivy-action 0.36.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 with: scan-type: 'fs' scan-ref: ${{ steps.extract.outputs.binary_path }} format: 'sarif' output: 'trivy-binary-results.sarif' severity: 'CRITICAL,HIGH,MEDIUM' + version: 'v0.70.0' + trivyignores: '.trivyignore' + config: 'trivy.yaml' continue-on-error: true - name: Check Trivy SARIF output exists @@ -396,14 +400,17 @@ jobs: - name: Run Trivy filesystem scan (fail on CRITICAL/HIGH) if: steps.check-artifact.outputs.artifact_exists == 'true' || github.event_name == 'push' || github.event_name == 'pull_request' - # aquasecurity/trivy-action 0.35.0 - uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 + # aquasecurity/trivy-action 0.36.0 + uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 with: scan-type: 'fs' scan-ref: ${{ steps.extract.outputs.binary_path }} format: 'table' severity: 'CRITICAL,HIGH' exit-code: '1' + version: 'v0.70.0' + trivyignores: '.trivyignore' + config: 'trivy.yaml' - name: Upload scan artifacts if: always() && steps.trivy-sarif-check.outputs.exists == 'true' diff --git a/.github/workflows/security-weekly-rebuild.yml b/.github/workflows/security-weekly-rebuild.yml index f2f0a95a3..cc515cbae 100644 --- a/.github/workflows/security-weekly-rebuild.yml +++ b/.github/workflows/security-weekly-rebuild.yml @@ -116,7 +116,7 @@ jobs: version: 'v0.70.0' - name: Upload Trivy results to GitHub Security - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 with: sarif_file: 'trivy-weekly-results.sarif' diff --git a/.github/workflows/supply-chain-pr.yml b/.github/workflows/supply-chain-pr.yml index ddcc5a244..49019aa66 100644 --- a/.github/workflows/supply-chain-pr.yml +++ b/.github/workflows/supply-chain-pr.yml @@ -272,6 +272,7 @@ jobs: image: ${{ steps.set-target.outputs.image_name }} format: cyclonedx-json output-file: sbom.cyclonedx.json + syft-version: v1.44.0 - name: Count SBOM components if: steps.set-target.outputs.image_name != '' @@ -285,7 +286,7 @@ jobs: - name: Install Grype if: steps.set-target.outputs.image_name != '' run: | - curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.111.1 + curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin v0.112.0 - name: Scan for vulnerabilities if: steps.set-target.outputs.image_name != '' @@ -362,7 +363,7 @@ jobs: - name: Upload SARIF to GitHub Security if: steps.check-artifact.outputs.artifact_found == 'true' - uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4 + uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4 continue-on-error: true with: sarif_file: grype-results.sarif diff --git a/.github/workflows/supply-chain-verify.yml b/.github/workflows/supply-chain-verify.yml index 402953e10..7bceb0023 100644 --- a/.github/workflows/supply-chain-verify.yml +++ b/.github/workflows/supply-chain-verify.yml @@ -124,6 +124,7 @@ jobs: image: ghcr.io/${{ github.repository_owner }}/charon:${{ steps.tag.outputs.tag }} format: cyclonedx-json output-file: sbom-verify.cyclonedx.json + syft-version: v1.44.0 - name: Verify SBOM Completeness if: steps.image-check.outputs.exists == 'true' diff --git a/.gitignore b/.gitignore index ca32e3b7e..5b3674d79 100644 --- a/.gitignore +++ b/.gitignore @@ -323,3 +323,6 @@ coverage_output.txt frontend/lint_output.txt lefthook_out.txt backend/test_out.txt +backend/cf_coverage.txt +backend/***_coverage.txt +backend/***_cov.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 24d63c476..e6dba72e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **Hecate Tunnel & Pathway Manager**: Connect remote servers behind NAT/firewalls without opening inbound ports, with pluggable connection types managed from the Remote Servers page (Issue #368) + - Connection types: Direct (host/port), Orthrus Agent (self-hosted), Cloudflare Tunnel, Tailscale, ZeroTier, NetBird + - Tunnel lifecycle management: create, start, stop, delete, rotate credentials + - Real-time tunnel log streaming to browser via WebSocket + - Orthrus agent provisioning with one-time auth key display and multi-method install wizard (Docker Compose, systemd, Tarball, Homebrew, Kubernetes) + - `TunnelStatusBadge` component on the Remote Servers page showing live connection state + - Orthrus agent binary published as `ghcr.io/wikid82/charon-orthrus-agent` (~2.4 MB, scratch-based) + - CI workflow for automated Orthrus agent image publishing + - E2E Playwright test coverage for tunnel manager and agent install wizard + - **Accessibility Testing**: Integrated `@axe-core/playwright` for automated WCAG 2.0/2.1/2.2 AA accessibility testing across all application pages (Issue #929) - Added 12 accessibility test specs: login, dashboard, proxy hosts, certificates, DNS providers, settings, security, uptime, tasks, domains, notifications, and setup pages - Tests target WCAG 2.0 Level A/AA and WCAG 2.2 Level AA rule sets via axe-core @@ -80,6 +90,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **CI: Package Deduplication**: Removed 3 duplicate `devDependency` keys in `package.json` for `@typescript-eslint/eslint-plugin`, `@typescript-eslint/parser`, and related packages — duplicate keys caused the last value to silently overwrite earlier entries +- **Frontend: Fast-Refresh Violation**: Extracted `isInUse` and `isDeletable` helper functions from `CertificateList` into a new `certificateUtils` utility module to satisfy React Fast Refresh constraints (non-component exports must not share a file with components) +- **Accessibility: Form Labels**: Replaced 5 invalid `