Sec Audit #55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sec Audit | |
| on: | |
| push: | |
| branches: [dev, main] | |
| paths: | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "src/**" | |
| - "crates/**" | |
| - "deny.toml" | |
| - ".gitleaks.toml" | |
| - ".github/security/gitleaks-allowlist-governance.json" | |
| - ".github/security/deny-ignore-governance.json" | |
| - ".github/security/unsafe-audit-governance.json" | |
| - "scripts/ci/install_gitleaks.sh" | |
| - "scripts/ci/install_syft.sh" | |
| - "scripts/ci/deny_policy_guard.py" | |
| - "scripts/ci/secrets_governance_guard.py" | |
| - "scripts/ci/unsafe_debt_audit.py" | |
| - "scripts/ci/unsafe_policy_guard.py" | |
| - "scripts/ci/config/unsafe_debt_policy.toml" | |
| - "scripts/ci/emit_audit_event.py" | |
| - "scripts/ci/security_regression_tests.sh" | |
| - ".github/workflows/sec-audit.yml" | |
| pull_request: | |
| branches: [dev, main] | |
| paths: | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "src/**" | |
| - "crates/**" | |
| - "deny.toml" | |
| - ".gitleaks.toml" | |
| - ".github/security/gitleaks-allowlist-governance.json" | |
| - ".github/security/deny-ignore-governance.json" | |
| - ".github/security/unsafe-audit-governance.json" | |
| - "scripts/ci/install_gitleaks.sh" | |
| - "scripts/ci/install_syft.sh" | |
| - "scripts/ci/deny_policy_guard.py" | |
| - "scripts/ci/secrets_governance_guard.py" | |
| - "scripts/ci/unsafe_debt_audit.py" | |
| - "scripts/ci/unsafe_policy_guard.py" | |
| - "scripts/ci/config/unsafe_debt_policy.toml" | |
| - "scripts/ci/emit_audit_event.py" | |
| - "scripts/ci/security_regression_tests.sh" | |
| - ".github/workflows/sec-audit.yml" | |
| merge_group: | |
| branches: [dev, main] | |
| schedule: | |
| - cron: "0 6 * * 1" # Weekly on Monday 6am UTC | |
| workflow_dispatch: | |
| inputs: | |
| full_secret_scan: | |
| description: "Scan full git history for secrets" | |
| required: true | |
| default: false | |
| type: boolean | |
| fail_on_secret_leak: | |
| description: "Fail workflow if secret leaks are detected" | |
| required: true | |
| default: true | |
| type: boolean | |
| fail_on_governance_violation: | |
| description: "Fail workflow if secrets governance policy violations are detected" | |
| required: true | |
| default: true | |
| type: boolean | |
| concurrency: | |
| group: security-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| checks: write | |
| env: | |
| GIT_CONFIG_COUNT: "1" | |
| GIT_CONFIG_KEY_0: core.hooksPath | |
| GIT_CONFIG_VALUE_0: /dev/null | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| audit: | |
| name: Security Audit | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable | |
| with: | |
| toolchain: 1.92.0 | |
| - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| deny: | |
| name: License & Supply Chain | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Enforce deny policy hygiene | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| python3 scripts/ci/deny_policy_guard.py \ | |
| --deny-file deny.toml \ | |
| --governance-file .github/security/deny-ignore-governance.json \ | |
| --output-json artifacts/deny-policy-guard.json \ | |
| --output-md artifacts/deny-policy-guard.md \ | |
| --fail-on-violation | |
| - uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2 | |
| with: | |
| command: check advisories licenses sources | |
| - name: Emit deny audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/deny-policy-guard.json ]; then | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type deny_policy_guard \ | |
| --input-json artifacts/deny-policy-guard.json \ | |
| --output-json artifacts/audit-event-deny-policy-guard.json \ | |
| --artifact-name deny-policy-audit-event \ | |
| --retention-days 14 | |
| fi | |
| - name: Upload deny policy artifacts | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: deny-policy-guard | |
| path: artifacts/deny-policy-guard.* | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload deny policy audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: deny-policy-audit-event | |
| path: artifacts/audit-event-deny-policy-guard.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| security-regressions: | |
| name: Security Regression Tests | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable | |
| with: | |
| toolchain: 1.92.0 | |
| - uses: useblacksmith/rust-cache@f53e7f127245d2a269b3d90879ccf259876842d5 # v3 | |
| with: | |
| prefix-key: sec-audit-security-regressions | |
| - name: Run security regression suite | |
| shell: bash | |
| run: ./scripts/ci/security_regression_tests.sh | |
| secrets: | |
| name: Secrets Governance (Gitleaks) | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Enforce gitleaks allowlist governance | |
| shell: bash | |
| env: | |
| FAIL_ON_GOVERNANCE_INPUT: ${{ github.event.inputs.fail_on_governance_violation || 'true' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| fail_on_governance="true" | |
| if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then | |
| fail_on_governance="${FAIL_ON_GOVERNANCE_INPUT}" | |
| fi | |
| cmd=(python3 scripts/ci/secrets_governance_guard.py | |
| --gitleaks-file .gitleaks.toml | |
| --governance-file .github/security/gitleaks-allowlist-governance.json | |
| --output-json artifacts/secrets-governance-guard.json | |
| --output-md artifacts/secrets-governance-guard.md) | |
| if [ "$fail_on_governance" = "true" ]; then | |
| cmd+=(--fail-on-violation) | |
| fi | |
| "${cmd[@]}" | |
| - name: Publish secrets governance summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/secrets-governance-guard.md ]; then | |
| cat artifacts/secrets-governance-guard.md >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "Secrets governance report missing." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Emit secrets governance audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/secrets-governance-guard.json ]; then | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type secrets_governance_guard \ | |
| --input-json artifacts/secrets-governance-guard.json \ | |
| --output-json artifacts/audit-event-secrets-governance-guard.json \ | |
| --artifact-name secrets-governance-audit-event \ | |
| --retention-days 14 | |
| fi | |
| - name: Upload secrets governance artifacts | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: secrets-governance-guard | |
| path: artifacts/secrets-governance-guard.* | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload secrets governance audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: secrets-governance-audit-event | |
| path: artifacts/audit-event-secrets-governance-guard.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Install gitleaks | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "${RUNNER_TEMP}/bin" | |
| ./scripts/ci/install_gitleaks.sh "${RUNNER_TEMP}/bin" | |
| echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" | |
| - name: Run gitleaks scan | |
| shell: bash | |
| env: | |
| FULL_SECRET_SCAN_INPUT: ${{ github.event.inputs.full_secret_scan || 'false' }} | |
| FAIL_ON_SECRET_LEAK_INPUT: ${{ github.event.inputs.fail_on_secret_leak || 'true' }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| log_opts="" | |
| scan_scope="full-history" | |
| fail_on_leak="true" | |
| if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then | |
| log_opts="${{ github.event.pull_request.base.sha }}..${GITHUB_SHA}" | |
| scan_scope="diff-range" | |
| elif [ "${GITHUB_EVENT_NAME}" = "push" ]; then | |
| base_sha="${{ github.event.before }}" | |
| if [ -n "$base_sha" ] && [ "$base_sha" != "0000000000000000000000000000000000000000" ]; then | |
| log_opts="${base_sha}..${GITHUB_SHA}" | |
| scan_scope="diff-range" | |
| fi | |
| elif [ "${GITHUB_EVENT_NAME}" = "merge_group" ]; then | |
| base_sha="${{ github.event.merge_group.base_sha }}" | |
| if [ -n "$base_sha" ]; then | |
| log_opts="${base_sha}..${GITHUB_SHA}" | |
| scan_scope="diff-range" | |
| fi | |
| elif [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then | |
| if [ "${FULL_SECRET_SCAN_INPUT}" != "true" ]; then | |
| if [ -n "${{ github.sha }}" ]; then | |
| log_opts="${{ github.sha }}~1..${{ github.sha }}" | |
| scan_scope="latest-commit" | |
| fi | |
| fi | |
| fail_on_leak="${FAIL_ON_SECRET_LEAK_INPUT}" | |
| fi | |
| cmd=(gitleaks git | |
| --config .gitleaks.toml | |
| --redact | |
| --report-format sarif | |
| --report-path artifacts/gitleaks.sarif | |
| --verbose) | |
| if [ -n "$log_opts" ]; then | |
| cmd+=(--log-opts="$log_opts") | |
| fi | |
| set +e | |
| "${cmd[@]}" | |
| status=$? | |
| set -e | |
| echo "### Gitleaks scan" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- Scope: ${scan_scope}" >> "$GITHUB_STEP_SUMMARY" | |
| if [ -n "$log_opts" ]; then | |
| echo "- Log range: \`${log_opts}\`" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "- Exit code: ${status}" >> "$GITHUB_STEP_SUMMARY" | |
| cat > artifacts/gitleaks-summary.json <<EOF | |
| { | |
| "schema_version": "zerobuild.audit.v1", | |
| "event_type": "gitleaks_scan", | |
| "event_name": "${GITHUB_EVENT_NAME}", | |
| "scope": "${scan_scope}", | |
| "log_opts": "${log_opts}", | |
| "result_code": "${status}", | |
| "fail_on_leak": "${fail_on_leak}" | |
| } | |
| EOF | |
| if [ "$status" -ne 0 ] && [ "$fail_on_leak" = "true" ]; then | |
| exit "$status" | |
| fi | |
| - name: Upload gitleaks SARIF | |
| if: always() | |
| uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4 | |
| with: | |
| sarif_file: artifacts/gitleaks.sarif | |
| category: gitleaks | |
| - name: Upload gitleaks artifact | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: gitleaks-report | |
| path: artifacts/gitleaks.sarif | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Emit gitleaks audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/gitleaks-summary.json ]; then | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type gitleaks_scan \ | |
| --input-json artifacts/gitleaks-summary.json \ | |
| --output-json artifacts/audit-event-gitleaks-scan.json \ | |
| --artifact-name gitleaks-audit-event \ | |
| --retention-days 14 | |
| fi | |
| - name: Upload gitleaks audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: gitleaks-audit-event | |
| path: artifacts/audit-event-gitleaks-scan.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| sbom: | |
| name: SBOM Snapshot | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Install syft | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "${RUNNER_TEMP}/bin" | |
| ./scripts/ci/install_syft.sh "${RUNNER_TEMP}/bin" | |
| echo "${RUNNER_TEMP}/bin" >> "$GITHUB_PATH" | |
| - name: Generate CycloneDX + SPDX SBOM | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| syft dir:. --source-name zerobuild \ | |
| -o cyclonedx-json=artifacts/zerobuild.cdx.json \ | |
| -o spdx-json=artifacts/zerobuild.spdx.json | |
| { | |
| echo "### SBOM snapshot" | |
| echo "- CycloneDX: artifacts/zerobuild.cdx.json" | |
| echo "- SPDX: artifacts/zerobuild.spdx.json" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Upload SBOM artifacts | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: sbom-snapshot | |
| path: artifacts/zerobuild.*.json | |
| retention-days: 14 | |
| - name: Emit SBOM audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cat > artifacts/sbom-summary.json <<EOF | |
| { | |
| "schema_version": "zerobuild.audit.v1", | |
| "event_type": "sbom_snapshot", | |
| "cyclonedx_path": "artifacts/zerobuild.cdx.json", | |
| "spdx_path": "artifacts/zerobuild.spdx.json" | |
| } | |
| EOF | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type sbom_snapshot \ | |
| --input-json artifacts/sbom-summary.json \ | |
| --output-json artifacts/audit-event-sbom-snapshot.json \ | |
| --artifact-name sbom-audit-event \ | |
| --retention-days 14 | |
| - name: Upload SBOM audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: sbom-audit-event | |
| path: artifacts/audit-event-sbom-snapshot.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| unsafe-debt: | |
| name: Unsafe Debt Audit | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Enforce unsafe policy governance | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| python3 scripts/ci/unsafe_policy_guard.py \ | |
| --policy-file scripts/ci/config/unsafe_debt_policy.toml \ | |
| --governance-file .github/security/unsafe-audit-governance.json \ | |
| --output-json artifacts/unsafe-policy-guard.json \ | |
| --output-md artifacts/unsafe-policy-guard.md \ | |
| --fail-on-violation | |
| - name: Publish unsafe governance summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/unsafe-policy-guard.md ]; then | |
| cat artifacts/unsafe-policy-guard.md >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "Unsafe policy governance report missing." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Run unsafe debt audit | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| python3 scripts/ci/unsafe_debt_audit.py \ | |
| --repo-root . \ | |
| --policy-file scripts/ci/config/unsafe_debt_policy.toml \ | |
| --output-json artifacts/unsafe-debt-audit.json \ | |
| --fail-on-findings \ | |
| --fail-on-excluded-crate-roots | |
| - name: Publish unsafe debt summary | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/unsafe-debt-audit.json ]; then | |
| python3 - <<'PY' >> "$GITHUB_STEP_SUMMARY" | |
| import json | |
| from pathlib import Path | |
| report = json.loads(Path("artifacts/unsafe-debt-audit.json").read_text(encoding="utf-8")) | |
| summary = report.get("summary", {}) | |
| source = report.get("source", {}) | |
| by_pattern = summary.get("by_pattern", {}) | |
| print("### Unsafe debt audit") | |
| print(f"- Total findings: `{summary.get('total_findings', 0)}`") | |
| print(f"- Files scanned: `{source.get('files_scanned', 0)}`") | |
| print(f"- Crate roots scanned: `{source.get('crate_roots_scanned', 0)}`") | |
| print(f"- Crate roots excluded: `{source.get('crate_roots_excluded', 0)}`") | |
| if by_pattern: | |
| print("- Findings by pattern:") | |
| for pattern_id, count in sorted(by_pattern.items()): | |
| print(f" - `{pattern_id}`: `{count}`") | |
| else: | |
| print("- Findings by pattern: none") | |
| PY | |
| else | |
| echo "Unsafe debt audit JSON report missing." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Emit unsafe policy governance audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/unsafe-policy-guard.json ]; then | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type unsafe_policy_guard \ | |
| --input-json artifacts/unsafe-policy-guard.json \ | |
| --output-json artifacts/audit-event-unsafe-policy-guard.json \ | |
| --artifact-name unsafe-policy-audit-event \ | |
| --retention-days 14 | |
| fi | |
| - name: Emit unsafe debt audit event | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ -f artifacts/unsafe-debt-audit.json ]; then | |
| python3 scripts/ci/emit_audit_event.py \ | |
| --event-type unsafe_debt_audit \ | |
| --input-json artifacts/unsafe-debt-audit.json \ | |
| --output-json artifacts/audit-event-unsafe-debt-audit.json \ | |
| --artifact-name unsafe-debt-audit-event \ | |
| --retention-days 14 | |
| fi | |
| - name: Upload unsafe policy guard artifacts | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: unsafe-policy-guard | |
| path: artifacts/unsafe-policy-guard.* | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload unsafe debt audit artifact | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: unsafe-debt-audit | |
| path: artifacts/unsafe-debt-audit.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload unsafe policy audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: unsafe-policy-audit-event | |
| path: artifacts/audit-event-unsafe-policy-guard.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - name: Upload unsafe debt audit event | |
| if: always() | |
| uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 | |
| with: | |
| name: unsafe-debt-audit-event | |
| path: artifacts/audit-event-unsafe-debt-audit.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| security-required: | |
| name: Security Required Gate | |
| if: always() && (github.event_name == 'pull_request' || github.event_name == 'push' || github.event_name == 'merge_group') | |
| needs: [audit, deny, security-regressions, secrets, sbom, unsafe-debt] | |
| runs-on: [self-hosted, Linux, X64, aws-india, blacksmith-2vcpu-ubuntu-2404, hetzner] | |
| steps: | |
| - name: Enforce security gate | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| results=( | |
| "audit=${{ needs.audit.result }}" | |
| "deny=${{ needs.deny.result }}" | |
| "security-regressions=${{ needs.security-regressions.result }}" | |
| "secrets=${{ needs.secrets.result }}" | |
| "sbom=${{ needs.sbom.result }}" | |
| "unsafe-debt=${{ needs['unsafe-debt'].result }}" | |
| ) | |
| for item in "${results[@]}"; do | |
| echo "$item" | |
| done | |
| for item in "${results[@]}"; do | |
| result="${item#*=}" | |
| if [ "$result" != "success" ]; then | |
| echo "Security gate failed: $item" | |
| exit 1 | |
| fi | |
| done |