httpd: don't let a quote in a port name break the web page

A port named NAS"1 made status.json unparsable, so the page stopped
loading, and the name survives a reboot in the saved config. VLAN names
and the SFP strings have the same problem.

They now go through a helper that replaces ", \ and non-printable bytes
with a dot, the way the hostname is already sanitized on ingest. The
length does not change, so the size checks still hold.

The helper also covers the 0xa0 byte that send_sfp_info() skipped as a
special case, so that check is gone.
This commit is contained in:
d00f committed 2026-09-17 22:29:57 +02:00
1 parent 0cc09e4015
commit 8626b4c1d4
1 file changed
+16 -8
+16 -8
View File
@@ -70,6 +70,14 @@ void char_to_html(char c)
} }
void json_char_to_html(uint8_t c)
{
if (c < 0x20 || c > 0x7e || c == '"' || c == '\\')
c = '.';
outbuf[slen++] = c;
}
// Convert uint8_t to ascii HEX char. // Convert uint8_t to ascii HEX char.
void byte_to_html(uint8_t val) void byte_to_html(uint8_t val)
{ {
@@ -198,8 +206,8 @@ void send_sfp_info(uint8_t sfp)
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
continue; continue;
uint8_t c = sfp_buf[i & 0xf]; uint8_t c = sfp_buf[i & 0xf];
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM if (c)
char_to_html(c); json_char_to_html(c);
} }
} }
@@ -293,7 +301,7 @@ void send_vlan(uint16_t vlan)
dbg_string("VLAN has no name\n"); dbg_string("VLAN has no name\n");
} else { } else {
while(vlan_names[n] && vlan_names[n] != ' ') while(vlan_names[n] && vlan_names[n] != ' ')
char_to_html(vlan_names[n++]); json_char_to_html(vlan_names[n++]);
} }
slen += strtox(outbuf + slen, "\",\"pvid\":\"0x"); slen += strtox(outbuf + slen, "\",\"pvid\":\"0x");
uint16_t pvid_mask = 0; uint16_t pvid_mask = 0;
@@ -825,7 +833,7 @@ void send_status(void)
itoa_html(i); itoa_html(i);
slen += strtox(outbuf + slen, ",\"name\":\""); slen += strtox(outbuf + slen, ",\"name\":\"");
for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) { for (uint8_t j = 0; j < PORT_NAME_SIZE && port_names[i][j]; j++) {
char_to_html(port_names[i][j]); json_char_to_html(port_names[i][j]);
} }
slen += strtox(outbuf + slen, "\""); slen += strtox(outbuf + slen, "\"");
@@ -865,13 +873,13 @@ void send_status(void)
} }
slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\""); slen += strtox(outbuf + slen,"\",\"sfp_vendor\":\"");
for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++) for (uint8_t s = 0; s < 16 && sfp_module_vendor[sfp][s]; s++)
outbuf[slen++] = sfp_module_vendor[sfp][s]; json_char_to_html(sfp_module_vendor[sfp][s]);
slen += strtox(outbuf + slen,"\",\"sfp_model\":\""); slen += strtox(outbuf + slen,"\",\"sfp_model\":\"");
for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++) for (uint8_t s = 0; s < 16 && sfp_module_model[sfp][s]; s++)
outbuf[slen++] = sfp_module_model[sfp][s]; json_char_to_html(sfp_module_model[sfp][s]);
slen += strtox(outbuf + slen,"\",\"sfp_serial\":\""); slen += strtox(outbuf + slen,"\",\"sfp_serial\":\"");
for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++) for (uint8_t s = 0; s < 16 && sfp_module_serial[sfp][s]; s++)
outbuf[slen++] = sfp_module_serial[sfp][s]; json_char_to_html(sfp_module_serial[sfp][s]);
slen += strtox(outbuf + slen,"\",\"sfp_los\":"); slen += strtox(outbuf + slen,"\",\"sfp_los\":");
if (machine.sfp_port[sfp].pin_los == GPIO_NA) { if (machine.sfp_port[sfp].pin_los == GPIO_NA) {
slen += strtox(outbuf + slen,"null"); slen += strtox(outbuf + slen,"null");
@@ -1043,7 +1051,7 @@ void send_vlanlist(void)
n = vlan_name(i); n = vlan_name(i);
if (n != 0xffff) { if (n != 0xffff) {
while (vlan_names[n] && vlan_names[n] != ' ') while (vlan_names[n] && vlan_names[n] != ' ')
char_to_html(vlan_names[n++]); json_char_to_html(vlan_names[n++]);
} }
slen += strtox(outbuf + slen, "\"}"); slen += strtox(outbuf + slen, "\"}");