Feature/rig bridge security hardening & other enhancements#803
Feature/rig bridge security hardening & other enhancements#803accius merged 10 commits intoaccius:Stagingfrom
Conversation
…anch Reimplements all security fixes from feature/rig-bridge-security-assessment-fixes onto Staging as a clean rewrite, preserving Staging's SmartSDR/RTL-TCP additions. rig-bridge/core/config.js: - Auto-generate apiToken via crypto.randomBytes on first run - Add tokenDisplayed, fixSplit, udpBindAddress to DEFAULT_CONFIG - Merge smartsdr/rtltcp config sections in loadConfig() rig-bridge/core/server.js: - Add isValidRemoteHost(), isValidPort(), makeRateLimiter() helpers - Add requireAuth middleware (X-RigBridge-Token header) - Add per-route rate limiters (freq/mode/ptt/cfg/token) - GET /api/log/stream: authenticate via ?token= query param - GET /api/ports: require auth - GET /api/config: strip apiToken from response - POST /api/config: require auth + SSRF host/port validation for all 5 plugin hosts - POST /api/test, /api/logging: require auth - POST /freq, /mode, /ptt: require auth + rate limit + input validation - New endpoints: POST /api/auth/verify, GET /api/token, POST /api/token/regenerate, POST /api/setup/token-seen - buildSetupHtml: login overlay, welcome banner, first-run token injection, full auth flow (doAutoLogin, doLogin, doLogout, authHeaders) rig-bridge/plugins/*.js: - flrig.js: add defensive host validation before XML-RPC connect - rigctld.js: dual-mode protocol, fixSplit support, host validation - tci.js: full WebSocket implementation (ws npm + native), host validation - wsjtx-relay.js: UDP bind to 127.0.0.1 by default, udpBindAddress config - rtl-tcp.js, smartsdr.js: add host validation (Staging-only plugins) src/: - RigContext.jsx: restore X-RigBridge-Token auth headers + 401 handling - RigControlPanel.jsx: restore two-tier error banner (unauthorized vs daemon) - SettingsPanel.jsx: restore API token field with show/hide toggle - lang/en.json + 14 other languages: add 4 API token i18n keys Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add supported radio entries, setup sections, config field tables, expected log output, troubleshooting rows, and project structure entries for the two new Staging plugins (smartsdr.js, rtl-tcp.js). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminates manual relay key and session ID copy-paste with two
complementary auto-configure flows.
OHC server (server/routes/wsjtx.js):
- GET /api/wsjtx/relay-credentials — returns WSJTX_RELAY_KEY to
callers on localhost origins (CORS opened for local rig-bridge UI)
- OPTIONS preflight handler for the same route
OHC frontend:
- SettingsPanel.jsx: new WSJT-X Relay sub-section inside the Rig
Control card (visible when rig control is enabled)
- Shows session ID (read-only) with copy button
- "Configure Relay on Rig Bridge" button: fetches relay key from
/api/wsjtx/relay-credentials, then POSTs url+key+session+enabled
to the configured rig-bridge /api/config in one click
- Inline success/error feedback
- App.jsx: passes wsjtx.sessionId down to SettingsPanel
- 15 lang files: 10 new i18n keys for the WSJT-X relay UI
rig-bridge setup UI (rig-bridge/core/server.js):
- "Fetch credentials" button next to the OHC Server URL field
auto-fills the relay key via GET {url}/api/wsjtx/relay-credentials
- Session ID help text directs users to OHC Settings to copy it
- fetchWsjtxCredentials() JS function with full error handling
rig-bridge/README.md:
- Replaces bare config table with three-option setup guide
(Option A: push from OHC, Option B: fetch in rig-bridge UI,
Option C: manual config)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The regex /\/$/ inside the buildSetupHtml template literal had its backslash silently dropped (\/ is an unrecognized escape in template literals, producing just /). This turned /\/$/ into //$/ which starts a line comment in the generated HTML, making fetch() unclosed and crashing the entire <script> block with 'Unexpected keyword if'. Fix: use a character class /[/]$/ instead of an escaped slash, which avoids the backslash-in-template-literal pitfall entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@accius probably there are some conflicts between @alanhargreaves (#804) and mine. In case just merge Alans first and I will adjust mine then accordingly. |
PTT polling:
- poll() now sends cmd 0x1c sub 0x00 (read TX state) on every tick,
staggered 100 ms after the mode query
- handleData() gains case 0x1c to parse the response and call
updateState('ptt', ...), so externally keyed TX (VOX, foot switch)
is reflected in the UI without waiting for a write command
DV mode:
- 0x17 was mapped to 'DATA-FM'; corrected to 'DV' to match the
rig-listener implementation (D-STAR voice mode on IC-705, IC-9700)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…esponses
Two fragile checks could falsely report PTT active:
1. IF; parser: `txState !== '0'` treated any unexpected byte at position 22
(garbage on reconnect, model-specific extension, truncation artifact, or
a bare empty string) as PTT ON. Changed to only assert ON for an explicit
'1' (PTT TX) or '2' (CAT/linear TX).
2. TX; case: a bare `TX;` with no digit produced txDigit = '', which failed
the `=== '0'` guard and fell through to updateState('ptt', true).
Now only '1' or '2' assert ON; missing digit is silently ignored.
Both changes follow the same principle: require a positive match to assert
TX state rather than inferring it from the absence of '0'.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces English fallback text added in the credential-flow commit with proper translations for all 10 WSJT-X relay keys across: ca, de, es, fr, it, ja, ka, ko, ms, nl, pt, ru, sl, zh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When rig-bridge returns 403 on a PTT command (pttEnabled: false in its
config), the UI now clearly tells the user why PTT isn't working instead
of silently reverting the button.
RigContext.jsx:
- Detect res.status === 403 in setPTT, set error('ptt-disabled'), and
revert the optimistic PTT state — same pattern as the 401 handler
- Clear ptt-disabled error on next successful PTT response
RigControlPanel.jsx:
- Amber warning banner (distinct from the red daemon/auth banners)
shown when error === 'ptt-disabled'
- PTT button gains a 🔒 prefix, dashed border, and tooltip with the
full message so the hint is visible even on small panels
- ptt-disabled excluded from the generic daemon-error fallback banner
i18n:
- New key app.rigControl.error.pttDisabled in en.json and all 14
language files with proper translations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both rig-bridge's WSJT-X relay and a locally-running OpenHamClock server bind to UDP 2237. Added a callout in the WSJT-X Relay section explaining that rig-bridge must be started first, what the 'port already in use' log message means, and how to recover. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes were producing spurious PTT=true in the Yaesu USB plugin:
1. IF; response PTT field is position-unreliable across models.
The TX/RX flag at position 22 was confirmed only on the FT-991A.
On FT-891, FT-710, FT-DX10 etc. the "unknown" byte at position 4
may be absent, shifting all subsequent fields left by one — so the
memory channel digit ('1' for channels 100-199) lands at position 22
and triggers PTT=TX. Remove PTT parsing from case 'IF' entirely.
2. Missed auto-info TX0; (unkey) had no short-term recovery path.
The 30-second keepalive was the only mechanism to recover a stuck
PTT=true after the radio released TX.
Fix: add TX; (bare = read query, not set command) to the startup
sequence (IF;TX;) and to the 30-second keepalive (AI1;IF;TX;).
PTT state is now sourced exclusively from TX;/RX; responses, which
use an unambiguous 3-character format (TX0;/TX1;/TX2;) that is
consistent across all FT-series models.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Great work @ceotjoe — this is a thorough and well-structured PR. Reviewed all 33 files. Security hardening — solid implementation The token auth flow is well done. crypto.randomBytes(16) for generation, first-run banner with one-time display, The UDP bind change from 0.0.0.0 to 127.0.0.1 by default is an important security improvement. WSJT-X relay credential flow — clean The dual-direction approach (push from OHC settings, or fetch from rig-bridge setup UI) covers both workflows nicely. Protocol fixes — these are important The Yaesu PTT fix is well-documented and explains exactly why the IF; response is unreliable across models. Moving to Minor notes for future consideration:
Merging now. @alanhargreaves heads up — #804 will need a rebase onto Staging after this lands since both PRs touch |
What does this PR do?
Summary
Ports all security fixes from
feature/rig-bridge-security-assessment-fixesinto a clean rewrite on top of Staging, aligns the two Staging-only plugins (SmartSDR, RTL-SDR) with the same security standards, and adds a zero-friction WSJT-X relay credential setup flow.Security hardening (rig-bridge)
Authentication
apiTokenon first run viacrypto.randomBytesrequireAuthExpress middleware validatesX-RigBridge-Tokenon all write endpointsRate limiting & input validation
freq,mode,ptt,cfg,token)POST /api/configvalidates host strings and port ranges for all 5 plugin typesSSRF prevention
isValidRemoteHost()/isValidPort()helpers added toserver.jsflrig,rigctld,tci,wsjtx-relay,smartsdr,rtl-tcp) validate the configured host before opening any connectionNew endpoints
POST /api/auth/verify,GET /api/token,POST /api/token/regenerate,POST /api/setup/token-seenGET /api/log/streamnow authenticates via?token=query param (EventSource can't set headers)GET /api/configstripsapiTokenfrom the responsePlugin updates
rigctld.js— dual-mode protocol support,fixSplitoptiontci.js— full rewrite with native WebSocket +wsnpm fallbackwsjtx-relay.js— UDP binds to127.0.0.1by default;udpBindAddressconfig fieldWSJT-X relay credential flow
Previously, users had to manually find and copy two opaque strings (relay key + session ID) across two different UIs. Now there are two one-click paths:
Option A — Push from OpenHamClock (recommended)
Settings → Station → Rig Control → WSJT-X Relay → Configure Relay on Rig Bridge
OpenHamClock fetches the relay key from its own server and POSTs both credentials (key + session ID) directly to the connected rig-bridge in one step.
Option B — Fetch from rig-bridge setup UI
Integrations tab → enter OHC URL → 🔗 Fetch credentials
rig-bridge pulls the relay key automatically; help text directs the user to copy their session ID from OHC Settings.
Changes:
GET /api/wsjtx/relay-credentialsendpoint on OHC server (CORS open for localhost only)SettingsPanel.jsxwith session ID display + push buttonfetchWsjtxCredentials()JS function in rig-bridge setup UIDocs
rig-bridge/README.mdupdated with SmartSDR and RTL-SDR plugin sections, three-option WSJT-X relay setup guide, updated troubleshooting table, and corrected project structure tree.Frontend (OHC)
RigContext.jsx—X-RigBridge-Tokenheader on all rig API calls; 401 →error: 'unauthorized'RigControlPanel.jsx— two-tier error banner (unauthorized vs. daemon unreachable)SettingsPanel.jsx— API token field with password show/hide toggleType of change
Checklist
server.js: caches have TTLs and size caps (we serve 2,000+ concurrent users)var(--accent-cyan), etc.).bak,.old,console.logdebug lines, or test scripts includedI'm still working on this, just to give a bit overview where the changes are.