Skip to content

[codex] Sanitize discovery padding bytes#3519

Merged
jensenpat merged 1 commit into
aethersdr:mainfrom
rfoust:codex/sanitize-discovery-values
Jun 11, 2026
Merged

[codex] Sanitize discovery padding bytes#3519
jensenpat merged 1 commit into
aethersdr:mainfrom
rfoust:codex/sanitize-discovery-values

Conversation

@rfoust

@rfoust rfoust commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Root Cause

Local SmartSDR discovery packets are decoded as UTF-8 and parsed as space-separated key=value tokens, but the parser previously stored most values verbatim. If a discovery packet carried NUL, carriage-return, or other control/padding bytes after a printable value such as turf_region=USA, those bytes survived QString::trimmed() when embedded in the token. The connection dialog then appended radio.turfRegion.trimmed() directly to the available-radio label, so Qt rendered the control bytes as visible placeholder glyphs after USA.

User Impact

The available-radio list could show stray box/control glyphs after otherwise valid discovery metadata. The screenshot symptom was USA followed by several placeholder characters in the local radio list subtitle.

Change Summary

  • Add discovery-value sanitization at the RadioDiscovery parser boundary.
  • Strip ASCII control bytes, including NUL padding and DEL, from parsed scalar values before storing them in RadioInfo.
  • Preserve the existing Flex DEL-as-space convention for gui_client_stations, gui_client_programs, and gui_client_hosts before sanitizing and splitting those list values.
  • Add a focused radio_discovery_test regression case that feeds a padded discovery packet and verifies turf_region is clean while DEL-encoded GUI client fields still decode correctly.

Validation

  • ./build/radio_discovery_test
  • ctest --test-dir build -R radio_discovery_test --output-on-failure
  • git diff --check
  • cmake --build build --target AetherSDR -j4

@rfoust rfoust requested review from a team as code owners June 11, 2026 02:11
Copilot AI review requested due to automatic review settings June 11, 2026 02:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the SmartSDR local discovery parser by sanitizing parsed key=value fields to remove trailing ASCII control/padding bytes (e.g., NUL/CR/DEL) before values are stored in RadioInfo, preventing control-glyph artifacts in the connection dialog UI.

Changes:

  • Added centralized discovery-value sanitization (cleanDiscoveryValue) and list parsing (splitDiscoveryListValue) in RadioDiscovery::parseDiscoveryPacket.
  • Preserved the legacy DEL-as-space decoding behavior for GUI-client list fields before sanitizing/splitting.
  • Introduced a focused regression test (radio_discovery_test) and wired it into CMake/CTest.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
tests/radio_discovery_test.cpp Adds a regression test ensuring padding/control bytes are stripped and DEL-separated GUI client fields still decode properly.
src/core/RadioDiscovery.h Exposes the packet parser to tests via a guarded friend declaration under AETHERSDR_TESTING.
src/core/RadioDiscovery.cpp Implements value sanitization and uses it across scalar and list discovery fields.
CMakeLists.txt Adds and registers the new radio_discovery_test executable, enabling the test-only friend access.

Comment thread tests/radio_discovery_test.cpp

@jensenpat jensenpat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed: sanitization at the parser boundary is correct, DEL-as-space convention preserved for gui_client_* fields, regression test covers the reported padding symptom. CI green.

@jensenpat jensenpat enabled auto-merge (squash) June 11, 2026 02:45
@jensenpat jensenpat merged commit a839c05 into aethersdr:main Jun 11, 2026
6 checks passed

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @rfoust — this is a well-scoped fix in the right place. Sanitizing at the parser boundary (rather than in ConnectionPanel) means every consumer of RadioInfo benefits, the DEL-as-space convention for the gui_client list fields is correctly preserved by decoding before stripping, and the regression test plus the standalone test target follow the existing pattern (client_quindar_test et al.). I verified the root cause holds: QString::trimmed() doesn't treat NUL as whitespace, so turf_region=USA\0\0\r previously survived as USA\0\0. The test's lcDiscovery definition also correctly lands inside namespace AetherSDR, matching the Q_DECLARE_LOGGING_CATEGORY in LogManager.h, so it links cleanly without pulling in LogManager.cpp. All four changed files are within the stated scope.

A few minor observations, none blocking:

  1. Bytes ≥ 0x80 survive the filter. The packet is decoded with QString::fromUtf8() before sanitizing, so non-UTF-8 padding bytes like 0xFF become U+FFFD replacement characters, which pass the code < 0x20 || code == 0x7f check and would still render as placeholder glyphs. If you want to harden this fully, also skip QChar::ReplacementCharacter (0xFFFD) in cleanDiscoveryValue(). The reported symptom was NUL/CR padding, so this is optional.

  2. Parallel-list alignment (edge case, worth knowing). The gui_client_* lists are consumed as parallel arrays downstream (RadioModel::setKnownGuiClients, MainWindowHelpers::buildDisconnectClients pair handles[i] with stations[i]/ips[i]). The new per-item trim-and-drop means an entry that's only control characters now disappears from one list, which could shift indices relative to its siblings. The old code had the mirror-image quirk (garbage placeholder entries), and real radios pad at packet end rather than mid-list, so this is fine as-is — just flagging it since the alignment contract isn't obvious from the parser.

  3. Copilot's include note is valid but minor. The test uses QByteArray and QStringList via transitive includes from RadioDiscovery.h / QCoreApplication. Adding the two explicit includes is cheap insurance against future Qt header reshuffles — worth doing if you push another revision, not worth one on its own.

  4. AETHERSDR_TESTING + friend class is a new pattern in this codebase (existing tests exercise public APIs). It's a reasonable, contained way to reach the private parser, and the #ifdef keeps it out of production builds — no objection, just noting it's precedent-setting if other tests copy it.

Nice catch on the screenshot symptom, and thanks for the focused regression test.


🤖 aethersdr-agent · cost: $10.3019 · model: claude-fable-5

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.

3 participants