From 30209887569151917219122efca97b06730e7235 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:44:46 +0000 Subject: [PATCH 1/2] Fix unhandled IndexError exceptions (DoS) in crypto classes Added bounds and length validation in `Amber::Support::MessageVerifier` and `Amber::Support::MessageEncryptor` to prevent unhandled `IndexError` exceptions when processing malformed input. Co-authored-by: renich <225115+renich@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/amber/support/message_encryptor.cr | 8 ++++++++ src/amber/support/message_verifier.cr | 10 ++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 000000000..53a06f195 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## $(date +%Y-%m-%d) - Unhandled IndexError in Cryptography Routines (DoS Vector) +**Vulnerability:** Amber's `MessageVerifier` and `MessageEncryptor` attempted to slice arrays and decrypt payloads before checking the bounds of the provided data (`IndexError` due to missing `split` size check and negative offsets in slices). This allows an attacker to send an invalid cookie or payload that crashes the worker. +**Learning:** Crystal's array slicing and destructuring raise `IndexError` when bounds are not met, which propagates to an uncaught 500 error instead of a graceful rejection. Always check payload sizes before executing cryptography functions. +**Prevention:** Always validate size and structure before extracting signatures, IVs, or payload data from untrusted inputs in cryptography routines. Add `size` bounds checks and safely handle destructuring. diff --git a/src/amber/support/message_encryptor.cr b/src/amber/support/message_encryptor.cr index 8ebbb1133..ed31ebbd7 100644 --- a/src/amber/support/message_encryptor.cr +++ b/src/amber/support/message_encryptor.cr @@ -18,6 +18,10 @@ module Amber::Support # Verify and Decrypt a message. We need to verify the message in order to # avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks. def verify_and_decrypt(value : Bytes) : Bytes + if value.size < @signature_size + @block_size + raise Exceptions::InvalidMessage.new("Invalid message size") + end + signature = value[value.size - @signature_size, @signature_size] data_iv = value[0, value.size - @signature_size] if Crypto::Subtle.constant_time_compare(sign_bytes(data_iv), signature) @@ -48,6 +52,10 @@ module Amber::Support end def decrypt(value : Bytes) + if value.size < @block_size + raise Exceptions::InvalidMessage.new("Invalid message size") + end + cipher = OpenSSL::Cipher.new(@cipher_algorithm) data = value[0, value.size - @block_size] iv = value[value.size - @block_size, @block_size] diff --git a/src/amber/support/message_verifier.cr b/src/amber/support/message_verifier.cr index 0d40f5b12..c78b47f10 100644 --- a/src/amber/support/message_verifier.cr +++ b/src/amber/support/message_verifier.cr @@ -11,7 +11,10 @@ module Amber::Support end def verified(signed_message : String) - data, digest = signed_message.split("--") + parts = signed_message.split("--", 2) + return unless parts.size == 2 + data, digest = parts[0], parts[1] + if valid_message?(data, digest) String.new(decode(data)) end @@ -25,7 +28,10 @@ module Amber::Support end def verify_raw(signed_message : String) : Bytes - data, digest = signed_message.split("--") + parts = signed_message.split("--", 2) + raise(Exceptions::InvalidSignature.new) unless parts.size == 2 + data, digest = parts[0], parts[1] + if valid_message?(data, digest) decode(data) else From 26233c476aedfe09e9afa24102a799a1f08f0602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9nich=20Bon=20=C4=86iri=C4=87?= Date: Tue, 16 Jun 2026 22:41:37 -0600 Subject: [PATCH 2/2] security: add unit specs and correct severity language for crypto hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unit specs for MessageVerifier and MessageEncryptor covering malformed, truncated, and undersized payloads. Correct date and severity description in the PR files and agent notes to reflect graceful-rejection hardening instead of a worker DoS crash. Co-developed-by: Gemini AI Signed-off-by: Rénich Bon Ćirić --- .jules/sentinel.md | 8 ++--- spec/amber/support/message_encryptor_spec.cr | 31 ++++++++++++++++++++ spec/amber/support/message_verifier_spec.cr | 27 +++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 spec/amber/support/message_encryptor_spec.cr create mode 100644 spec/amber/support/message_verifier_spec.cr diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 53a06f195..ec2af4f1d 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -1,4 +1,4 @@ -## $(date +%Y-%m-%d) - Unhandled IndexError in Cryptography Routines (DoS Vector) -**Vulnerability:** Amber's `MessageVerifier` and `MessageEncryptor` attempted to slice arrays and decrypt payloads before checking the bounds of the provided data (`IndexError` due to missing `split` size check and negative offsets in slices). This allows an attacker to send an invalid cookie or payload that crashes the worker. -**Learning:** Crystal's array slicing and destructuring raise `IndexError` when bounds are not met, which propagates to an uncaught 500 error instead of a graceful rejection. Always check payload sizes before executing cryptography functions. -**Prevention:** Always validate size and structure before extracting signatures, IVs, or payload data from untrusted inputs in cryptography routines. Add `size` bounds checks and safely handle destructuring. +## 2026-06-16 - Unhandled IndexError Hardening in Cryptography Routines +**Vulnerability:** Amber's `MessageVerifier` and `MessageEncryptor` attempted to slice arrays and decrypt payloads before checking the bounds of the provided data (`IndexError` due to missing `split` size check and negative offsets in slices). While top-level middleware rescues and returns a 500 status code, these classes should gracefully reject malformed payloads themselves with custom exceptions. +**Learning:** Crystal's array slicing and destructuring raise `IndexError` when bounds are not met, which propagates to an uncaught exception instead of a graceful rejection. Always check payload sizes before executing cryptography functions. +**Prevention:** Always validate size and structure before extracting signatures, IVs, or payload data from untrusted inputs in cryptography routines. Add `size` bounds checks and safely handle destructuring, raising typed exceptions like `InvalidSignature` and `InvalidMessage`. diff --git a/spec/amber/support/message_encryptor_spec.cr b/spec/amber/support/message_encryptor_spec.cr new file mode 100644 index 000000000..7addb153a --- /dev/null +++ b/spec/amber/support/message_encryptor_spec.cr @@ -0,0 +1,31 @@ +require "../../spec_helper" + +module Amber::Support + describe MessageEncryptor do + secret = "a" * 32 + encryptor = MessageEncryptor.new(secret) + + it "encrypts and decrypts valid messages successfully" do + message = "Hello, world!" + encrypted = encryptor.encrypt_and_sign(message) + decrypted = encryptor.verify_and_decrypt(encrypted) + String.new(decrypted).should eq message + end + + it "raises Exceptions::InvalidMessage for undersized payloads in verify_and_decrypt" do + # signature_size is 32, block_size is 16. Total required: >= 48 bytes + undersized = Bytes.new(20) + expect_raises(Exceptions::InvalidMessage, "Invalid message size") do + encryptor.verify_and_decrypt(undersized) + end + end + + it "raises Exceptions::InvalidMessage for undersized payloads in decrypt" do + # block_size is 16. decrypt expects >= 16 bytes + undersized = Bytes.new(10) + expect_raises(Exceptions::InvalidMessage, "Invalid message size") do + encryptor.decrypt(undersized) + end + end + end +end diff --git a/spec/amber/support/message_verifier_spec.cr b/spec/amber/support/message_verifier_spec.cr new file mode 100644 index 000000000..32ba25076 --- /dev/null +++ b/spec/amber/support/message_verifier_spec.cr @@ -0,0 +1,27 @@ +require "../../spec_helper" + +module Amber::Support + describe MessageVerifier do + secret = "a" * 32 + verifier = MessageVerifier.new(secret) + + it "signs and verifies valid messages successfully" do + message = "Hello, world!" + signed = verifier.generate(message) + verified = verifier.verified(signed) + verified.should eq message + end + + it "returns nil for malformed/no-delimiter payloads in verified" do + malformed = "invalidpayload" + verifier.verified(malformed).should be_nil + end + + it "raises Exceptions::InvalidSignature for malformed/no-delimiter payloads in verify_raw" do + malformed = "invalidpayload" + expect_raises(Exceptions::InvalidSignature) do + verifier.verify_raw(malformed) + end + end + end +end