Skip to content

fix(meshcore): preserve parentheses and emoji in device name on save (#3450)#3451

Merged
Yeraze merged 1 commit into
mainfrom
fix/meshcore-name-emoji
Jun 14, 2026
Merged

fix(meshcore): preserve parentheses and emoji in device name on save (#3450)#3451
Yeraze merged 1 commit into
mainfrom
fix/meshcore-name-emoji

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #3450. Editing a MeshCore companion's Device Name (Source → Configuration) silently stripped parentheses, emoji, and other Unicode the moment Save was pressed.

Root cause

MeshCoreManager.sanitizeName() used an allow-list:

name.replace(/[^a-zA-Z0-9\s\-_]/g, '').substring(0, 32)

That deletes anything outside [A-Za-z0-9 _-] — so (, ), and emoji were dropped. (The route just .trim()s and the frontend sends raw input, so this was the only stripping point.)

Fix

Rework sanitizeName to drop only control characters (C0 controls + DEL) by code point — which still removes CR/LF/tab that would break the line-based repeater serial CLI (set name <name>) — while preserving printable Unicode (parentheses, emoji, punctuation). The result is capped to the device's 32-byte name field by UTF-8 byte length, truncating on a code-point boundary so a multi-byte character is never split. (For ASCII names this is identical to the old 32-char cap — no regression.)

Testing

New meshcoreManager.deviceName.test.ts: parentheses + emoji preserved, control chars (CR/LF/tab/NUL) stripped, surrounding whitespace trimmed, 32-byte cap without splitting a multi-byte emoji, and empty-after-strip rejection.

Full Vitest suite: 6395 passing, 0 failures.

🤖 Generated with Claude Code

…3450)

The MeshCore name sanitizer used an `[a-zA-Z0-9\s\-_]` allow-list, so saving a
companion's Device Name silently deleted parentheses, emoji, and other Unicode.

Rework sanitizeName to drop ONLY control characters (C0 + DEL) by code point —
this still removes CR/LF/tab that would break the line-based repeater serial CLI
(`set name <name>`) — while preserving printable Unicode. The result is capped
to the 32-byte device name field by UTF-8 byte length, truncating on a
code-point boundary so a multi-byte character is never split.

Tests cover: parentheses + emoji preserved, control chars stripped, whitespace
trimmed, 32-byte cap without splitting a multi-byte char, and empty-after-strip
rejection. Full suite: 6395 passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review Complete ✅

I've completed my review of this pull request. Here's my comprehensive feedback:

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Review modified meshcoreManager.ts implementation
  • Review new test file for coverage completeness
  • Analyze code quality and best practices
  • Check for potential bugs or issues
  • Assess performance considerations
  • Evaluate security concerns
  • Provide comprehensive feedback

🟢 Overall Assessment

This is a well-implemented fix that properly addresses the Unicode stripping issue while maintaining security and robustness. The code quality is high and follows the repository's conventions.

✅ Code Quality & Best Practices

Strengths:

  • Clean separation of concerns: The sanitizeName() method has a single responsibility with clear documentation
  • Proper Unicode handling: Uses Array.from() and codePointAt() to correctly handle surrogate pairs and astral characters
  • Smart byte-level truncation: The truncateUtf8Bytes() helper ensures multi-byte characters aren't split
  • Comprehensive documentation: JSDoc clearly explains the rationale and behavior change from MeshCore companion node name strips parentheses and emoji on save #3450
  • Error handling: Appropriately throws when name becomes empty after sanitization

Minor observations:

  • The one-liner filter could be split for readability, but it's functionally correct
  • Method placement and visibility are appropriate (private static for utility)

🔒 Security Considerations

✅ Excellent security posture:

  • Control character filtering: Correctly removes C0 controls (0x00-0x1F) and DEL (0x7F) that could break the serial CLI interface
  • Input validation: Rejects empty names after sanitization
  • No injection risks: The filtering approach prevents command injection through newlines/control chars
  • Maintains essential security: Still prevents malicious control characters while allowing legitimate Unicode

🚀 Performance Considerations

✅ Performance is appropriate:

  • Efficient Unicode iteration: Array.from() with codePointAt() is the correct approach for Unicode strings
  • Minimal overhead: Only processes characters once during filtering
  • Reasonable for use case: Device name setting is infrequent, so the performance is suitable
  • Byte-length optimization: Buffer.byteLength() checks avoid unnecessary work

🧪 Test Coverage

✅ Excellent test coverage:

The new test suite (meshcoreManager.deviceName.test.ts) comprehensively covers:

  • Core functionality: Parentheses and emoji preservation
  • Edge cases: Control character stripping (CR/LF/tab/NUL)
  • Boundary conditions: 32-byte UTF-8 truncation without character splitting
  • Error conditions: Empty name rejection
  • Real-world scenarios: Practical device name patterns

Test quality highlights:

🐛 Potential Issues

🟡 Minor considerations:

  1. Unicode normalization: The code doesn't normalize Unicode forms (NFC vs NFD). This is probably fine for device names, but worth noting.

  2. Documentation clarity: Consider adding a brief inline comment explaining the codepoint ranges (0x1f and 0x7f) for future maintainers:

    // Remove C0 controls (0x00-0x1F) and DEL (0x7F)
    const c = ch.codePointAt(0); 
    return c !== undefined && c > 0x1f && c !== 0x7f;
  3. Error message consistency: The error message format should align with other validation errors in the codebase (though this may already be consistent).

📋 Recommendations

  1. ✅ Ready to merge - This is a solid fix that addresses the issue correctly
  2. Consider: Adding the inline comment mentioned above for maintainability
  3. Validate: Ensure CI passes completely (test suite reported 6395 passing)

🎯 Summary

This PR successfully fixes the Unicode stripping issue while maintaining security and adding robust test coverage. The implementation correctly balances user experience (preserving emoji/parentheses) with system security (removing control characters). The code follows TypeScript and repository best practices and includes comprehensive tests.

Verdict: ✅ APPROVE - Well-implemented fix with excellent test coverage.


@Yeraze Yeraze merged commit e79b0ff into main Jun 14, 2026
19 checks passed
@Yeraze Yeraze deleted the fix/meshcore-name-emoji branch June 14, 2026 10:40
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.

MeshCore companion node name strips parentheses and emoji on save

1 participant