Strip internal forensics finding references (F06, N07, NEW-C, R02)
from docstrings, comments, and assertion messages. Descriptive text
is preserved — only the ID prefixes are removed.
Strip G1/ prefix from docstrings, _g1_ from function names,
and G1 from section comments. Finding IDs (F06, N07, etc.)
are preserved as they are meaningful standalone references.
A BATTERY frame with len(data) < 3 caused dbuf.read(2) to return short
bytes; int.from_bytes(b"", ...) silently yielded 0, propagating a bogus
level=0 to HA sensors. Same silent-zero class as N07 (storage fields).
Option B: early-return with debug log, matching the NEW-C pattern for
STATUS_RESPONSE. No BATTERY event is dispatched for malformed frames.
Not in the original forensics report — discovered during G1 N07 work and
logged in issues_log.md. Resolved here because no later branch touches
this handler.
Files changed:
- src/meshcore/reader.py: add `if len(data) < 3: return` guard before
the level read in the BATTERY branch
- tests/unit/test_reader.py: add test_g1_battery_too_short_for_level —
sends a 1-byte frame (type only), asserts no BATTERY event dispatched
and debug log emitted
Adds the four unit tests required by proposal §4.1 verification:
(a) test_g1_handle_rx_malformed_frame_logged_and_swallowed — F06.
Sends a 4-byte CHANNEL_MSG_RECV_V3 frame missing the channel_idx
byte. The handler raises IndexError on dbuf.read(1)[0]; the F06
umbrella must catch it, log "handle_rx parse error" with a
traceback, and return cleanly without dispatching any event.
(b) test_g1_battery_short_frame_omits_storage_fields — N07.
Sends a 3-byte BATTERY frame (type + 2-byte level only). Verifies
that exactly one BATTERY event is dispatched, the payload contains
`level` but does NOT contain `used_kb` or `total_kb`. Pre-fix the
`len(data) > 3` gate would have produced both fields with bogus
silent zeros.
(c) test_g1_status_response_short_frame_skipped — NEW-C.
Sends a 30-byte STATUS_RESPONSE push frame (well below the 60-byte
minimum). Verifies that no STATUS_RESPONSE event is dispatched and
the new "STATUS_RESPONSE push frame too short" debug log fires.
(d) test_g1_parse_packet_payload_txt_type_decodes_high_bits — R02.
Sets up a synthetic channel with a known 16-byte AES key, encrypts
a 16-byte plaintext where byte 4 = (5 << 2) | 1 = 0x15, builds the
full pkt_payload (chan_hash + cipher_mac + ciphertext), wraps it in
a minimal route header, calls parsePacketPayload directly, and
asserts log_data["txt_type"] == 5 and log_data["attempt"] == 1.
Pre-R02-fix `uncrypted[4:4]` was the empty slice, so txt_type was
always 0 — this test would have failed with txt_type=0.
Adds a small _CapturingDispatcher helper class. Adds imports for
logging at the top of the file. The existing test_binary_response is
left untouched.
File: tests/unit/test_reader.py
- Update mock dispatcher to use subscribe-before-send pattern matching
the rewritten CommandHandler.send() method
- Use 32-byte pubkeys in tests for commands that now require
prefix_length=32 (login, logout, statusreq, reset_path, share/export/remove contact)
- Fix send_trace test path format to match flags=1 (2-byte path hashes)
- Update LPP current test to expect signed wrap for values > 32.767
- Fix BinaryReqType import (moved from meshcore.parsing to meshcore.packets)
- Fix register_binary_request call signature (added pubkey_prefix param)
- Update timeout test to expect 'no_event_received' instead of 'timeout'
Refactored the BinaryCommandHandler to align with the other command handlers, inheriting from CommandHandlerBase. This resolves an AttributeError and simplifies the command structure. Moved binary_commands.py into the commands module. Applied fixes to the BLE connection handler based on feedback, improving reliability on macOS and ensuring the device address is correctly handled.