diff --git a/.github/workflows/sec-audit.yml b/.github/workflows/sec-audit.yml index c85e7e59e..954f17e73 100644 --- a/.github/workflows/sec-audit.yml +++ b/.github/workflows/sec-audit.yml @@ -21,6 +21,7 @@ on: - "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] @@ -42,6 +43,7 @@ on: - "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] @@ -145,6 +147,22 @@ jobs: if-no-files-found: ignore retention-days: 14 + security-regressions: + name: Security Regression Tests + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 30 + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - 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: blacksmith-2vcpu-ubuntu-2404 @@ -545,7 +563,7 @@ jobs: 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, secrets, sbom, unsafe-debt] + needs: [audit, deny, security-regressions, secrets, sbom, unsafe-debt] runs-on: blacksmith-2vcpu-ubuntu-2404 steps: - name: Enforce security gate @@ -555,6 +573,7 @@ jobs: 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 }}" diff --git a/scripts/ci/security_regression_tests.sh b/scripts/ci/security_regression_tests.sh new file mode 100755 index 000000000..9dd49f3a6 --- /dev/null +++ b/scripts/ci/security_regression_tests.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Focused security regression suite covering critical auth/policy/secret paths. +# Keep tests narrowly scoped and deterministic so they can run in security CI. +TESTS=( + run_tool_call_loop_denies_supervised_tools_on_non_cli_channels + run_tool_call_loop_blocks_tools_excluded_for_channel + webhook_rejects_public_traffic_without_auth_layers + metrics_endpoint_rejects_public_clients_when_pairing_is_disabled + metrics_endpoint_requires_bearer_token_when_pairing_is_enabled + extract_ws_bearer_token_rejects_empty_tokens + autonomy_config_serde_defaults_non_cli_excluded_tools + config_validate_rejects_duplicate_non_cli_excluded_tools + config_debug_redacts_sensitive_values + config_save_encrypts_nested_credentials + replayed_totp_code_is_rejected + validate_command_execution_rejects_forbidden_paths + screenshot_path_validation_blocks_escaped_paths + test_execute_blocked_in_read_only_mode + key_file_created_on_first_encrypt + scrub_google_api_key_prefix + scrub_aws_access_key_prefix +) + +CARGO_BIN="${CARGO_BIN:-cargo}" + +for test_name in "${TESTS[@]}"; do + echo "==> ${CARGO_BIN} test --locked --lib ${test_name}" + "${CARGO_BIN}" test --locked --lib "${test_name}" -- --nocapture +done