Commit Graph

21940 Commits

Author SHA1 Message Date
J. Nick Koston 380a669377 Merge branch 'cleanup/remove-dead-host-method' into integration 2026-02-08 08:24:03 -06:00
J. Nick Koston 9299d83eeb Remove unused host() method from AsyncWebServerRequest
Dead code with no callers. Also avoids an unchecked .value() on
optional<string> that would crash on HTTP/1.0 requests without
a Host header.
2026-02-08 08:23:27 -06:00
J. Nick Koston 381f4e9f34 Revert "Remove unused host() method from AsyncWebServerRequest"
This reverts commit 99b7996759.
2026-02-08 08:23:04 -06:00
J. Nick Koston 99b7996759 Remove unused host() method from AsyncWebServerRequest
Dead code with no callers. Also avoids an unchecked .value() on
optional<string> that would crash on HTTP/1.0 requests without
a Host header.
2026-02-08 08:22:24 -06:00
J. Nick Koston 31ca8984ed Merge branch 'web_server_idf_heap' into integration 2026-02-08 08:05:13 -06:00
J. Nick Koston 02687615b3 Merge upstream/dev into web_server_idf_heap
Resolve conflict in utils.cpp: use query_len parameter (from dev)
instead of query_url.size() (query_url is const char*, not string).
2026-02-08 08:04:57 -06:00
J. Nick Koston b283f1ae75 Merge all hardening branches into integration
Resolve conflict in web_server_idf.cpp: keep constant-time compare
logic adapted to stack-allocated digest buffer from integration.
2026-02-08 08:03:23 -06:00
J. Nick Koston 2ceb6ee95b Add comment explaining Windows-specific multiple_dots behavior
On Windows, Path.resolve() treats '....' as parent traversal (403),
while on Unix it is a literal directory name that stays inside the
base directory (404).
2026-02-08 07:55:48 -06:00
J. Nick Koston 4cdd73904f Encode usernames as UTF-8 bytes for hmac.compare_digest
hmac.compare_digest() on str inputs raises TypeError if either
contains non-ASCII characters. Encode both sides as UTF-8 bytes.
Add test with non-ASCII username to prevent regressions.
2026-02-08 07:49:53 -06:00
J. Nick Koston b8cad678b1 URL-encode whitespace in empty file name test parameter
Replace raw spaces with %20%20 to avoid flakiness from HTTP clients
handling unencoded spaces differently.
2026-02-08 07:48:41 -06:00
J. Nick Koston 5c5bf50e49 Update test docstring to reflect validation instead of sanitization 2026-02-08 07:41:00 -06:00
J. Nick Koston f2e1c2c650 Derive provided_len from string size and bound loop to digest_len
- Use auth.value().size() instead of strlen() to avoid rescanning
  attacker-controlled header content.
- Iterate over digest_len (expected length) instead of max_len so a
  long Authorization header cannot force extra work. The full-width
  length XOR already rejects any length mismatch.
2026-02-08 07:39:09 -06:00
J. Nick Koston 401d3c2056 Fix idedata test mock to use Path instead of str
The test set mock_image.path to str, but FlashImage.path is a Path.
This masked a pre-existing bug where Path.endswith() doesn't exist.
Fix the mock to match the real type so as_posix() works correctly.
2026-02-08 07:36:02 -06:00
J. Nick Koston 30662bc11b Update module docstring to reflect auth test coverage 2026-02-08 07:34:14 -06:00
J. Nick Koston b650d2df31 Reject empty file names and fix FlashImage.path endswith call
- Return 400 for empty or whitespace-only file_name to prevent the
  idedata fallback from matching everything via empty-string suffix.
- Use image.path.as_posix().endswith() since FlashImage.path is a Path
  object which does not have a string endswith method.
- Add parametrized test for empty/whitespace file name values.
2026-02-08 07:32:00 -06:00
J. Nick Koston bb2d7c9742 Use full-width accumulator and iterate max_len in constant-time compare
- Change result accumulator from volatile uint8_t to volatile size_t
  to prevent truncation bypass (e.g. digest_len + 256 XOR).
- Iterate over max(digest_len, provided_len) so trailing bytes in
  either string are also compared.
2026-02-08 07:25:50 -06:00
J. Nick Koston 4795971f1c Use usefixtures for tests that don't reference mock_auth_settings
Replace unused mock_auth_settings parameter with
@pytest.mark.usefixtures decorator to avoid PLW0613 lint warnings.
2026-02-08 07:25:19 -06:00
J. Nick Koston ea99593575 Build auth_settings on dashboard_settings and monkeypatch env
- Refactor auth_settings fixture to extend dashboard_settings instead
  of duplicating setup.
- Explicitly clear DISABLE_HA_AUTHENTICATION in HA add-on test to
  prevent order-dependent flakiness.
2026-02-08 07:24:44 -06:00
J. Nick Koston bf7ede1d43 Make mock_token_hex strict on unexpected nbytes
Raise ValueError for unexpected nbytes values so tests fail clearly
if production code starts calling token_hex with an incorrect size.
2026-02-08 07:24:11 -06:00
J. Nick Koston 43448d55f1 Guard against None firmware_bin_path and mock subprocess in tests
- Add None check for storage_json.firmware_bin_path before computing
  base_dir (covers configs from StorageJSON.from_wizard()).
- Mock async_run_system_command in path traversal tests so paths that
  pass validation but don't exist return 404 deterministically.
- Add test for firmware_bin_path=None case.
2026-02-08 07:23:24 -06:00
J. Nick Koston e362e6fe2f Fix multiple_dots test for Windows path resolution
On Windows, ....//secrets.yaml escapes the base directory (403),
while on Unix it stays inside (404). Use sys.platform to set the
expected status code per platform.
2026-02-08 07:22:08 -06:00
J. Nick Koston 82d9616f1b Add explicit binascii.Error catch and bad-padding test
binascii.Error is already a subclass of ValueError, but listing it
explicitly makes the intent clear. Added test for incorrect base64
padding (e.g. "Basic abc").
2026-02-08 07:18:29 -06:00
J. Nick Koston 999774889d Add comments explaining constant-time comparison logic 2026-02-08 07:17:24 -06:00
J. Nick Koston c90ca4df87 Use encoder output length and remove early return on length mismatch
Use the out length from esp_crypto_base64_encode instead of strlen.
Fold length mismatch into the accumulator to make comparison fully
constant-time without early return.
2026-02-08 07:16:19 -06:00
J. Nick Koston a167332518 Fix password_hash type and add HA add-on regression test
Initialize password_hash as b"" (bytes) to match password_hash()
return type, preventing TypeError in hmac.compare_digest when
HA add-on auth is enabled without a password.
2026-02-08 07:14:20 -06:00
J. Nick Koston 1b7efdd051 Match cnonce length to hash algorithm digest size
Use nonce_size // 2 as token_hex argument so MD5 auth produces
a 32-char cnonce and SHA256 auth produces a 64-char cnonce,
matching the original protocol behavior.

Rename mock_random fixture to mock_token_hex and use separate
mock cnonce constants per hash algorithm.
2026-02-08 07:11:56 -06:00
J. Nick Koston caff93d7b8 Add test coverage for secrets.choice in fallback PSK generation
Verifies that wizard_file uses secrets.choice (not random.choice)
to generate the 12-character fallback hotspot password.
2026-02-08 07:08:25 -06:00
J. Nick Koston 806a86a6ad Add test coverage for is_authenticated base64 handling
Tests malformed base64, invalid UTF-8, missing colon separator,
valid credentials, wrong credentials, and auth-disabled cases.
2026-02-08 07:06:24 -06:00
J. Nick Koston 42126bae72 Add test coverage for check_password
Tests correct credentials, wrong password, wrong username,
both wrong, and auth-disabled cases.
2026-02-08 07:03:29 -06:00
J. Nick Koston 803b9a7a18 Update path traversal tests for resolve/relative_to behavior
Real traversals that escape the base directory now return 403.
Paths like '....' that resolve inside the base directory but
don't exist return 404.
2026-02-08 07:01:37 -06:00
J. Nick Koston a8fd6c132e Update tests to mock secrets.token_hex instead of random.random
The cnonce generation was changed to use secrets.token_hex(32),
so the test mocks and assertions need to match.
2026-02-08 06:59:32 -06:00
J. Nick Koston 1dcffdc872 [web_server_idf] Use constant-time comparison for Basic Auth
Replace strcmp() with a constant-time XOR accumulation loop
for comparing base64-encoded credentials in HTTP Basic Auth.
2026-02-08 06:51:52 -06:00
J. Nick Koston a40c87eeed [dashboard] Use resolve/relative_to for download path validation
Replace string-based path sanitization (.replace/.lstrip) with
Path.resolve() and relative_to() validation, matching the
pattern used by other dashboard endpoints (e.g. settings.rel_path).

The previous approach was not exploitable but was inconsistent
with the rest of the codebase.
2026-02-08 06:48:38 -06:00
J. Nick Koston 2829f7b485 [dashboard] Handle malformed Basic Auth headers gracefully
Wrap base64 decode and split in try/except so malformed
Authorization headers return a clean 401 instead of an
unhandled exception producing a 500 response with stack
trace in logs.

Catches ValueError (covers binascii.Error from b64decode)
and UnicodeDecodeError (from .decode()).
2026-02-08 06:47:49 -06:00
J. Nick Koston 79a205eee2 [dashboard] Use constant-time comparison for username check
Use hmac.compare_digest() for the username comparison to match
the existing constant-time password comparison. This prevents
username enumeration via timing analysis.
2026-02-08 06:46:28 -06:00
J. Nick Koston e039676422 [wizard] Use secrets module for fallback AP password generation
Replace random.choice() with secrets.choice() for generating
the fallback hotspot password. The random module uses Mersenne
Twister which is not cryptographically secure. The secrets
module is the correct choice for credential generation.

The file already imports secrets for other credential generation.
2026-02-08 06:44:45 -06:00
J. Nick Koston 9616596146 [ota] Use secrets module for OTA authentication cnonce
Replace random.random() with secrets.token_hex() for generating
the client nonce in OTA challenge-response authentication.

The random module uses Mersenne Twister which is not
cryptographically secure. The secrets module is the correct
choice for security-sensitive token generation.
2026-02-08 06:40:30 -06:00
J. Nick Koston ad546edbb7 fix 2026-02-08 03:31:06 -06:00
J. Nick Koston a07b429510 Merge branch 'hlk_fm22x_eliminate_heap_alloc' into integration 2026-02-08 03:28:50 -06:00
J. Nick Koston 94d6ca22b2 Merge branch 'voice-assistant-timer-vector' into integration 2026-02-08 03:28:43 -06:00
J. Nick Koston 78f98fa08f [hlk_fm22x] Drain exact frame bytes on oversize response
Discard exactly length+1 (payload + checksum) instead of flushing
the entire RX buffer, which could eat bytes from the next frame.
2026-02-08 02:51:27 -06:00
J. Nick Koston eb3bad823b [hlk_fm22x] Reword comment to avoid lint false positive on 'byte' 2026-02-08 02:40:04 -06:00
J. Nick Koston 5374252470 [hlk_fm22x] Add bounds checks and fix format specifiers
- Flush UART RX buffer when response exceeds max size
- Guard handle_note_ against zero-length data
- Guard handle_reply_ against length < 2
- Validate VERIFY response has full name payload before access
- Guard GET_VERSION against length underflow
- Cast %.*s precision to int, use %zu for size_t
- Improve MAX_RESPONSE_SIZE comment with payload layout
2026-02-08 02:38:40 -06:00
J. Nick Koston f7630075ff Fix Trigger/Automation type mismatch for timer_tick_trigger_ 2026-02-08 02:32:03 -06:00
J. Nick Koston 8677f3db03 Add timer automation tests for voice_assistant 2026-02-08 02:27:23 -06:00
J. Nick Koston 9add30b900 Pass timer tick vector as const ref through automation to avoid copy 2026-02-08 02:26:22 -06:00
J. Nick Koston 2e50651400 [hlk_fm22x] Replace per-cycle vector allocation with member buffer
Replace std::vector<uint8_t> in recv_command_() with a member
std::array<uint8_t, 36> buffer to eliminate heap allocation on
every polling cycle. Also use pointer+length instead of vector
references in handle_note_/handle_reply_, and use TextSensor's
publish_state(const char*, size_t) overload to avoid temporary
std::string construction for version and face name publishing.
2026-02-08 02:17:22 -06:00
J. Nick Koston bfee24421a Merge branch 'voice-assistant-timer-vector' into integration 2026-02-08 02:07:34 -06:00
J. Nick Koston 86fee6e4af [voice_assistant] Replace timer unordered_map with vector to eliminate per-tick heap allocation 2026-02-08 02:02:55 -06:00
J. Nick Koston 48ea97139e Merge branch 'web_server_idf_namespace' into integration 2026-02-08 01:47:01 -06:00