Skip to content

fix(automation): redact masked QLineEdit values in dumpTree (#3646)#3717

Merged
NF0T merged 1 commit into
aethersdr:mainfrom
jensenpat:fix/automation-dumptree-password-redaction
Jun 21, 2026
Merged

fix(automation): redact masked QLineEdit values in dumpTree (#3646)#3717
NF0T merged 1 commit into
aethersdr:mainfrom
jensenpat:fix/automation-dumptree-password-redaction

Conversation

@jensenpat

Copy link
Copy Markdown
Collaborator

Follow-up to #3710 (merged) — addresses @aethersdr-agent's post-merge review finding.

Problem

dumpTree's widgetValue() returned QLineEdit::text() for every line edit, with no echo-mode check. So masked credential fields were serialized in cleartext — and the bridge writes dumpTree into a temp tree.json (automation_probe.py demo), so the secret lands in a temp-dir artifact an agent or CI run could read.

Affected QLineEdit::Password fields:

  • src/gui/ConnectionPanel.cpp:407 — SmartLink radio password
  • src/gui/MqttSettingsDialog.cpp:110 — MQTT broker password

The bridge is opt-in (AETHER_AUTOMATION) and the socket is same-user 0600, so this isn't privilege escalation — but writing live credentials to a temp file is an easy footgun, and worth closing.

Fix

One spot in widgetValue() — redact any non-Normal echo mode (Password / NoEcho / PasswordEchoOnEdit):

if (auto* le = qobject_cast<const QLineEdit*>(w)) {
    if (le->echoMode() != QLineEdit::Normal)
        return le->text().isEmpty() ? QString() : QStringLiteral("<hidden>");
    return le->text();
}

Slightly more than the suggested one-liner: a populated masked field serializes as "<hidden>", an empty one omits value entirely — so the field stays assertable (present / empty / set) without ever exposing the secret. Normal fields are unchanged.

Verification (live, FLEX-8400M)

Typed a canary secret into the SmartLink password field via invoke, then:

  • password field → "value": "<hidden>"
  • canary string absent from the full dumpTree JSON ✓
  • canary string absent from the tree.json artifact written by automation_probe.py demo
  • Normal QLineEdit (email) still serializes its value — no over-redaction ✓
  • emptied masked field → value omitted ✓

💻 Generated with Claude Code (Opus 4.8) with architecture by @jensenpat

…r#3646)

dumpTree's widgetValue() returned QLineEdit::text() for every line edit
regardless of echo mode, so password fields — the SmartLink radio password
(ConnectionPanel.cpp:407) and the MQTT broker password (MqttSettingsDialog.cpp:110),
both QLineEdit::Password — were serialized in cleartext, including into the temp
tree.json that `automation_probe.py demo` writes. The bridge is opt-in and the
socket is same-user 0600 so this isn't privilege escalation, but writing live
credentials into a temp artifact is an easy footgun for an agent or CI run.

Redact any non-Normal echo mode (Password / NoEcho / PasswordEchoOnEdit):
return "<hidden>" for a populated masked field and null (omitted) for an empty
one, so the field stays assertable (present / empty / set) without exposing the
value.

Reported by @aethersdr-agent in review of aethersdr#3710 (post-merge).

Verified live on a FLEX-8400M: a canary secret typed into the SmartLink
password field serializes as "<hidden>" and is absent from both dumpTree and
the tree.json artifact; a Normal QLineEdit (email) is unaffected; an emptied
masked field omits its value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@NF0T NF0T 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.

Human-directed independent review (Claude / @NF0T)

Minimal, correctly-targeted fix for the credential-exposure finding flagged in the bot's post-merge review of #3710.

Claims independently verified against current main:

  • ConnectionPanel.cpp:407m_passwordEdit->setEchoMode(QLineEdit::Password) confirmed ✓
  • MqttSettingsDialog.cpp:110m_passEdit->setEchoMode(QLineEdit::Password) confirmed ✓
  • These are the only two non-Normal echo mode fields in src/gui/ (comprehensive grep). No other masked fields are missed. ✓
  • widgetValue() in current main (post-#3710 merge) returns le->text() unconditionally — the bug is live. ✓

Fix is correct: echoMode() != QLineEdit::Normal catches all three masked modes (Password, NoEcho, PasswordEchoOnEdit). Empty field → QString() (omitted from JSON), non-empty → "<hidden>" — assertability preserved, value never exposed. Normal fields unchanged. No over-redaction, no under-redaction.

Scope: +8/−1 in widgetValue() only. No side effects.

This is a single-file Tier 3 change. LGTM — approve on exit from draft.

@jensenpat jensenpat marked this pull request as ready for review June 21, 2026 16:18
@jensenpat jensenpat requested a review from a team as a code owner June 21, 2026 16:18

@NF0T NF0T 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.

Human-directed independent review (Claude / @NF0T)

Previously reviewed at draft stage — same commit ed0c3fe9, no changes. Approving on exit from draft as noted.

@NF0T NF0T merged commit 90fbf0a into aethersdr:main Jun 21, 2026
6 checks passed
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.

2 participants