From b64f5e51fb551c531bf09e6b32826f042563e19c Mon Sep 17 00:00:00 2001 From: Richard Lences <82523201+richardlences@users.noreply.github.com> Date: Thu, 30 Jul 2026 19:39:28 +0200 Subject: [PATCH 1/3] Fix non-ASCII SSID characters in connected network display Wrap awk output in printf '%b' to decode iw's \xNN hex escapes back to the actual characters. --- bin/omarchy-network-status | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/omarchy-network-status b/bin/omarchy-network-status index b03df3bb..a460a3e2 100755 --- a/bin/omarchy-network-status +++ b/bin/omarchy-network-status @@ -120,7 +120,7 @@ print_verbose() { link=$(iw dev "$iface" link 2>/dev/null) if [[ -n $link ]]; then - printf 'ssid\t%s\n' "$(awk '/SSID:/ { sub(/.*SSID: /, ""); print; exit }' <<<"$link")" + printf 'ssid\t%s\n' "$(printf '%b' "$(awk '/SSID:/ { sub(/.*SSID: /, ""); print; exit }' <<<"$link")")" printf 'signal_dbm\t%s\n' "$(awk '/signal:/ { print $2; exit }' <<<"$link")" printf 'freq\t%s\n' "$(awk '/freq:/ { print $2; exit }' <<<"$link")" printf 'bitrate\t%s %s\n' "$(awk '/tx bitrate:/ { print $3; exit }' <<<"$link")" "$(awk '/tx bitrate:/ { print $4; exit }' <<<"$link")" From bac31ff892d0ff1f553c76b3166d80d2756bc5d0 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 31 Jul 2026 19:08:37 -0700 Subject: [PATCH 2/3] Decode escaped SSIDs after status parsing --- bin/omarchy-network-status | 2 +- shell/plugins/panels/network/Model.js | 23 ++++++++++++++++++++++- test/shell.d/network-test.sh | 9 +++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/bin/omarchy-network-status b/bin/omarchy-network-status index a460a3e2..b03df3bb 100755 --- a/bin/omarchy-network-status +++ b/bin/omarchy-network-status @@ -120,7 +120,7 @@ print_verbose() { link=$(iw dev "$iface" link 2>/dev/null) if [[ -n $link ]]; then - printf 'ssid\t%s\n' "$(printf '%b' "$(awk '/SSID:/ { sub(/.*SSID: /, ""); print; exit }' <<<"$link")")" + printf 'ssid\t%s\n' "$(awk '/SSID:/ { sub(/.*SSID: /, ""); print; exit }' <<<"$link")" printf 'signal_dbm\t%s\n' "$(awk '/signal:/ { print $2; exit }' <<<"$link")" printf 'freq\t%s\n' "$(awk '/freq:/ { print $2; exit }' <<<"$link")" printf 'bitrate\t%s %s\n' "$(awk '/tx bitrate:/ { print $3; exit }' <<<"$link")" "$(awk '/tx bitrate:/ { print $4; exit }' <<<"$link")" diff --git a/shell/plugins/panels/network/Model.js b/shell/plugins/panels/network/Model.js index 8b46db39..e2ea2592 100644 --- a/shell/plugins/panels/network/Model.js +++ b/shell/plugins/panels/network/Model.js @@ -90,7 +90,25 @@ function parseBandStatus(raw) { } } +function decodeIwSsid(value) { + var raw = String(value || "") + var encoded = "" + for (var i = 0; i < raw.length; i++) { + if (raw[i] === "\\" && raw[i + 1] === "x" && /^[0-9a-f]{2}$/i.test(raw.substring(i + 2, i + 4))) { + encoded += "%" + raw.substring(i + 2, i + 4) + i += 3 + } else { + encoded += encodeURIComponent(raw[i]) + } + } + + try { + return decodeURIComponent(encoded) + } catch (error) { + return raw + } +} function parseKeyValue(raw) { var next = {} @@ -100,7 +118,9 @@ function parseKeyValue(raw) { if (!line) continue var idx = line.indexOf("\t") if (idx === -1) continue - next[line.substring(0, idx)] = line.substring(idx + 1).trim() + var key = line.substring(0, idx) + var value = line.substring(idx + 1) + next[key] = key === "ssid" ? decodeIwSsid(value) : value.trim() } return next } @@ -319,6 +339,7 @@ if (typeof module !== "undefined") { bandSectionTitle: bandSectionTitle, bandTooltip: bandTooltip, parseBandStatus: parseBandStatus, + decodeIwSsid: decodeIwSsid, parseKeyValue: parseKeyValue, throughputState: throughputState, pingLatencyState: pingLatencyState, diff --git a/test/shell.d/network-test.sh b/test/shell.d/network-test.sh index a6838097..7807c5b7 100644 --- a/test/shell.d/network-test.sh +++ b/test/shell.d/network-test.sh @@ -26,6 +26,15 @@ assertDeepEqual( { iface: 'wlan0', rx_bytes: '100', tx_bytes: '50' }, 'network parses detail key values' ) +assertEqual(network.decodeIwSsid('Cafe\\xe2\\x80\\x99'), 'Cafe’', 'network decodes UTF-8 SSID bytes') +assertEqual(network.decodeIwSsid('\\x20Cafe\\x20'), ' Cafe ', 'network preserves edge spaces in SSIDs') +assertEqual(network.decodeIwSsid('slash\\x5cname'), 'slash\\name', 'network decodes SSID backslashes once') +assertEqual(network.decodeIwSsid('invalid\\xff'), 'invalid\\xff', 'network preserves invalid UTF-8 escapes') +assertDeepEqual( + network.parseKeyValue('ssid\tline\\x0abreak\\x09tab\\x00nul\nsignal_dbm\t-40\n'), + { ssid: 'line\nbreak\ttab\0nul', signal_dbm: '-40' }, + 'network decodes control bytes after parsing detail records' +) assertDeepEqual( network.throughputState({ prevIface: '', prevSampleTime: 0 }, { iface: 'wlan0', rx_bytes: '100', tx_bytes: '50' }, 10), { prevIface: 'wlan0', prevRxBytes: 100, prevTxBytes: 50, prevSampleTime: 10, downloadRate: 0, uploadRate: 0 }, From a734713c427db630238b9ae2632eb21798dc972b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 31 Jul 2026 19:13:52 -0700 Subject: [PATCH 3/3] Keep control escapes safe in SSID labels --- shell/plugins/panels/network/Model.js | 23 +++++++++++++---------- test/shell.d/network-test.sh | 6 ++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/shell/plugins/panels/network/Model.js b/shell/plugins/panels/network/Model.js index e2ea2592..6ab63bf9 100644 --- a/shell/plugins/panels/network/Model.js +++ b/shell/plugins/panels/network/Model.js @@ -92,18 +92,21 @@ function parseBandStatus(raw) { function decodeIwSsid(value) { var raw = String(value || "") - var encoded = "" - - for (var i = 0; i < raw.length; i++) { - if (raw[i] === "\\" && raw[i + 1] === "x" && /^[0-9a-f]{2}$/i.test(raw.substring(i + 2, i + 4))) { - encoded += "%" + raw.substring(i + 2, i + 4) - i += 3 - } else { - encoded += encodeURIComponent(raw[i]) - } - } try { + var encoded = "" + + for (var i = 0; i < raw.length; i++) { + if (raw[i] === "\\" && raw[i + 1] === "x" && /^[0-9a-f]{2}$/i.test(raw.substring(i + 2, i + 4))) { + var hex = raw.substring(i + 2, i + 4) + var byte = parseInt(hex, 16) + encoded += byte < 32 || byte === 127 ? encodeURIComponent(raw.substring(i, i + 4)) : "%" + hex + i += 3 + } else { + encoded += encodeURIComponent(raw[i]) + } + } + return decodeURIComponent(encoded) } catch (error) { return raw diff --git a/test/shell.d/network-test.sh b/test/shell.d/network-test.sh index 7807c5b7..99df2a92 100644 --- a/test/shell.d/network-test.sh +++ b/test/shell.d/network-test.sh @@ -27,13 +27,15 @@ assertDeepEqual( 'network parses detail key values' ) assertEqual(network.decodeIwSsid('Cafe\\xe2\\x80\\x99'), 'Cafe’', 'network decodes UTF-8 SSID bytes') +assertEqual(network.decodeIwSsid('Smile \\xf0\\x9f\\x98\\x80'), 'Smile 😀', 'network decodes emoji SSID bytes') assertEqual(network.decodeIwSsid('\\x20Cafe\\x20'), ' Cafe ', 'network preserves edge spaces in SSIDs') assertEqual(network.decodeIwSsid('slash\\x5cname'), 'slash\\name', 'network decodes SSID backslashes once') assertEqual(network.decodeIwSsid('invalid\\xff'), 'invalid\\xff', 'network preserves invalid UTF-8 escapes') +assertEqual(network.decodeIwSsid('already 😀'), 'already 😀', 'network safely preserves unexpected non-BMP input') assertDeepEqual( network.parseKeyValue('ssid\tline\\x0abreak\\x09tab\\x00nul\nsignal_dbm\t-40\n'), - { ssid: 'line\nbreak\ttab\0nul', signal_dbm: '-40' }, - 'network decodes control bytes after parsing detail records' + { ssid: 'line\\x0abreak\\x09tab\\x00nul', signal_dbm: '-40' }, + 'network leaves control-byte escapes safe for single-line display' ) assertDeepEqual( network.throughputState({ prevIface: '', prevSampleTime: 0 }, { iface: 'wlan0', rx_bytes: '100', tx_bytes: '50' }, 10),