Skip to content

test(python-frame): pin the exact decrypt refusal per vector (LAB-8714) - #161

Merged
27Bslash6 merged 1 commit into
mainfrom
agent/miss-huang/LAB-8714-pin-decrypt-messages
Oct 9, 2026
Merged

27Bslash6 merged 1 commit into
mainfrom
agent/miss-huang/LAB-8714-pin-decrypt-messages

Conversation

@27Bslash6

Copy link
Copy Markdown
Contributor

Pins the exact refusal message in every_vector_is_refused_at_decrypt_by_an_encrypting_client. The test checked only the decrypt failed: prefix, so its doc comment's account of how an encrypting reader refuses a CK frame (stored bytes go whole to AES-GCM) was prose, not a test.

  • AUTH_FAILURE moves from encryption_vector_tests.rs to tests/common/mod.rs; both suites use it. A new SHORT_CIPHERTEXT constant holds decrypt failed: Invalid ciphertext format: Ciphertext too short.
  • Each row now expects SHORT_CIPHERTEXT when its bytes are under 28 (nonce + tag), AUTH_FAILURE otherwise. The test asserts 31 rows, 4 of them short.
  • The doc comment no longer says the refusal is pinned to the error class only.

Test code only; nothing under src/, the vectors or the README changes.

Mutants (not committed)

Each was applied to EncryptionLayer::decrypt, and the test was run with its panic swapped for a report so every failing row is named. With the committed test, each mutant fails the test (first failure raw_payload_frame).

Mutant A: refuse CK magic before AES-GCM
         let aad = self.build_aad(cache_key, false);
+        if ciphertext.starts_with(b"CK") {
+            return Err(CachekitError::Encryption(
+                "decrypt failed: CK frame refused".to_owned(),
+            ));
+        }
         let (plaintext, index) = self

Fails all 29 CK rows on all three read paths (get, interop_get, interop_get_swr): every row starting CK gets a third message that matches neither constant. The two non-CK rows (bare_envelope_fed_to_frame_reader, plain_msgpack_fed_to_frame_reader) pass, as expected.

Mutant B: strip the CK frame, decrypt only the payload
         let aad = self.build_aad(cache_key, false);
+        let ciphertext = if ciphertext.starts_with(b"CK") && ciphertext.len() >= 7 {
+            let hdr = u32::from_be_bytes(ciphertext[3..7].try_into().unwrap()) as usize;
+            &ciphertext[(7 + hdr).min(ciphertext.len())..]
+        } else {
+            ciphertext
+        };
         let (plaintext, index) = self

Fails 3 rows on all three read paths, each getting SHORT_CIPHERTEXT where AUTH_FAILURE is expected:

  • raw_payload_frame and ck_frame_fed_to_interop_reader: 100 bytes, 77-byte header, 16-byte payload.
  • header_overrun_by_one: the declared header runs past the frame, so this mutant slices to the bytes present and is left with an empty payload.

Every other CK row of 28 bytes or more keeps a payload of 28 bytes or more and still fails authentication, so it passes. If a re-vendor lengthens those three rows, this mutant survives again.

Checks run locally

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --features "cachekitio,redis,encryption,l1,macros,memcached,file,tracing" -- -D warnings
  • cargo test --features "cachekitio,redis,encryption,l1,macros,memcached,file,tracing": all suites pass.

Closes LAB-8714

The encrypted python-frame sweep checked only the 'decrypt failed: ' prefix,
so a reader that stripped the CK frame or refused CK magic before AES-GCM
still passed. Assert the exact message per row: short-ciphertext under 28
bytes (4 rows), authentication failure otherwise (27). AUTH_FAILURE moves to
tests/common so both encryption suites share it.
@coderabbitai

coderabbitai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: cachekit-io/cachekit-rs/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ab356adc-4ac7-4f5b-845e-7f5465ea748c

📥 Commits

Reviewing files that changed from the base of the PR and between 3a0d241 and 44e21d2.


📒 Files selected for processing (3)
  • crates/cachekit/tests/common/mod.rs
  • crates/cachekit/tests/encryption_vector_tests.rs
  • crates/cachekit/tests/python_frame_tests.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.



Summary by CodeRabbit

  • Tests
    • Expanded encryption compatibility checks to verify that invalid encrypted data is rejected with the expected error.
    • Added coverage for inputs that are too short to contain the required encryption information, while continuing to confirm that all test vectors are refused.

Walkthrough

The tests now use shared constants for encryption errors. Python frame vector tests check the exact error for each rejected vector based on whether its ciphertext is shorter than 28 bytes.

Changes

Encryption test errors

Layer / File(s) Summary
Shared error constants
crates/cachekit/tests/common/mod.rs, crates/cachekit/tests/encryption_vector_tests.rs
The common test module defines constants for authentication failure and short ciphertext errors. Encryption vector tests import the shared authentication-failure constant instead of defining it locally.
Length-based error checks
crates/cachekit/tests/python_frame_tests.rs
The test expects the short-ciphertext error for inputs under 28 bytes and the authentication-failure error otherwise. It checks the exact error messages and asserts that four vectors are short, while retaining the check that all 31 vectors are refused.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Other

Merge Risk: ⚪ Minimal · up to 44e21

The change strengthens the encryption-vector tests to distinguish malformed short ciphertext from authentication failures and catches the two described decrypt regressions. No concrete merge-blocking risk remains; normal checks suffice.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly identifies the test change and the exact decrypt refusal being pinned. It is concise and related to the main change.
Description check Passed The description accurately explains the per-vector error assertions, shared constants, mutant coverage, and validation commands. It is directly related to the changeset.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR


  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kodus-27b

kodus-27b Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Providing Context (Files & MCPs)

Add these hints in your PR description (or a comment) to unlock deeper checks:

  • Ticket / Acceptance Criteria: `Refs: ABC-123` (Linear/Jira/Asana/ClickUp/Trello) or a direct ticket link.
  • Bugfix Validation: a Sentry/Datadog/Bugsnag event link (or paste the stack trace/error message).
  • Endpoint Risk: mention the route (e.g., `POST /api/payments`) or controller/action name.
  • Attach a repo file as context: use an explicit marker like `@file:docs/guide.mdx#L10-L50` (replace with your real path).
  • API Contract Docs: include `@file:openapi.yaml` or `@file:swagger.json` when changing routes/schemas.
  • Definition of Done / Standards: include `@file:DOD.md` or `@file:CONTRIBUTING.md` if your repo has them.
  • Design System Source of Truth: include `@file:ui/index.ts` (replace with your DS entrypoint path).
  • Feature Flags: include the flag key/name and `@file:flags.ts` / `@file:config.json` (and optionally the PostHog flag name).
  • Edge/CDN Rules: link the Cloudflare rule/zone or describe the intended redirect/header behavior.
  • Attach an MCP tool output: use `@mcp<provider|tool>` (replace with an installed MCP provider + tool, e.g., `@mcp<sentry|events.search>`).
Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ✅
Security ✅
Business Logic ✅

Access your configuration settings here.

@27Bslash6
27Bslash6 merged commit a68f517 into main Oct 9, 2026
11 checks passed
@27Bslash6
27Bslash6 deleted the agent/miss-huang/LAB-8714-pin-decrypt-messages branch October 9, 2026 14:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant