fix(auth): match the Bearer scheme case-insensitively (RFC 6750)#929
Merged
Conversation
`require_bearer` parsed the Authorization header with
`strip_prefix("Bearer ")`, which is case-sensitive. Per RFC 6750 §2.1 /
RFC 7235 §2.1 the auth-scheme is case-insensitive, so a correct token sent
as `Authorization: bearer <token>` (or `BEARER`, or with extra whitespace)
was rejected with a confusing "invalid bearer token" 401 — needless friction
when setting up `RUVIEW_API_TOKEN` (the active #864/#924 theme).
Now the scheme is matched with `eq_ignore_ascii_case` and leading token
whitespace trimmed. The token comparison itself is unchanged — still exact
and constant-time (`ct_eq`) — so this does not weaken auth: a wrong token or
a non-Bearer scheme (`Basic …`) still returns 401.
New test `accepts_case_insensitive_bearer_scheme` covers `bearer`/`BEARER`/
extra-space (accept) and wrong-token/`Basic` (still reject). bearer_auth
suite: 9 passed.
Co-Authored-By: claude-flow <ruv@ruv.net>
ruvnet
added a commit
that referenced
this pull request
Jun 3, 2026
… action (#930) Two real problems in the Static Application Security Testing job: 1. **It scanned a path that no longer exists.** `bandit -r src/` and `semgrep … src/` pointed at the repo-root `src/`, but the Python code moved to `archive/v1/src/` (64 .py files) when the runtime was rewritten in Rust. So the SAST scan matched nothing — a silent no-op (this is also why `bandit-results.sarif` was "Path does not exist" on recent runs). Fixed both to `archive/v1/src/`. 2. **Deprecated + redundant + flaky semgrep step.** The `returntocorp/semgrep-action@v1` step pulled `returntocorp/semgrep-agent:v1` from Docker Hub every run (intermittently timing out → red check, e.g. on #929) and is EOL. It was redundant: the pip `semgrep --sarif` step is what feeds GitHub Security; the action only pushed to the Semgrep cloud app via SEMGREP_APP_TOKEN. Removed it and folded its `p/docker` + `p/kubernetes` rulesets into the pip semgrep command, so coverage is preserved with no Docker pull. The job stays `continue-on-error: true` (non-gating). YAML validated.
ruvnet
added a commit
that referenced
this pull request
Jun 3, 2026
Per the CLAUDE.md pre-merge checklist (item 5, "Add entry under [Unreleased]"), several recently-merged PRs landed without CHANGELOG entries. Backfilling the user/operator-facing ones — most importantly the MAT triage safety fix: - #926 (Security/safety): survivor with a heartbeat never triaged Deceased - #918: per-node HA devices report each node's own presence/motion - #919: actionable --model load diagnostic (refs #894) - #920: --export-rvf no longer silently produces a placeholder model - #929 (Security): bearer scheme matched case-insensitively (RFC 6750) CI-internal fixes (#925 rust-cache, #930 SAST) are intentionally omitted — they don't change product behavior. Docs-only.
ruvnet
added a commit
that referenced
this pull request
Jun 3, 2026
* fix(ci): SAST actually scans the code + drop deprecated flaky semgrep action Two real problems in the Static Application Security Testing job: 1. **It scanned a path that no longer exists.** `bandit -r src/` and `semgrep … src/` pointed at the repo-root `src/`, but the Python code moved to `archive/v1/src/` (64 .py files) when the runtime was rewritten in Rust. So the SAST scan matched nothing — a silent no-op (this is also why `bandit-results.sarif` was "Path does not exist" on recent runs). Fixed both to `archive/v1/src/`. 2. **Deprecated + redundant + flaky semgrep step.** The `returntocorp/semgrep-action@v1` step pulled `returntocorp/semgrep-agent:v1` from Docker Hub every run (intermittently timing out → red check, e.g. on #929) and is EOL. It was redundant: the pip `semgrep --sarif` step is what feeds GitHub Security; the action only pushed to the Semgrep cloud app via SEMGREP_APP_TOKEN. Removed it and folded its `p/docker` + `p/kubernetes` rulesets into the pip semgrep command, so coverage is preserved with no Docker pull. The job stays `continue-on-error: true` (non-gating). YAML validated. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(protocol): resolve 0xC511_0004 magic collision (closes #928) Background `0xC511_0004` was assigned to two different packet formats in firmware — `EDGE_FUSED_MAGIC` (ADR-063, 48-byte `edge_fused_vitals_pkt_t`) and `WASM_OUTPUT_MAGIC` (ADR-040, variable-length `wasm_output_pkt_t`). Both were transmitted. The sensing-server only had a WASM parser for that magic and no fused-vitals parser, so on the ESP32-C6 + MR60BHA2 mmWave configuration the fused-vitals packet was silently misparsed as a malformed WASM output — `breathing_rate` was read as `event_count`, mmWave-fused vitals were lost, and spurious WASM events were emitted to subscribers. Fix 1. Reassign `WASM_OUTPUT_MAGIC` to `0xC511_0007` (next free slot per the registry in `rv_feature_state.h`). Smaller blast radius than moving fused-vitals — the registry already treats `0xC511_0004` as fused-vitals canonical and several years of deployed feature tracking depends on that assignment. 2. Add `parse_edge_fused_vitals` + `EdgeFusedVitalsPacket` in `wifi-densepose-sensing-server::main`. Byte layout taken directly from `edge_processing.h:129`, mirroring the firmware's `_Static_assert(sizeof(edge_fused_vitals_pkt_t) == 48)` so future firmware changes that grow the packet will break this parser loudly instead of silently. 3. Add a dispatch arm in the UDP receive loop. Fused-vitals is tried BEFORE WASM so a stale firmware (still emitting 0xC511_0004 with the WASM payload) fails to parse as fused-vitals (size mismatch), then fails to parse as WASM (magic mismatch on the new 0x...0007), and gets dropped — a deliberate "fail loud" outcome rather than the pre-fix silent garbage. 4. Update the registry comment in `rv_feature_state.h` to add the new 0x...0007 row. 5. Add five tests in a new `issue_928_magic_collision_tests` mod: - `parse_edge_fused_vitals_extracts_fields_correctly` - `parse_edge_fused_vitals_rejects_short_buffer` - `parse_edge_fused_vitals_rejects_wrong_magic` - `parse_wasm_output_rejects_legacy_0004_magic` - `parse_wasm_output_accepts_new_0007_magic` WebSocket payload Fused-vitals now broadcasts as `{"type": "edge_fused_vitals", ...}` with the mmWave-specific block nested under `mmwave`. Schema is additive — existing subscribers that only inspect `type` are unaffected; subscribers that switch on `type` gain a new branch. Deployment note This is a wire-protocol change. Firmware older than this commit that emits WASM output on 0xC511_0004 will lose its WASM event stream against an updated host (host expects 0xC511_0007). Per the issue discussion, "fail loud" is preferred to silent misparsing. Operators running C6+mmWave should reflash firmware concurrent with the host upgrade. Test results cargo test -p wifi-densepose-sensing-server --no-default-features --bin sensing-server → 122 passed / 0 failed (5 new + 117 existing, unchanged) Co-Authored-By: claude-flow <ruv@ruv.net>
github-actions Bot
pushed a commit
to TheArkhitect/RuView
that referenced
this pull request
Jun 3, 2026
…loses ruvnet#928) (ruvnet#931) * fix(ci): SAST actually scans the code + drop deprecated flaky semgrep action Two real problems in the Static Application Security Testing job: 1. **It scanned a path that no longer exists.** `bandit -r src/` and `semgrep … src/` pointed at the repo-root `src/`, but the Python code moved to `archive/v1/src/` (64 .py files) when the runtime was rewritten in Rust. So the SAST scan matched nothing — a silent no-op (this is also why `bandit-results.sarif` was "Path does not exist" on recent runs). Fixed both to `archive/v1/src/`. 2. **Deprecated + redundant + flaky semgrep step.** The `returntocorp/semgrep-action@v1` step pulled `returntocorp/semgrep-agent:v1` from Docker Hub every run (intermittently timing out → red check, e.g. on ruvnet#929) and is EOL. It was redundant: the pip `semgrep --sarif` step is what feeds GitHub Security; the action only pushed to the Semgrep cloud app via SEMGREP_APP_TOKEN. Removed it and folded its `p/docker` + `p/kubernetes` rulesets into the pip semgrep command, so coverage is preserved with no Docker pull. The job stays `continue-on-error: true` (non-gating). YAML validated. Co-Authored-By: claude-flow <ruv@ruv.net> * fix(protocol): resolve 0xC511_0004 magic collision (closes ruvnet#928) Background `0xC511_0004` was assigned to two different packet formats in firmware — `EDGE_FUSED_MAGIC` (ADR-063, 48-byte `edge_fused_vitals_pkt_t`) and `WASM_OUTPUT_MAGIC` (ADR-040, variable-length `wasm_output_pkt_t`). Both were transmitted. The sensing-server only had a WASM parser for that magic and no fused-vitals parser, so on the ESP32-C6 + MR60BHA2 mmWave configuration the fused-vitals packet was silently misparsed as a malformed WASM output — `breathing_rate` was read as `event_count`, mmWave-fused vitals were lost, and spurious WASM events were emitted to subscribers. Fix 1. Reassign `WASM_OUTPUT_MAGIC` to `0xC511_0007` (next free slot per the registry in `rv_feature_state.h`). Smaller blast radius than moving fused-vitals — the registry already treats `0xC511_0004` as fused-vitals canonical and several years of deployed feature tracking depends on that assignment. 2. Add `parse_edge_fused_vitals` + `EdgeFusedVitalsPacket` in `wifi-densepose-sensing-server::main`. Byte layout taken directly from `edge_processing.h:129`, mirroring the firmware's `_Static_assert(sizeof(edge_fused_vitals_pkt_t) == 48)` so future firmware changes that grow the packet will break this parser loudly instead of silently. 3. Add a dispatch arm in the UDP receive loop. Fused-vitals is tried BEFORE WASM so a stale firmware (still emitting 0xC511_0004 with the WASM payload) fails to parse as fused-vitals (size mismatch), then fails to parse as WASM (magic mismatch on the new 0x...0007), and gets dropped — a deliberate "fail loud" outcome rather than the pre-fix silent garbage. 4. Update the registry comment in `rv_feature_state.h` to add the new 0x...0007 row. 5. Add five tests in a new `issue_928_magic_collision_tests` mod: - `parse_edge_fused_vitals_extracts_fields_correctly` - `parse_edge_fused_vitals_rejects_short_buffer` - `parse_edge_fused_vitals_rejects_wrong_magic` - `parse_wasm_output_rejects_legacy_0004_magic` - `parse_wasm_output_accepts_new_0007_magic` WebSocket payload Fused-vitals now broadcasts as `{"type": "edge_fused_vitals", ...}` with the mmWave-specific block nested under `mmwave`. Schema is additive — existing subscribers that only inspect `type` are unaffected; subscribers that switch on `type` gain a new branch. Deployment note This is a wire-protocol change. Firmware older than this commit that emits WASM output on 0xC511_0004 will lose its WASM event stream against an updated host (host expects 0xC511_0007). Per the issue discussion, "fail loud" is preferred to silent misparsing. Operators running C6+mmWave should reflash firmware concurrent with the host upgrade. Test results cargo test -p wifi-densepose-sensing-server --no-default-features --bin sensing-server → 122 passed / 0 failed (5 new + 117 existing, unchanged) Co-Authored-By: claude-flow <ruv@ruv.net> 2c136ac
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.
What
require_bearerparsed theAuthorizationheader withstrip_prefix("Bearer ")— case-sensitive. Per RFC 6750 §2.1 / RFC 7235 §2.1 the auth-scheme is case-insensitive, so a correct token sent asAuthorization: bearer <token>(orBEARER, or with extra whitespace) was rejected with a confusinginvalid bearer token401.That's needless friction in the active auth-setup theme (#864 secure-by-default, #924 ngrok demo) — someone configuring
RUVIEW_API_TOKENwhose client lowercases the scheme would see a 401 with a correct token.How
Match the scheme with
eq_ignore_ascii_case("Bearer")and trim leading token whitespace. The token comparison is unchanged — still exact and constant-time (ct_eq). So this does not weaken auth:Tests
New
accepts_case_insensitive_bearer_schemeasserts all of the above. Fullbearer_authsuite: 9 passed.Audited the surrounding auth code while here —
ct_eqcorrectly rejects length mismatch (no prefix-auth bypass) and is constant-time; the middleware fails closed. No security bug; this is purely an RFC-compliance/robustness fix.🤖 Generated with claude-flow