Skip to content

Fix CWX quote encoding and macro status decoding - #6494

Merged
jensenpat merged 2 commits into
aethersdr:mainfrom
rfoust:codex/fix-6437-cwx-quotes
Oct 18, 2026
Merged

jensenpat merged 2 commits into
aethersdr:mainfrom
rfoust:codex/fix-6437-cwx-quotes

Conversation

@rfoust

@rfoust rfoust commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #6488. Implements the CWX quote stand-in item from #6437; the remaining compatibility items stay open.

Embedded double quotes break CWX's quoted command argument, and macro status leaves the radio's quote stand-in undecoded. Batched sends, live characters and macro saves now encode " as byte 0x1E; macro status maps it back. Space encoding, BT/prosign handling, speed modifiers, local-keyer text, queue character counts and TX admission retain their existing behavior.

The authority is the published FlexLib 4.3.16.42034 source, inspected directly and compared with the 4.2.20 source. CWX.cs:30 defines the stand-in; lines 48, 91, 100 and 153 encode it, and line 527 decodes it. The new reference applies substitution without a firmware gate. Text without quotes remains byte-identical; quotation-mark handling on older firmware is unverified and documented accordingly.

Regression proof

Socket-free tests exercise the production command construction and full status-line parser. For CQ "DX", the expected raw send-command bytes are 6377782073656e64202243517f1e44581e222031; reply character counts continue to use the original text length. Macro fixtures cover quoted/unquoted status, spaces, BT, empty macros, resend and adjacent status keys.

Panel tests drive Send, a Live quotation-mark keystroke, macro editing and F1 playback against the real model. Independent fixed byte fixtures replace the stale duplicate test encoder. The editor and local keyer retain literal quotation marks.

Protocol proof stays in the socket-free tests. This PR adds no production bridge API, synthetic firmware peer, socket-owning test, settings or changelog changes. No live firmware, RF, or Windows/Linux runtime validation is claimed. cwx insert, the radio CW decoder and AutoDX have no existing applicable consumers here and remain outside this patch.

Constitution principle honored

Principle I — FlexLib Is The Protocol Authority. The substitution comes from the officially published source. Principles VI and VIII: TX admission is preserved and regression tests reject removal of the mappings.

Validation

  • Native ARM64 macOS application build passed with RADE enabled; host/system processors and final executable are ARM64, with no RNNoise x86 sources in the Ninja graph.
  • Four focused CTests passed: cwx_quote_test, cwx_speed_modifier_test, cwx_drain_watch_test, cwx_panel_test.
  • Both model encode/decode mappings are mutation-checked. Removing quote encoding also makes the panel's Send, Live, macro-save and F1 assertions fail; the fix was restored and rebuilt.
  • User docs build, generated references, bridge-doc drift check, test registration and frozen CI gate checks passed.
  • The full default ARM64 build passed before the review cleanup; the final application and changed test targets were rebuilt after it.

Generated with OpenAI Codex.

@rfoust
rfoust marked this pull request as ready for review October 14, 2026 23:56
@rfoust
rfoust requested a review from a team as a code owner October 14, 2026 23:56
Copilot AI balanced review requested due to automatic review settings October 14, 2026 23:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T00:03:43.226957Z 94315c8 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94315c8cd4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/core/AutomationServer.cpp Outdated

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue fit

Yes for the protocol fix. #6488's acceptance list maps cleanly onto the diff: all three encode sites now route through one helper (src/models/CwxModel.cpp:17-24, consumed at :199, :281, :350), the single decode site gained the 0x1E case (:577), and tests/cwx_quote_test.cpp asserts exact command bytes through the real CommandParser rather than a hand-built kv map. The "queue character counts unchanged" claim is the one I most wanted to break and it holds structurally, not just by test: nChars is taken from the pre-encode text (seg.text.length() at :205, ch.length() at :286) and "→0x1E is 1:1, so cwxEndIndex, the sent= drain comparison and TX admission keep their arithmetic untouched.

The bridge-preview acceptance bullet is also satisfied — but see Scope: that bullet is self-authored and the issue still carries maintainer-review.

Scope

File What it changes Claimed? Verdict
src/models/CwxModel.cpp encodeCwxText helper; 3 encode sites; 0x1E decode Yes In scope — the fix
tests/cwx_quote_test.cpp New socket-free encode/decode regression Yes In scope
tests/tests.cmake Registers cwx_quote_test, joins AETHER_SETTINGS_CONSUMERS Yes In scope — matches the adjacent cwx_drain_watch_test idiom exactly
src/core/AutomationServer.cpp New cwx preview / cwx preview-status bridge actions Yes New public bridge surface → maintainer decision
tests/automation_tx_watchdog_test.cpp Isolation test for the two new actions Yes In scope
docs/automation-bridge.md Documents the two actions; corrects "Every action here emits a cwx verb" Yes In scope
docs/user/docs/cwx-panel.md One sentence on quote support Yes In scope

Nothing in the diff is unexplained by #6488. No CHANGELOG.md entry — correct. Not updating docs/user/versioned_docs/version-stable/cwx-panel.md is also correct: that snapshot is re-taken at release prep (34363a3), and the only difference between the two files is this PR's new sentence.

Blockers

None.

Needs maintainer decision

1. src/core/AutomationServer.cpp:9136-9175 — the two new bridge actions return raw Flex wire text above the seam.

The reply hands the client the constructed command and its bytes:

                                        {QStringLiteral("command"), command},
                                        {QStringLiteral("utf8Hex"), QString::fromLatin1(command.toUtf8().toHex())},

docs/agents/backends.md:9-20 (the #5554 standing notice) says, unqualified by file: "check that your change does not add to any item listed there — … no new raw Flex wire text above the seam." @chatgpt-codex-connector is reading that correctly in substance. Three things in tension, none of which I think I should resolve:

  • I looked for precedent and found none. tci send's {"command", command} (:12098) echoes caller-supplied TCI text, and lastCommand in the waveform snapshot (:5537) is a structured {action, name, code, body} object built at :6297/:6310 — not the waveform remove … wire string. So as far as I can tell these are the first bridge fields to carry Flex command bytes to a client.
  • Against treating it as a violation: the project's own command-plane ratchet does not register a new site, and legitimately so — AutomationServer constructs nothing. The strings come from CwxModel, whose baseline in tools/check_command_plane.py:79 is unchanged at 9, as is AutomationServer.cpp's 6 at :103. Static checks are green. The exposure is new; the emitter is not.
  • Also against: #6488's acceptance list asks for exactly this preview, and the triage comment on that issue proposed this precise shape (disconnected-only, stack-local model, hex bytes). But the issue was authored by the PR author ~21 minutes before the PR and still carries maintainer-review, so that bullet is self-certification rather than a ruling, and the triage bot is a lead.

I'd note for the record that Codex's "the socket-free test already exercises the production encoder, so keep this proof in the test" is the weaker half of its argument — cwx_quote_test is the mutation guard, but the preview is what produced the before/after byte captures in the PR body, which the test cannot. The question is whether that diagnostic belongs in the shipped bridge API or in an opt-in target.

Nits (all non-blocking)

src/core/AutomationServer.cpp:9145 — declaration order makes the capture lifetime fragile. preview is declared before the state its signal lambdas capture by reference:

        CwxModel preview;

…with commands, operation and capture declared at :9153-9160. Reverse destruction frees the captured locals before the QObject. This is safe today — I checked: ~CwxModel (src/models/CwxModel.cpp:31-34) only stores to m_queueValid and emits nothing, and both connect calls use &preview as context on one thread, so delivery is direct and synchronous and no lambda can fire after doCwx returns. It breaks silently the day something emits during teardown. Mechanical fix — hoist the three declarations above the model:

        QJsonArray commands;
        QString operation;
        const auto capture = [&commands, &operation](const QString& command, int nChars) {
            commands.append(QJsonObject{{QStringLiteral("operation"), operation},
                                        {QStringLiteral("command"), command},
                                        {QStringLiteral("utf8Hex"), QString::fromLatin1(command.toUtf8().toHex())},
                                        {QStringLiteral("nChars"), nChars}});
        };
        // This temporary production model has no transport, audio or TX
        // coordinator binding. Its signals are captured only into this reply.
        CwxModel preview;

(preview-status returns at :9148 before any of this, so the early-return path is unaffected.)

src/core/AutomationServer.cpp:2739-2805 — the previews are refused in observe-only mode. cwx is absent from isReadOnlyRequest's kSafe set and has no per-action clause, so the gate at :3992 blocks cwx preview whenever m_readOnly is set — even though it is pure introspection with no transport. Fail-closed, so not a defect, and the docs don't overclaim (they say "work with TX automation disabled", not observe-only). Worth matching the sibling anyway: #6493's body records "Review reproduced and corrected an Observe-only allow-list omission" for its isolated probe, i.e. the same omission was treated as worth fixing there. Drop-in beside the tci clause at :2796:

    if (name == QLatin1String("cwx")) {
        return normalizedAction == QLatin1String("preview")
            || normalizedAction == QLatin1String("preview-status");
    }

tests/cwx_panel_test.cpp:123-127 — a now-stale duplicate of the encoder.

QString sendCommand(const QString& text, int block)
{
    QString encoded = text;
    encoded.replace(' ', QChar(0x7f));

This is the fourth encode site and it was not updated. It stays green only because no fixture in that file contains a " — so it is a silent stale copy, not a guard. The triage comment on #6488 expected it to adopt the shared helper. Small, and arguably its own cleanup.

docs/user/docs/cwx-panel.md:96 — scoped to 4.3 for an unconditional change. "On FlexRadio with SmartSDR 4.3 firmware, double quotation marks (") are preserved…" is accurate as far as it goes, but encodeCwxText has no firmware gate, and AGENTS.md:40-42 puts the project's reference radio at 4.2.18. I considered and rejected this as a regression: before the change a " closed the quoted argument early and corrupted the command, so pre-4.3 goes from broken one way to unverified, not from working to broken. Still, a 4.2 operator reading that sentence learns nothing about their own radio. One clause ("on earlier firmware the stand-in byte is sent but its handling is unverified") would close it.

What I tried to break

  • parseKVs composition, which I expected to be the bug. closesQuotedValue (src/core/backends/flex/CommandParser.cpp:19-21) returns false for a self-closing token like macro12="…$" (two quotes, not one), so I thought the wpm=25 key would be swallowed. It isn't: the multi-token join at :26 only fires when the opening token has count('"') == 1, so a self-closed field is used verbatim and the helper never runs. The test's macro12="…" wpm=25 fixture plus its model.speed() == 25 assertion genuinely proves this, and the 0x1E substitution is what keeps that quote-counting parser well-formed on inbound macros.
  • A character validator silently rejecting ". RadioModel::cwTextValidationError (src/models/RadioModel.cpp:4264-4286) walks caps.cwTextAllowedCharacters, which would have made the fix dead on the CAT/TCI/bridge paths. Only IcomCivBackend.cpp:349 sets it; Flex leaves it empty, so there is no character restriction. Refuted.
  • A second encoder or a pre-decode upstream. Grepped every 0x7f/0x1e site in src/: the only CWX ones are the four in CwxModel.cpp (three now shared, one in the panel test). cwx status reaches CwxModel::applyStatus with raw kvs via RadioModel.cpp:11535-11537 — no cleanClientText/decodeText pre-pass intercepts it, so the decode is not double-applied.
  • Round-trip asymmetry. The decode maps *→= but the encode does not map =→*, so client-saved macros send a literal = while the radio echoes *. Pre-existing, asserted deliberately by the test, and out of scope here — noting it only so it is clear I didn't mistake it for new.
  • The isolation claim on the preview. A default-constructed CwxModel has null m_sendAvailability/m_transmissionAdmission/m_textSender and an empty TransmissionRoute, so dispatchCommand falls to the two signals whose only receivers are the capture lambdas; transmissionRequested/transmissionDispatched have none. Both gates sit before the branch, so the disconnected check and the 1024-char bound cover preview-status too. The token-auth gate at :3964 and the observe-only gate at :3992 both run on the canonical verb before doCwx, so no per-verb registration was needed. The new test case asserts the real keyer, macro store and transport stay untouched and would fail against the unfixed encoder (it pins 43517f1e44581e).
  • CodeGuard's 111 findings. None intersect the change. The reported lines (300, 464, 841, 1760, 2399, 6197, 6987, 12290…) all fall far outside the changed regions (3391-3395, 9136-9175, 9226); the four I sampled are comment prose — :6987 is the // A directed broadcast (e.g. 192.168.50.255) … comment behind CS-VLN-SSRF-PRIV-192. toHex() at :9158 is a Qt method name with no URL or network call in doCwx, and there is no shell, QProcess or system() in the function for the CMDI families to land on. Credit: security-audit (CodeGuard). The test fixtures' setAuthToken("rotated-test-authorization") strings are rotation-test inputs, not secrets.

Could not check:

  • The 0x1E mapping itself — the Principle I citation. The in-tree reference at reference/FlexLib/FlexLib/CWX.cs is 4.2.18-era and contains zero 0x1E occurrences (I grepped it), and edge.flexradio.com is unreachable from here. Per the project's own rule a missing 4.3 key in a 4.2.x tree means stale reference, not fabrication — and #6437 cites specific CWX.cs line numbers — but nothing in-tree corroborates the byte. What would settle it: installing reference/FlexLib_API_v4.3.16.42034/, which the triage comment on #6488 called step one and expected to precede implementation. Non-blocking; the client-side defect stands on its own regardless of the exact byte.
  • Runtime behavior. I have no build and no test run; every finding above is reasoned from the head checkout at /tmp/aetherclaude/pr-6494, not reproduced.
  • CI does not run either new test. The only ctest -R steps in .github/workflows/ci.yml are the DV-waveform four (:819-820), cross_needle_meter_test (:832), mac_nr_filter_test (:1174) and asr_gpu_probe_test (:1189). Green build/check-macos/check-windows proves cwx_quote_test and automation_tx_watchdog_test compile; the PR body's local pass is the only evidence they execute.

Socket-test disclosure: none. tests/cwx_quote_test.cpp is socket-free. tests/automation_tx_watchdog_test.cpp contains no QLocalServer/QTcpServer/listen()/connectToHost()/bind() and links aethercore with Qt6::Network only transitively for the library; the new case drives AutomationServer through an in-process f.request(...) helper. The PR adds no socket-owning test and removes none.

Recommendation

Needs maintainer decision on the two new bridge actions; approve with nits on everything else.

The protocol fix is the good kind of small: one helper, four call sites collapsed to three plus a decode, a 1:1 substitution that provably leaves the drain arithmetic alone, and a test that fails against the unfixed encoder for the right reason. I tried the five most likely ways for it to be wrong and it survived all of them. The open question is not correctness but placement — whether a byte-level Flex diagnostic belongs in the shipped bridge API, given #5554's "no new raw Flex wire text above the seam" and that I could find no existing bridge field doing it. The acceptance bullet asking for it is the PR author's own, on an issue still labelled maintainer-review, so that call is Jeremy's. Concrete next step: rule on the preview (keep as-is / move behind an opt-in target / drop), and separately decide whether the 4.3.16 reference tree should land before this merges so the Principle I citation resolves in-tree. The three nits are safe to fold in either way.

Thanks for the unusually legible protocol work here — routing all four encode sites through one helper is what makes the quote case checkable at all, and the macro12="…" wpm=25 fixture is a well-chosen one.


🤖 aethersdr-agent · cost: $19.5906 · model: claude-opus-5

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Cisco CodeGuard — static analysis of this PR (111 finding(s))

  • [HIGH] CS-VLN-SSRF-PRIV-192 — Vulnerability: CS-VLN-SSRF-PRIV-192 in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:6987
  • [HIGH] CS-VLN-SSRF-LOCALHOST — Vulnerability: CS-VLN-SSRF-LOCALHOST in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:12290
  • [HIGH] CS-VLN-SSRF-LOCALHOST — Vulnerability: CS-VLN-SSRF-LOCALHOST in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:12291
  • [MEDIUM] CS-VLN-SSRF-HEX — Vulnerability: CS-VLN-SSRF-HEX in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:6197
  • [CRITICAL] CS-VLN-CMDI-SEMI — Vulnerability: CS-VLN-CMDI-SEMI in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1760
  • [CRITICAL] CS-VLN-CMDI-SEMI — Vulnerability: CS-VLN-CMDI-SEMI in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:7475
  • [CRITICAL] CS-VLN-CMDI-SEMI — Vulnerability: CS-VLN-CMDI-SEMI in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:13081
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2399
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:8438
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:9504
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:11525
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:11888
  • [CRITICAL] CS-VLN-CMDI-AMP — Vulnerability: CS-VLN-CMDI-AMP in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:12447
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:300
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:464
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:841
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:950
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1307
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1380
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1532
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1609
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1756
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1786
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:1790
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2008
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2118
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2177
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2185
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2297
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2395
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2426
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2725
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2748
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2821
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:2887
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:3358
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:3418
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:3579
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:3605
  • [CRITICAL] CS-VLN-CMDI-BACKTICK — Vulnerability: CS-VLN-CMDI-BACKTICK in src/core/AutomationServer.cpp /tmp/aetherclaude/pr-6494/src/core/AutomationServer.cpp:3663

Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them.


🤖 aethersdr-agent · cost: $21.0783 · model: claude-opus-5

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Antares Detector — candidate vulnerable file(s)

  • src/models/CwxModel.cpp

The quote decoding logic in src/models/CwxModel.cpp decodes quoted macro fields by stripping quotes and replacing control characters (e.g., decoded.replace(QChar(0x7f), ' ');). This involves reading untrusted input (the received body) and performing string manipulation without obvious bounds-checking or safe parsing. The code decodes quoted macro fields from FlexLib CWX.cs format, which can contain embedded quotes and control characters, and the implementation may be vulnerable to issues like unsafe string replacement or memory-safety problems in the quote parsing logic.

Localized by Cisco Foundation AI Antares-1B running locally in the AetherClaude sandbox, seeded by the Cartographer security map. Advisory only — please verify before acting.


🤖 aethersdr-agent · cost: $21.0783 · model: claude-opus-5

rfoust added a commit to rfoust/AetherSDR that referenced this pull request Oct 15, 2026
Address PR aethersdr#6494 feedback by removing the production raw Flex preview API and its companion tests/docs. Replace the stale panel test encoder with independent byte fixtures covering Send, Live, macro save and F1 playback, and clarify that quote handling on older firmware is unverified.
@rfoust

rfoust commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator Author

Addressed the review feedback in signed commit 54fd6ef:

  • Removed cwx preview and preview-status from the production bridge, including their docs and test additions. This also removes the capture-lifetime and observe-only concerns. The issue's proposed preview acceptance item is superseded by socket-free regression proof; no bridge API decision is needed for this patch.
  • Removed the stale duplicate encoder from cwx_panel_test. Real panel Send, Live, macro editing and F1 playback now assert independent fixed command bytes with embedded quotes. Removing production quote encoding makes all four new assertions fail; restoring it returns them to green.
  • Clarified the user docs that quotation-mark handling on earlier firmware is unverified. No speculative firmware gate was added.

For protocol provenance, I directly downloaded the official FlexLib 4.3.16.42034 archive and compared it with 4.2.20. In CWX.cs, line 30 defines QUOTE_SUBST as byte 0x1E; lines 48, 91, 100 and 153 encode it, and line 527 decodes it. Archive SHA-256: 60978db51dac6ea781899dd80832ad4e7876ce96ed72532b57106376fc26463f. The older in-tree reference was not the authority for this mapping; the PR description links the published source.

I also checked the security advisories. CodeGuard's reported file, AutomationServer.cpp, is now unchanged from the base, so those reports are not introduced by the final patch; this is not a security clearance of the existing bridge. For the Antares candidate, macro indices are checked against 1–12 before access, QString::mid and single-character replacement manage their own bounds, and inbound macro updates use QSignalBlocker/setPlainText without command feedback. The report supplies no supported unsafe-memory operation or reproducer, and I found no defect requiring a change there.

Validation: final native ARM64 application build with RADE, all four focused CWX tests, user-docs build, and relevant static/registration checks passed. The per-PR test filter remains frozen by repository policy; local execution and mutation evidence are recorded in the PR description. New-head CI is running. PR remains ready for review.

Generated with OpenAI Codex.

@jensenpat jensenpat self-assigned this Oct 18, 2026
Implement the CWX quote stand-in item from aethersdr#6437 using the published FlexLib 4.3.16 source. Cover actual wire bytes, macro status parsing, TX guards and an isolated disconnected agent automation bridge preview. Fixes aethersdr#6488.
Address PR aethersdr#6494 feedback by removing the production raw Flex preview API and its companion tests/docs. Replace the stale panel test encoder with independent byte fixtures covering Send, Live, macro save and F1 playback, and clarify that quote handling on older firmware is unverified.
@jensenpat
jensenpat force-pushed the codex/fix-6437-cwx-quotes branch from 54fd6ef to 399a8af Compare October 18, 2026 01:33

@jensenpat jensenpat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No remaining blockers found at 399a8afa787f6f5e6b57d720359e32470209b435.

Rebased the two commits onto c54912ec4 without conflicts, preserved Robbie Foust's authorship, and verified both replacement signatures on GitHub. The final five-file patch retains the quote fix and removes the preview API; AutomationServer.cpp and bridge documentation have no PR delta. The earlier stale panel encoder and older-firmware documentation findings are addressed in this head; the existing inline thread has an evidence-bearing reply and is resolved. No blocking reviews remain.

Verified the protocol authority directly from the published FlexLib 4.3.16 archive (SHA-256 60978db51dac6ea781899dd80832ad4e7876ce96ed72532b57106376fc26463f): CWX.cs defines 0x1E, encodes quotes for sends/macro saves, and decodes macro status without a firmware gate. The helper preserves one-for-one character counts and leaves local-keyer text and admission paths unchanged. Macro indices remain bounded before array access; Qt string operations and the panel's signal blocker do not substantiate the advisory memory-safety claim. The reported AutomationServer findings concern code outside the final patch.

Local macOS validation on this exact head: all four focused CTests passed with QT_QPA_PLATFORM=offscreen (cwx_quote_test, cwx_speed_modifier_test, cwx_drain_watch_test, cwx_panel_test). Removing encoding caused five quote assertions to fail; removing decoding caused three to fail. Restored the exact patch, rebuilt all four targets, and reran them: 4/4 passed, clean working tree. Boundary/capability/command-plane checks have identical base/head findings; registration, frozen gate, generated-reference, bridge-doc drift, and log-rule checks passed. Hosted Docs and Static Checks passed. Platform CI is still running and must finish before merge.

Manual adversarial review performed; the separate automated code-review skill is unavailable in this session. Protocol evidence is socket-free model/parser and real-panel testing: the bridge has no applicable byte/status probe, and no app bridge session, live radio, firmware, or RF validation is claimed. Older firmware remains explicitly unverified.

Generated with OpenAI Codex (GPT-6 Astra)

@jensenpat
jensenpat enabled auto-merge (squash) October 18, 2026 01:38
@jensenpat
jensenpat disabled auto-merge October 18, 2026 01:48
@jensenpat
jensenpat enabled auto-merge (squash) October 18, 2026 01:48
@jensenpat
jensenpat merged commit a066947 into aethersdr:main Oct 18, 2026
7 checks passed
@jensenpat jensenpat removed their assignment Oct 18, 2026
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.

Fix CWX quotation-mark encoding and macro status decoding

3 participants