Skip to content

fix(memory): panic-safe truncate + DRY validate_relation + rmcp 2.0.0 security upgrade - #1289

Closed
cyberlife-coder wants to merge 4 commits into
developfrom
claude/admiring-fermi-y7szun
Closed

cyberlife-coder wants to merge 4 commits into
developfrom
claude/admiring-fermi-y7szun

Conversation

@cyberlife-coder

@cyberlife-coder cyberlife-coder commented Jun 30, 2026 •

Copy link
Copy Markdown
Owner

Description

Three atomic fixes to velesdb-memory, each addressing a distinct code-quality issue found during a review of open PRs (PR #1278 — rmcp upgrade) and the code landed in develop.


Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • 🔧 Refactoring (no functional changes)

Changes

1. fix(memory): prevent panic in truncate() when UTF-8 straddles byte 120

File: crates/velesdb-memory/src/extract.rs

The previous implementation added words until out.len() >= 120, then called out.truncate(120). If the last word pushed a multi-byte UTF-8 character (e.g. a 3-byte kanji at bytes 118–120), String::truncate() would panic — it requires a valid char boundary, and continuation bytes (0x80–0xBF) are never one.

Fix: Check before pushing a word whether adding it would push the output past 120 bytes. If it would, break without touching out. This preserves the <= 120-byte invariant at all times, making out.truncate() unnecessary and the panic impossible.

The function is an internal error-message preview (used in the extract feature). Stopping at a word boundary is the correct semantic here.

2. refactor(memory): remove redundant is_ascii() guard in validate_relation

File: crates/velesdb-memory/src/service.rs

// Before
if label.chars().any(|c| c.is_ascii() && c.is_ascii_control()) {

// After
if label.chars().any(|c| c.is_ascii_control()) {

char::is_ascii_control() returns false for all non-ASCII codepoints (U+0080+), so the c.is_ascii() guard is always redundant — it adds no filter and no protection. DRY simplification; semantics are identical.

3. chore(deps): upgrade rmcp 1.8.0 → 2.0.0 (security fixes)

Files: crates/velesdb-memory/Cargo.toml, Cargo.lock

rmcp 2.0.0 ships three security patches:

The breaking API changes in 2.0.0 (new Audio variant in PromptMessageContent, MCP 2025-11-25 spec alignment) do not affect velesdb-memory: the server only uses the tool-call surface (tool_router / tool macros / transport-io), not the prompt-message types that changed.

This supersedes dependabot PR #1278, which was blocked by the freshness check because develop had advanced after the bot opened it.


How Has This Been Tested?

  • Unit tests — cargo test -p velesdb-memory — all 56 tests pass (service BDD, TTL BDD, why BDD, schema tests, MCP server integration tests)
  • Compile check — cargo check -p velesdb-memory — no warnings

Test Configuration:

  • OS: Linux (Ubuntu 24.04)
  • Rust version: stable (1.89)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes

Additional Notes

The truncate panic (fix #1) could only be triggered in production when:

  1. The extract feature is compiled in
  2. Ollama returns output containing non-ASCII characters (CJK, emoji, etc.)
  3. The content happens to have a multi-byte character straddling byte position 120

Low probability but deterministic once conditions align — classified as a correctness bug.

Date: 2026-06-30

claude added 3 commits June 30, 2026 07:10
`char::is_ascii_control()` already returns false for non-ASCII codepoints
(U+0080 and above), so the preceding `c.is_ascii()` check is always true
when `is_ascii_control()` returns true — it adds no additional filter.
Removing it is a DRY simplification with identical runtime behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3iesVNNipaTkcSyHok1aq
The previous implementation added words until out.len() >= 120, then
called out.truncate(120). If the last word added pushed a multi-byte
UTF-8 character to straddle byte position 120 (e.g. a 3-byte kanji at
bytes 118-120), String::truncate() would panic — it requires a valid
char boundary and 0x80-0xBF continuation bytes are never one.

Fix: check BEFORE pushing a word whether it would push the total past
LIMIT. If it would, break without modifying `out`. This preserves the
<= 120-byte invariant without any post-hoc byte truncation, so no
char-boundary panic is possible regardless of input encoding.

The function is internal (error-message preview for the extract feature),
so word-boundary rounding is the correct semantic: partial words in error
messages are confusing; stopping cleanly at the last fitting word is not.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3iesVNNipaTkcSyHok1aq
rmcp 2.0.0 ships three security-relevant patches:
- Prevent OAuth resource spoofing (rust-sdk#937)
- Block OAuth metadata SSRF (rust-sdk#935)
- Prevent streamable HTTP session leak (rust-sdk#934)

Breaking-API changes in 2.0.0 (new Audio variant in PromptMessageContent,
MCP 2025-11-25 spec alignment) do not affect velesdb-memory: the server
only uses the tool-call surface (tool_router / tool macros / transport-io),
not the prompt-message types that changed. All tests green.

Supersedes dependabot PR #1278, which was blocked by freshness check
because develop advanced after the bot opened it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3iesVNNipaTkcSyHok1aq
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution to VelesDB!

Before this PR can be merged, please sign the Contributor License Agreement by posting the following comment:

I have read the CLA Document and I hereby sign the CLA

This grants the project a license to use your contribution under the VelesDB Core License 1.0 and future licensing terms.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity

Metric Results
Complexity 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Replace `if out.is_empty() { 0 } else { 1 }` with `usize::from(!out.is_empty())`
as suggested by clippy::bool-to-int-with-if (-D warnings); guard updated to
`if !out.is_empty()` for consistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K3iesVNNipaTkcSyHok1aq
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.

2 participants