Security: validate BLE-supplied filenames before any SD access - #34
Merged
Conversation
Closes the path-traversal / FAT-unsafe-filename gap from the code
review. BLE delivered raw command bytes straight into SD.open() /
SD.remove() / '/TRACKS/%s' path building, so anyone in radio range
could read or delete files outside the intended scope (../, absolute
paths) or wedge FAT with reserved characters.
New BirdsEye/filename_validator.{h,cpp}: pure isValidFilename(name,
max_len) — non-empty, <= 24 chars (fits the smallest on-device
filename buffer), no leading dot (kills '.', '..', dotfiles), and a
strict [A-Za-z0-9._-] allow-list (kills '/', '\', ':', wildcards,
spaces, control + high-bit bytes). No slashes means path components
can't form, so '../' traversal is impossible.
Wired into all five filename-bearing BLE commands in bluetooth.ino —
GET, DELETE, TGET, TPUT, TDEL — each rejecting before it touches the
card. GET/DELETE reply ERROR / NOT_FOUND (consistent with existing
failure codes, and doesn't advertise that traversal was detected);
track commands reply TERR:BAD_NAME.
13 host test cases (now 85 total, all green): real DOVEX/track names
accepted, length boundary at max_len, leading-dot + traversal +
slash rejection, Windows-reserved + wildcard rejection, control and
high-bit byte rejection.
Pin Lock deferred per earlier decision — needs coordinated phone-app
work.
Defers nothing else; behavior for valid filenames is unchanged.
This was referenced May 21, 2026
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR #3 on the path to 8/10. Closes the path-traversal / FAT-unsafe filename gap flagged in the code review.
The problem
BLE delivered raw command bytes straight into the filesystem with no sanitization:
GET:<name>→SD.open(name, FILE_READ)at rootDELETE:<name>→SD.remove(name)at rootTGET:/TPUT:/TDEL:→snprintf(path, "/TRACKS/%s", name)So anyone in BLE range (the service is
SECMODE_OPEN) could:..//TRACKS/viaTPUT:../FOO→/TRACKS/../FOO→/FOOon FAT:,*, wildcards, control bytes)The fix
New
BirdsEye/filename_validator.{h,cpp}— a pure, host-testable function:A name is valid iff all of:
max_len(production limitkMaxBleFilenameLen = 24, which fits the smallest on-device filename buffer —trackUploadFilename[25]).(rejects.,.., and dotfiles)[A-Za-z0-9._-]Because
/(and\) are rejected outright, no path components can form, so../traversal is structurally impossible — not just pattern-matched away.Wired into all five filename-bearing BLE commands in
bluetooth.ino, each validating before any SD call:GET:ERRORDELETE:NOT_FOUNDTGET:/TPUT:/TDEL:TERR:BAD_NAMETERR:prefix conventionTests
13 new host cases (built on the PR #2 doctest harness), 85 total, all green:
20240115_1430.dovex) and track names (OKC.json) acceptedmax_lenpasses,max_len+1fails; caller-supplied smaller limits respected.,..,.hidden)../secret,../../etc/passwd)/absolute,sub/dir,back\slash):,*,?,|,",<,>)v1.0.dovex,foo..bar)cmake -S tests -B tests/build && cmake --build tests/build ctest --test-dir tests/build --output-on-failureScope notes
compile-sketchjob is the regression net for the build; the BLE reject paths are covered by the unit tests.Where we are
🤖 Generated with Claude Code
Generated by Claude Code