Skip to content

fix: add input validation to prevent unbounded storage writes#94

Merged
mubarakcoded merged 4 commits intomasterfrom
71-issue-ab-unbounded-user-input-stored-in-sqlite-causes-disk-and-cpu-exhaustion
Jan 1, 2026
Merged

fix: add input validation to prevent unbounded storage writes#94
mubarakcoded merged 4 commits intomasterfrom
71-issue-ab-unbounded-user-input-stored-in-sqlite-causes-disk-and-cpu-exhaustion

Conversation

@mubarakcoded
Copy link
Contributor

@mubarakcoded mubarakcoded commented Dec 31, 2025

Summary by CodeRabbit

  • New Features

    • Early input validation to reject oversized or invalid group, welcome, and message data (names, descriptions, content, tags, events, admin keys, relays) before saving.
  • Bug Fixes

    • Clearer validation error reporting when inputs exceed enforced limits.
  • Tests

    • Added and adjusted tests to cover validation failures and boundary cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

Walkthrough

Adds a new internal validation module and applies pre-insert checks for string length and serialized JSON size across group, message, and welcome storage; error handling updated to include a Validation variant and tests added/adjusted to cover new limits.

Changes

Cohort / File(s) Summary
Validation infrastructure
crates/mdk-sqlite-storage/src/lib.rs, crates/mdk-sqlite-storage/src/validation.rs
New validation module declared in lib.rs. Adds public constants for maximum byte/length limits and two helpers validate_size and validate_string_length that return crate::error::Error::Validation on violations.
Error type
crates/mdk-sqlite-storage/src/error.rs
Error enum converted to thiserror::Error derive; adds Validation { field_name, max_size, actual_size } variant and annotates existing variants with #[error(...)]. Adjusted From/Display conversions accordingly.
Groups storage
crates/mdk-sqlite-storage/src/groups.rs
Adds pre-insert validation for group_name and group_description lengths and serialized admin_pubkeys JSON size; validation failures mapped to GroupError::InvalidParameters. Tests added for oversized name/description.
Messages storage
crates/mdk-sqlite-storage/src/messages.rs
Adds pre-insert validation for message content length, tags JSON size, and serialized event JSON size; uses event_json in DB INSERT. Test added for oversized message content.
Welcomes storage
crates/mdk-sqlite-storage/src/welcomes.rs
Adds pre-insert validation for group_name/group_description lengths, serialized group_admin_pubkeys and group_relays sizes, and event JSON size; uses event_json in DB INSERT. Test added for oversized group name.
Tests (shared)
crates/mdk-storage-traits/tests/shared/group_tests.rs
Adjusts long-name edge-case to a 250-character string (within new 255-byte limit) and updates comments.
Cargo metadata
crates/mdk-sqlite-storage/Cargo.toml
Adds dependency flag thiserror.workspace = true under [dependencies].

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: adding input validation to storage operations to prevent unbounded writes. It is concise, specific, and directly reflects the changeset's core objective across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77761e8 and 51d6066.

📒 Files selected for processing (2)
  • crates/mdk-sqlite-storage/src/error.rs
  • crates/mdk-sqlite-storage/src/validation.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)

**/*.rs: All trait bounds should be written in where clauses rather than inline with type parameters in Rust
Use Self instead of the explicit type name when referring to the type in impl blocks
Derive Debug, Clone, Copy, PartialEq, Eq, and Hash for public types when possible, in that order
Derive Default for public types when there is a reasonable default value
Always use full paths for logging with tracing::<op>!(...) instead of importing and using <op>!(...)
Prefer .to_string() or .to_owned() for &str to String conversion instead of .into() or String::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import the fmt module rather than using full paths when implementing traits from core::fmt or std::fmt
Use relative self:: imports for sub-modules rather than bare module names
Place all use statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoid if let ... { } else { } constructs; use match instead when both branches are non-empty
Use if let ... { } without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g., x.rs) with mod x; declaration rather than inline mod x { .. } blocks, except for tests and benches modules
Use inline mod tests { #[cfg(test)] ... } for test modules rather than creating separate tests.rs files
Use inline mod benches { #[cfg(bench)] ... } for benchmark modules rather than creating separate benches.rs files

Files:

  • crates/mdk-sqlite-storage/src/validation.rs
  • crates/mdk-sqlite-storage/src/error.rs
🧬 Code graph analysis (1)
crates/mdk-sqlite-storage/src/error.rs (1)
crates/mdk-core/src/error.rs (12)
  • from (203-205)
  • from (209-211)
  • from (215-217)
  • from (221-223)
  • from (230-232)
  • from (239-241)
  • from (248-250)
  • from (257-259)
  • from (266-268)
  • from (275-277)
  • from (284-286)
  • from (294-308)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Coverage
🔇 Additional comments (5)
crates/mdk-sqlite-storage/src/error.rs (2)

4-17: LGTM! Clean migration to thiserror.

The thiserror migration eliminates boilerplate while maintaining clear, descriptive error messages. The #[from] attributes on rusqlite::Error and refinery::Error correctly replace the manual From implementations.


18-27: Well-structured validation error variant.

The Validation variant provides clear, actionable error information with field name, size limits, and actual size. The error message format is user-friendly and helps quickly identify which field exceeded limits.

crates/mdk-sqlite-storage/src/validation.rs (3)

1-26: Clear documentation and reasonable size limits.

The constants are well-documented with explicit units and encoding (UTF-8 bytes). The limits are appropriate for preventing resource exhaustion while allowing legitimate use cases:

  • 1 MB message content provides ample space without unbounded growth
  • 100 KB JSON limits prevent excessive metadata
  • 255 bytes for group names aligns with database standards
  • Other limits are similarly well-balanced

28-39: Efficient validation with correct semantics.

The function correctly validates that data.len() <= max_size (using > means the maximum is inclusive). The #[inline] attribute is appropriate since this is a small, frequently-called function. Error reporting includes all necessary context for debugging.


41-55: Correct string validation with helpful documentation.

The documentation clearly explains that validation is based on UTF-8 byte length, not Unicode character count. This is important for multi-byte characters (emoji, etc.). The implementation correctly uses s.len() which returns byte count in Rust.


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 and usage tips.

@mubarakcoded mubarakcoded marked this pull request as ready for review January 1, 2026 08:17
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/mdk-sqlite-storage/src/groups.rs (1)

82-106: Consider serializing admin_pubkeys before acquiring the lock.

The group name and description validations correctly occur before acquiring the lock (lines 82-91), which is efficient. However, the admin_pubkeys serialization happens after acquiring the lock at line 93 (serialization at lines 95-98, validation at 101-106).

For consistency with messages.rs and welcomes.rs, consider moving the serialization and validation of admin_pubkeys before the lock acquisition. This would avoid holding the lock during serialization and validation operations.

🔎 Suggested refactor
-        // Validate group name and description lengths
         validate_string_length(&group.name, MAX_GROUP_NAME_LENGTH, "Group name")
             .map_err(GroupError::InvalidParameters)?;
 
         validate_string_length(
             &group.description,
             MAX_GROUP_DESCRIPTION_LENGTH,
             "Group description",
         )
         .map_err(GroupError::InvalidParameters)?;
 
-        let conn_guard = self.db_connection.lock().map_err(into_group_err)?;
-
+        // Serialize and validate admin pubkeys before acquiring lock
         let admin_pubkeys_json: String =
             serde_json::to_string(&group.admin_pubkeys).map_err(|e| {
                 GroupError::DatabaseError(format!("Failed to serialize admin pubkeys: {}", e))
             })?;
 
-        // Validate admin pubkeys JSON size
         validate_size(
             admin_pubkeys_json.as_bytes(),
             MAX_ADMIN_PUBKEYS_JSON_SIZE,
             "Admin pubkeys JSON",
         )
         .map_err(GroupError::InvalidParameters)?;
 
+        let conn_guard = self.db_connection.lock().map_err(into_group_err)?;
+
         let last_message_id: Option<&[u8; 32]> =
             group.last_message_id.as_ref().map(|id| id.as_bytes());
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1084da1 and ff615dc.

📒 Files selected for processing (5)
  • crates/mdk-sqlite-storage/src/groups.rs
  • crates/mdk-sqlite-storage/src/lib.rs
  • crates/mdk-sqlite-storage/src/messages.rs
  • crates/mdk-sqlite-storage/src/validation.rs
  • crates/mdk-sqlite-storage/src/welcomes.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)

**/*.rs: All trait bounds should be written in where clauses rather than inline with type parameters in Rust
Use Self instead of the explicit type name when referring to the type in impl blocks
Derive Debug, Clone, Copy, PartialEq, Eq, and Hash for public types when possible, in that order
Derive Default for public types when there is a reasonable default value
Always use full paths for logging with tracing::<op>!(...) instead of importing and using <op>!(...)
Prefer .to_string() or .to_owned() for &str to String conversion instead of .into() or String::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import the fmt module rather than using full paths when implementing traits from core::fmt or std::fmt
Use relative self:: imports for sub-modules rather than bare module names
Place all use statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoid if let ... { } else { } constructs; use match instead when both branches are non-empty
Use if let ... { } without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g., x.rs) with mod x; declaration rather than inline mod x { .. } blocks, except for tests and benches modules
Use inline mod tests { #[cfg(test)] ... } for test modules rather than creating separate tests.rs files
Use inline mod benches { #[cfg(bench)] ... } for benchmark modules rather than creating separate benches.rs files

Files:

  • crates/mdk-sqlite-storage/src/welcomes.rs
  • crates/mdk-sqlite-storage/src/messages.rs
  • crates/mdk-sqlite-storage/src/lib.rs
  • crates/mdk-sqlite-storage/src/groups.rs
  • crates/mdk-sqlite-storage/src/validation.rs
🧠 Learnings (1)
📚 Learning: 2025-12-03T10:18:10.432Z
Learnt from: CR
Repo: marmot-protocol/mdk PR: 0
File: .cursor/rules/rust.mdc:0-0
Timestamp: 2025-12-03T10:18:10.432Z
Learning: Applies to **/*.rs : Use inline `mod tests { #[cfg(test)] ... }` for test modules rather than creating separate `tests.rs` files

Applied to files:

  • crates/mdk-sqlite-storage/src/welcomes.rs
🧬 Code graph analysis (1)
crates/mdk-sqlite-storage/src/messages.rs (1)
crates/mdk-sqlite-storage/src/validation.rs (2)
  • validate_size (28-38)
  • validate_string_length (42-52)
🔇 Additional comments (8)
crates/mdk-sqlite-storage/src/lib.rs (1)

27-27: LGTM: Internal validation module added.

The module declaration follows the coding guideline for separate file module declarations and is appropriately placed with other internal modules.

crates/mdk-sqlite-storage/src/groups.rs (2)

14-17: LGTM: Validation imports added.

The imports follow the coding guidelines with correct import order and path usage.


368-434: LGTM: Comprehensive validation tests.

The tests effectively verify that oversized group names and descriptions are rejected with appropriate error messages. The test coverage adequately validates the new input constraints.

crates/mdk-sqlite-storage/src/messages.rs (2)

25-48: LGTM: Validation logic correctly prevents unbounded writes.

The validation pattern correctly enforces size limits before database operations:

  • Message content is validated before lock acquisition (efficient)
  • Tags and event JSON are serialized and validated after lock acquisition but before DB write (acceptable since serialization is fast)
  • Error messages are informative and include actual vs. maximum sizes

258-316: LGTM: Test validates content size enforcement.

The test effectively verifies that message content exceeding the 1MB limit is rejected with an appropriate error message.

crates/mdk-sqlite-storage/src/welcomes.rs (2)

26-71: LGTM: Validation logic prevents unbounded storage writes.

The validation pattern correctly enforces size limits:

  • Group name and description are validated before lock acquisition (efficient)
  • JSON serialization and validation occur after lock acquisition but before DB write (acceptable)
  • All validations use appropriate error types and messages

267-318: LGTM: Test validates group name length enforcement.

The test effectively verifies that welcome messages with oversized group names are rejected with appropriate error messages.

crates/mdk-sqlite-storage/src/validation.rs (1)

26-52: LGTM: Validation functions are well-implemented.

The validation helper functions are:

  • Appropriately marked #[inline] for performance in hot paths
  • Simple and focused on single responsibilities
  • Provide clear, informative error messages with actual vs. expected values
  • Follow a consistent error message format

@github-actions
Copy link

github-actions bot commented Jan 1, 2026

✅ Coverage: 86.12% → 86.28% (+0.16%)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/mdk-storage-traits/tests/shared/group_tests.rs (1)

3-11: Reorder imports to follow coding guidelines.

The imports should follow this sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules. Currently, the nostr import (external) appears after mdk_storage_traits imports (internal).

As per coding guidelines, the imports should be reordered.

🔎 Proposed reordering
 use std::collections::BTreeSet;
 
+use nostr::RelayUrl;
+
+use super::create_test_group;
 use mdk_storage_traits::GroupId;
 use mdk_storage_traits::groups::GroupStorage;
 use mdk_storage_traits::groups::error::GroupError;
 use mdk_storage_traits::groups::types::GroupExporterSecret;
-use nostr::RelayUrl;
-
-use super::create_test_group;
🧹 Nitpick comments (1)
crates/mdk-storage-traits/tests/shared/group_tests.rs (1)

283-291: Test updated appropriately for new validation limits.

The change from 1000 to 250 characters correctly aligns with the new MAX_GROUP_NAME_LENGTH = 255 validation. The test now verifies that names within the limit work as expected.

Optional: Consider testing closer to the boundary.

The test uses 250 bytes, which leaves some margin. Consider testing at exactly 255 bytes to ensure the boundary condition works correctly:

🔎 Optional boundary test suggestion
-    // Test saving group with long name (within limits)
-    let long_name = "a".repeat(250); // Within 255 byte limit
+    // Test saving group with name at maximum length
+    let long_name = "a".repeat(255); // Exactly at 255 byte limit

Additionally, consider adding a test case with multi-byte UTF-8 characters to verify that the validation correctly checks byte length rather than character count. For example, a string with 128 two-byte characters (256 bytes total) should be rejected even though it's only 128 characters.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff615dc and 85cdfc3.

📒 Files selected for processing (2)
  • crates/mdk-sqlite-storage/src/validation.rs
  • crates/mdk-storage-traits/tests/shared/group_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/mdk-sqlite-storage/src/validation.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)

**/*.rs: All trait bounds should be written in where clauses rather than inline with type parameters in Rust
Use Self instead of the explicit type name when referring to the type in impl blocks
Derive Debug, Clone, Copy, PartialEq, Eq, and Hash for public types when possible, in that order
Derive Default for public types when there is a reasonable default value
Always use full paths for logging with tracing::<op>!(...) instead of importing and using <op>!(...)
Prefer .to_string() or .to_owned() for &str to String conversion instead of .into() or String::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import the fmt module rather than using full paths when implementing traits from core::fmt or std::fmt
Use relative self:: imports for sub-modules rather than bare module names
Place all use statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoid if let ... { } else { } constructs; use match instead when both branches are non-empty
Use if let ... { } without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g., x.rs) with mod x; declaration rather than inline mod x { .. } blocks, except for tests and benches modules
Use inline mod tests { #[cfg(test)] ... } for test modules rather than creating separate tests.rs files
Use inline mod benches { #[cfg(bench)] ... } for benchmark modules rather than creating separate benches.rs files

Files:

  • crates/mdk-storage-traits/tests/shared/group_tests.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Coverage

@mubarakcoded mubarakcoded force-pushed the 71-issue-ab-unbounded-user-input-stored-in-sqlite-causes-disk-and-cpu-exhaustion branch from 85cdfc3 to 785e442 Compare January 1, 2026 09:03
@github-actions
Copy link

github-actions bot commented Jan 1, 2026

✅ Coverage: 86.12% → 86.28% (+0.16%)

@github-actions
Copy link

github-actions bot commented Jan 1, 2026

✅ Coverage: 86.12% → 86.25% (+0.13%)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/mdk-sqlite-storage/src/validation.rs (1)

26-39: Consider deriving additional traits for ValidationError.

The current derives are minimal (Debug via thiserror::Error). According to coding guidelines, consider adding Clone, PartialEq, and Eq for improved usability in testing and error handling contexts.

🔎 Suggested trait additions
-#[derive(Debug, thiserror::Error)]
+#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
 pub enum ValidationError {

As per coding guidelines, derive order should be: Debug, Clone, Copy, PartialEq, Eq, Hash (omitting Copy and Hash here since String is not Copy and Hash isn't needed).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 785e442 and 77761e8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • crates/mdk-sqlite-storage/Cargo.toml
  • crates/mdk-sqlite-storage/src/groups.rs
  • crates/mdk-sqlite-storage/src/messages.rs
  • crates/mdk-sqlite-storage/src/validation.rs
  • crates/mdk-sqlite-storage/src/welcomes.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/mdk-sqlite-storage/src/welcomes.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust.mdc)

**/*.rs: All trait bounds should be written in where clauses rather than inline with type parameters in Rust
Use Self instead of the explicit type name when referring to the type in impl blocks
Derive Debug, Clone, Copy, PartialEq, Eq, and Hash for public types when possible, in that order
Derive Default for public types when there is a reasonable default value
Always use full paths for logging with tracing::<op>!(...) instead of importing and using <op>!(...)
Prefer .to_string() or .to_owned() for &str to String conversion instead of .into() or String::from()
Order imports in the following sequence: (1) core/alloc/std, (2) external crates, (3) current sub-modules with mod declarations, (4) internal crate modules
Import the fmt module rather than using full paths when implementing traits from core::fmt or std::fmt
Use relative self:: imports for sub-modules rather than bare module names
Place all use statements at the top of the file following the specified import order, never inside functions, methods, or other blocks
Avoid if let ... { } else { } constructs; use match instead when both branches are non-empty
Use if let ... { } without an else clause when a match arm would be intentionally empty
Define sub-modules using separate files (e.g., x.rs) with mod x; declaration rather than inline mod x { .. } blocks, except for tests and benches modules
Use inline mod tests { #[cfg(test)] ... } for test modules rather than creating separate tests.rs files
Use inline mod benches { #[cfg(bench)] ... } for benchmark modules rather than creating separate benches.rs files

Files:

  • crates/mdk-sqlite-storage/src/messages.rs
  • crates/mdk-sqlite-storage/src/validation.rs
  • crates/mdk-sqlite-storage/src/groups.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test Coverage
🔇 Additional comments (8)
crates/mdk-sqlite-storage/Cargo.toml (1)

23-23: LGTM!

The addition of thiserror as a workspace dependency is appropriate for supporting the new ValidationError type introduced in the validation module.

crates/mdk-sqlite-storage/src/groups.rs (3)

14-17: LGTM!

Import order follows the coding guidelines correctly: internal crate modules are imported after external crates.


82-106: Validation logic is correctly implemented.

The validation sequence is well-structured:

  1. Validate string lengths first (name, description)
  2. Serialize admin_pubkeys to JSON
  3. Validate serialized JSON size

This ordering ensures early failure on invalid input before expensive serialization, and prevents unbounded JSON from being written to the database.


368-434: Test coverage effectively validates boundary conditions.

The tests correctly verify that inputs exceeding limits (256 bytes for 255-byte limit, 2001 bytes for 2000-byte limit) trigger validation errors with appropriate error messages.

crates/mdk-sqlite-storage/src/messages.rs (2)

25-48: Validation implementation is correct and well-sequenced.

The validation flow properly checks message content length before proceeding, then validates serialized JSON sizes (tags and event) before database insertion. This prevents unbounded data from reaching the database while maintaining clear error messages for each validation failure.


258-316: Test comprehensively validates content size limits.

The test properly sets up all prerequisites (group creation) and verifies that content exceeding 1 MB triggers a validation error with the expected error message.

crates/mdk-sqlite-storage/src/validation.rs (2)

5-24: Size limits are well-chosen for preventing resource exhaustion.

The constants provide reasonable upper bounds:

  • 1 MB for message content
  • 100 KB for JSON structures (tags, events)
  • 255/2000 bytes for text fields (name/description)
  • 50 KB for admin/relay JSON

These limits balance security (preventing disk/CPU exhaustion) with usability (generous enough for normal use cases).


41-76: Validation functions are correct; note unresolved design discussion.

The inline validation helpers correctly check size limits and produce clear error messages. The functions properly handle UTF-8 byte counting (as documented).

However, there's an unresolved discussion from past review comments (lines 41-76) where @dannym-arx and @erskingardner suggested wrapping ValidationError as a variant within the domain error enums (e.g., GroupError, MessageError) rather than converting to string via .map_err(|e| Error::InvalidParameters(e.to_string())).

Current approach:

validate_string_length(...).map_err(|e| GroupError::InvalidParameters(e.to_string()))?

Suggested alternative:

// Add to GroupError enum:
// ValidationFailed(ValidationError)
validate_string_length(...).map_err(GroupError::ValidationFailed)?

The current string-based approach works but loses type information. Adding a ValidationError variant to each domain error enum would preserve type safety and enable programmatic error handling.

Based on past review comments, should ValidationError be integrated as an error variant in GroupError, MessageError, etc., rather than converted to string?

@github-actions
Copy link

github-actions bot commented Jan 1, 2026

✅ Coverage: 86.12% → 86.3% (+0.18%)

@mubarakcoded mubarakcoded merged commit 6744f18 into master Jan 1, 2026
11 checks passed
@mubarakcoded mubarakcoded deleted the 71-issue-ab-unbounded-user-input-stored-in-sqlite-causes-disk-and-cpu-exhaustion branch January 1, 2026 11:39
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.

Issue AB: Unbounded User Input Stored in SQLite Causes Disk and CPU Exhaustion

3 participants