Skip to content

Treat an empty password as valid, distinct from unset - #17

Merged
mullinmax merged 1 commit into
mainfrom
claude/allow-empty-password
Jul 18, 2026
Merged

Treat an empty password as valid, distinct from unset#17
mullinmax merged 1 commit into
mainfrom
claude/allow-empty-password

Conversation

@mullinmax

Copy link
Copy Markdown
Contributor

Summary

Some Vector boards have no password configured, which the firmware authenticates by signing with an empty-string HMAC key. The client currently conflates an empty password ("") with "no password set" (None), so an empty password can never authenticate:

  • Machine.password did return self._password or os.environ.get(...), so an explicit "" was coerced into the $VECTOR_PASSWORD fallback.
  • Machine._preflight_auth and HttpTransport._send both rejected a falsy password (if not self.password: raise), so even if "" reached them it raised AuthenticationRequiredError — yet auth.sign("", ...) produces a perfectly valid signature the firmware accepts.

This distinguishes None (unset) from "" (a valid empty password):

  • Machine.password returns an explicitly-set empty string as-is; only None falls back to $VECTOR_PASSWORD.
  • Machine._preflight_auth and HttpTransport._send guard on is None instead of falsiness.

Unset behavior is unchanged: with no password configured (and no env var), authenticated routes still raise AuthenticationRequiredError before any traffic.

Testing

Added tests:

  • test_http.py::test_empty_password_is_valid_and_signs — an empty password fetches a challenge and signs with an empty HMAC key.
  • test_machine.py::test_empty_password_passes_preflight — authenticated routes work with password="".
  • test_machine.py::test_empty_password_not_overridden_by_env — an explicit "" wins over $VECTOR_PASSWORD.

python -m pytest tests/test_machine.py tests/test_http.py tests/test_auth.py tests/test_connect.py passes. (The two test_usb.py failures in this environment are pre-existing and unrelated — they require the optional pyserial dependency, which isn't installed here.)

Context

Found while wiring up empty-password support in the Memory Mapper tool (warped-pinball/memory-mapper#34), which currently has to sign empty-password requests itself to work around this. With this fix that workaround can be dropped and the high-level Machine methods used directly.

🤖 Generated with Claude Code

https://claude.ai/code/session_014Y49XvgGpmTHrYLDeTvmbA


Generated by Claude Code

Some Vector boards have no password configured, which the firmware
authenticates by signing with an empty-string HMAC key. The client
conflated an empty password with "no password set": the Machine.password
property coerced "" to the $VECTOR_PASSWORD fallback, and both the auth
preflight and the HTTP transport rejected a falsy password outright, so an
empty password could never be used.

Distinguish None (unset — still falls back to the environment and raises
AuthenticationRequiredError when nothing is configured) from "" (a valid
empty password that is signed as such):

- Machine.password returns an explicitly-set empty string instead of the
  env fallback; only None falls back.
- Machine._preflight_auth and HttpTransport._send guard on `is None`
  rather than falsiness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y49XvgGpmTHrYLDeTvmbA
@github-actions

Copy link
Copy Markdown

Coverage report

Total coverage: 100.00% (0.00% vs base 100.00%)

@mullinmax
mullinmax marked this pull request as ready for review July 18, 2026 20:19
@mullinmax
mullinmax merged commit 2f6814d into main Jul 18, 2026
11 checks passed
@mullinmax
mullinmax deleted the claude/allow-empty-password branch July 18, 2026 20:19
mullinmax added a commit that referenced this pull request Jul 18, 2026
* Treat an empty password as valid, distinct from unset

Some Vector boards have no password configured, which the firmware
authenticates by signing with an empty-string HMAC key. The client
conflated an empty password with "no password set": the Machine.password
property coerced "" to the $VECTOR_PASSWORD fallback, and both the auth
preflight and the HTTP transport rejected a falsy password outright, so an
empty password could never be used.

Distinguish None (unset — still falls back to the environment and raises
AuthenticationRequiredError when nothing is configured) from "" (a valid
empty password that is signed as such):

- Machine.password returns an explicitly-set empty string instead of the
  env fallback; only None falls back.
- Machine._preflight_auth and HttpTransport._send guard on `is None`
  rather than falsiness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y49XvgGpmTHrYLDeTvmbA

* Require a version bump on every PR; bump to 0.2.2

The empty-password fix (#17) merged without a version bump, so main could
ship a behavior change under the already-released 0.2.1. Add a CI gate that
fails a pull request unless it raises __version__ above the base branch, and
bump to 0.2.2 to cover the merged fix.

- scripts/check_version_bump.py compares the head __version__ against the
  base branch's (read from git) and requires head > base; a missing base
  file (new package) passes. PEP 440 ordering via packaging when available,
  with a numeric-plus-suffix fallback.
- ci.yml gains a version-bump job (pull_request only) that runs it against
  the PR base sha.
- Unit tests cover the comparison logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y49XvgGpmTHrYLDeTvmbA

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant