diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60d0539..c9c002e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,15 +28,15 @@ jobs: runs-on: cachekit timeout-minutes: 10 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust - name: Cache Python virtual environment - uses: actions/cache@v5 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: .venv key: venv-${{ runner.os }}-py${{ env.DEFAULT_PYTHON_VERSION }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock', 'rust/**/*.rs', 'rust/**/Cargo.toml') }} @@ -94,15 +94,15 @@ jobs: --health-retries 5 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust - name: Cache Python virtual environment - uses: actions/cache@v5 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: .venv key: venv-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml', '**/uv.lock', 'rust/**/*.rs', 'rust/**/Cargo.toml') }} @@ -140,7 +140,7 @@ jobs: - name: Upload coverage to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5 with: files: ./coverage.xml use_oidc: true @@ -149,7 +149,7 @@ jobs: - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5 with: files: ./junit.xml report_type: test_results @@ -164,7 +164,7 @@ jobs: runs-on: cachekit timeout-minutes: 10 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Check Python ↔ Rust version consistency run: | @@ -176,7 +176,7 @@ jobs: fi - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 91152bf..6b57c7b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -50,12 +50,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 # Python setup for accurate analysis - name: Set up Python if: matrix.language == 'python' - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" @@ -68,7 +68,7 @@ jobs: # Initialize CodeQL - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@603b797f8b14b413fe025cd935a91c16c4782713 # v3 with: languages: ${{ matrix.language }} # Use security-extended for maximum coverage @@ -80,11 +80,11 @@ jobs: # Autobuild attempts to build any compiled code # For Python, this is largely a no-op but ensures imports resolve - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@603b797f8b14b413fe025cd935a91c16c4782713 # v3 # Run CodeQL analysis - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@603b797f8b14b413fe025cd935a91c16c4782713 # v3 with: category: "/language:${{ matrix.language }}" # Upload SARIF to GitHub Security tab @@ -94,7 +94,7 @@ jobs: # Archive SARIF for audit trail - name: Upload SARIF artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: codeql-sarif-${{ matrix.language }} path: sarif-results diff --git a/.github/workflows/fuzz-smoke.yml b/.github/workflows/fuzz-smoke.yml index bd1d94a..a172edd 100644 --- a/.github/workflows/fuzz-smoke.yml +++ b/.github/workflows/fuzz-smoke.yml @@ -26,13 +26,15 @@ jobs: timeout-minutes: 20 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Rust nightly - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # nightly + with: + toolchain: nightly - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust/fuzz cache-all-crates: true @@ -91,7 +93,7 @@ jobs: - name: Upload crash artifacts if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: fuzz-crash-artifacts path: rust/fuzz/artifacts/ diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 85e7572..afbe88a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -35,7 +35,7 @@ jobs: # If APP_ID/APP_PRIVATE_KEY not set, falls back to GITHUB_TOKEN - name: Generate GitHub App token id: app-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2 if: ${{ vars.USE_APP_TOKEN == 'true' }} with: app-id: ${{ secrets.APP_ID }} @@ -43,7 +43,7 @@ jobs: - name: Run release-please id: release - uses: googleapis/release-please-action@v4 + uses: googleapis/release-please-action@c3fc4de07084f75a2b61a5b933069bda6edf3d5c # v4 with: manifest-file: .release-please-manifest.json config-file: release-please-config.json @@ -80,7 +80,7 @@ jobs: manylinux: "2_28" runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: # Use release-please tag or manual input tag ref: ${{ needs.release-please.outputs.tag_name || github.event.inputs.release_tag }} @@ -89,7 +89,7 @@ jobs: # Linux uses Docker containers which have Python pre-installed - name: Set up Python if: matrix.os != 'ubuntu-latest' - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: | 3.9 @@ -98,14 +98,14 @@ jobs: 3.12 3.13 - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1 with: target: ${{ matrix.target }} args: --release --out dist ${{ matrix.interpreter }} manylinux: ${{ matrix.manylinux || 'auto' }} rust-toolchain: stable - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: wheels-${{ matrix.target }} path: dist @@ -116,17 +116,17 @@ jobs: if: needs.release-please.outputs.release_created == 'true' || github.event.inputs.force_release == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ needs.release-please.outputs.tag_name || github.event.inputs.release_tag }} - - uses: PyO3/maturin-action@v1 + - uses: PyO3/maturin-action@04ac600d27cdf7a9a280dadf7147097c42b757ad # v1 with: command: sdist args: --out dist rust-toolchain: stable - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: sdist path: dist @@ -140,13 +140,13 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: pattern: wheels-* merge-multiple: true path: dist - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: sdist path: dist diff --git a/.github/workflows/security-deep.yml b/.github/workflows/security-deep.yml index daf21b2..5385ff9 100644 --- a/.github/workflows/security-deep.yml +++ b/.github/workflows/security-deep.yml @@ -19,10 +19,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Run Kani verification - uses: model-checking/kani-github-action@v1 + uses: model-checking/kani-github-action@f838096619a707b0f6b2118cf435eaccfa33e51f # v1 with: working-directory: rust args: --tests --no-default-features --features compression,checksum,messagepack,encryption @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 200 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install nightly Rust run: | @@ -40,7 +40,7 @@ jobs: rustup default nightly - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust @@ -89,10 +89,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@8d55fbecc275b1c35dbe060458839f8d30439ccf # v3 with: enable-cache: true @@ -100,15 +100,17 @@ jobs: run: uv python install 3.11 - name: Set up Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # stable + with: + toolchain: stable - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust - name: Cache Python virtual environment - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: .venv key: venv-${{ runner.os }}-py3.11-${{ hashFiles('**/pyproject.toml', '**/uv.lock') }} @@ -130,7 +132,7 @@ jobs: - name: Upload crash corpus if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: atheris-crashes-${{ github.run_id }} path: tests/fuzzing/corpus/ @@ -150,7 +152,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install nightly Rust with Miri run: | @@ -158,7 +160,7 @@ jobs: rustup default nightly - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust @@ -176,7 +178,7 @@ jobs: matrix: sanitizer: [address, thread, memory] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install nightly Rust run: | @@ -188,7 +190,7 @@ jobs: run: rustup component add rust-src --toolchain nightly - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust key: ${{ matrix.sanitizer }} @@ -227,7 +229,7 @@ jobs: needs: [kani-verification, fuzzing, atheris-fuzzing, miri-full, sanitizers] if: always() steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Generate security report run: | @@ -289,7 +291,7 @@ jobs: cat "$REPORT" - name: Archive security report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: security-deep-report path: reports/security/ diff --git a/.github/workflows/security-fast.yml b/.github/workflows/security-fast.yml index 45d7f72..214036f 100644 --- a/.github/workflows/security-fast.yml +++ b/.github/workflows/security-fast.yml @@ -27,9 +27,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: rustsec/audit-check@v2 + - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -38,24 +38,25 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: EmbarkStudios/cargo-deny-action@82eb9f621fbc699dd0918f3ea06864c14cc84246 # v2 clippy-security: name: Security Lints runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # stable with: + toolchain: stable components: clippy - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust @@ -70,10 +71,12 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # stable + with: + toolchain: stable - name: Install cargo-binstall uses: cargo-bins/cargo-binstall@main @@ -91,10 +94,10 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@8d55fbecc275b1c35dbe060458839f8d30439ccf # v3 with: enable-cache: true @@ -102,7 +105,7 @@ jobs: run: uv python install 3.12 - name: Cache Python virtual environment - uses: actions/cache@v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5 with: path: .venv key: venv-${{ runner.os }}-py3.12-${{ hashFiles('**/pyproject.toml', '**/uv.lock') }} @@ -119,7 +122,7 @@ jobs: - name: Upload report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 with: name: pip-audit-report path: pip-audit-report.json diff --git a/.github/workflows/security-medium.yml b/.github/workflows/security-medium.yml index 3d4d062..fa8846f 100644 --- a/.github/workflows/security-medium.yml +++ b/.github/workflows/security-medium.yml @@ -23,10 +23,12 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # stable + with: + toolchain: stable - name: Install cargo-binstall uses: cargo-bins/cargo-binstall@main @@ -65,7 +67,7 @@ jobs: echo "✅ Unsafe ratio ($UNSAFE_RATIO%) is within acceptable limits" - name: Archive geiger report - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 if: always() with: name: geiger-report @@ -77,7 +79,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install nightly Rust with Miri run: | @@ -85,7 +87,7 @@ jobs: rustup default nightly - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: workspaces: rust diff --git a/src/cachekit/backends/cachekitio/backend.py b/src/cachekit/backends/cachekitio/backend.py index cfa4245..935ac7d 100644 --- a/src/cachekit/backends/cachekitio/backend.py +++ b/src/cachekit/backends/cachekitio/backend.py @@ -77,14 +77,15 @@ def _inject_metrics_headers(stats: _FunctionStats | None) -> dict[str, str]: >>> headers["X-CacheKit-L1-Status"] 'disabled' - >>> # Graceful degradation with None + >>> # Graceful degradation with None (standalone usage) >>> headers = _inject_metrics_headers(None) >>> headers - {} + {'X-CacheKit-L1-Status': 'disabled'} """ - # Graceful degradation: return empty dict if stats is None + # Graceful degradation: default to L1-Status: disabled if stats is None + # This ensures standalone usage with ck_sdk_* keys doesn't trigger 400 errors if stats is None: - return {} + return {"X-CacheKit-L1-Status": "disabled"} # Extract metrics from stats info = stats.get_info() @@ -220,16 +221,14 @@ def _request_sync( called from within a @cache decorated function. If no stats available in context, headers are not injected (backward compatible). """ - # Inject metrics headers if stats available in context + # Inject metrics headers (always — defaults to L1-Status: disabled when no stats) stats = get_current_function_stats() - - if stats is not None: - metrics_headers = _inject_metrics_headers(stats) - # Merge with existing headers - if "headers" in kwargs: - kwargs["headers"] = {**kwargs["headers"], **metrics_headers} - else: - kwargs["headers"] = metrics_headers + metrics_headers = _inject_metrics_headers(stats) + # Merge with existing headers + if "headers" in kwargs: + kwargs["headers"] = {**kwargs["headers"], **metrics_headers} + else: + kwargs["headers"] = metrics_headers url = f"/v1/cache/{endpoint}" try: @@ -272,16 +271,14 @@ async def _request_async( called from within a @cache decorated function. If no stats available in context, headers are not injected (backward compatible). """ - # Inject metrics headers if stats available in context + # Inject metrics headers (always — defaults to L1-Status: disabled when no stats) stats = get_current_function_stats() - - if stats is not None: - metrics_headers = _inject_metrics_headers(stats) - # Merge with existing headers - if "headers" in kwargs: - kwargs["headers"] = {**kwargs["headers"], **metrics_headers} - else: - kwargs["headers"] = metrics_headers + metrics_headers = _inject_metrics_headers(stats) + # Merge with existing headers + if "headers" in kwargs: + kwargs["headers"] = {**kwargs["headers"], **metrics_headers} + else: + kwargs["headers"] = metrics_headers url = f"/v1/cache/{endpoint}" try: