Commit Graph
8 Commits
Author SHA1 Message Date
d00f ccb90ed16f l2: refresh the type column when a row is reused
The paint loop reuses existing rows and rewrites port, MAC, VLAN and the
delete button, but never the type cell - only the insert path set it. With a
fixed row order that stayed invisible, since a row usually landed back where
it was. Sorting moves rows, so every other column followed the data while
type kept the previous row's value, which made sorting by type look broken
when the sort itself was correct.
2026-08-05 21:42:54 +02:00
d00f 4389cfcd8b l2: show which column sorts and in which direction
Clicking a heading sorted the table but nothing said so afterwards - the
only feedback was the rows moving, which is no help when the sort key is a
column you are not looking at.

Give every sortable heading a permanent marker: a neutral double arrow when
it is not the sort key, up or down when it is. The marker sits in its own
span so the i18n pass, which replaces the heading text, does not wipe it.
2026-08-05 21:35:54 +02:00
d00f 5e901fb5c2 l2: sort and filter the forwarding table from its header
The table lists every learned and static entry in one flat block, which is
fine with a handful and unusable with a few hundred: finding out where one
MAC sits, or what a port has learned, meant reading the whole thing.

Make the column headings sort and give each one a filter box. Filters are
substring matches combined with AND, so "port 8 + static" is two keystrokes.
A counter above the table shows matched out of total, so a filter that hides
everything is obvious rather than looking like an empty table.

Sorting keeps the CPU entry from comparing as a number - it sorts last
instead of landing between ports 8 and 9, where a string-vs-number compare
would otherwise put it.

The filter inputs live inside the existing header cells rather than in a
second row: the paint loop addresses data rows as rows[i+1], and a second
header row would have shifted every one of them.
2026-08-05 21:21:03 +02:00
d00f 2974e4b66a l2: stop labelling the SFP port as CPU
The port column was mapped from logical to physical numbering first and the
CPU label applied afterwards, testing for port 9. On an 8+1 board the SFP
port maps to physical 9 as well, so every entry learned on the SFP was shown
as CPU - on this switch that was 26 of 30 entries.

Label the CPU while the number is still logical, before the mapping, so the
two cannot collide.
2026-08-05 21:21:03 +02:00
d00f 0f257d38df httpd: shorten comments per review
Keep only the non-obvious bits; the rationale for each change already
lives in the respective commit messages.
2026-07-25 10:39:32 +02:00
d00f 3fd9cdfa7e httpd: parse the session cookie by name, not fixed offset
Root cause of the "browser login always bounces back with Wrong password!
while curl works": scan_header() read the session id from a fixed offset
into the Cookie header (p + 17), assuming "session=" is the first and only
cookie. Browsers keep stale cookies for a long time - e.g. an "admin" cookie
left over from this switch's VENDOR firmware - so the header can arrive as
"Cookie: admin=..; session=..", the fixed offset then points into the admin
value, authentication silently fails and every page bounces to login although
the password had been accepted. curl sends only "session=", which is why
command-line tests passed while a real browser (with that stale cookie) failed.

- scan_header(): scan the Cookie header for the actual "session=" key
  (matched as "session" - is_word() requires a separator after the pattern
  and '=' is on its list, the first value byte is not).
- is_word_x(): accept ';' as a terminating separator so the session value
  also matches when it is not the last cookie in the header.

Verified on hardware end-to-end in a real browser WITH the stale "admin"
cookie present: login -> index.html, all pages and JSON endpoints work.
2026-07-21 06:48:18 +02:00
d00f 3189820ba0 httpd: send Connection: close (single-connection uIP mitigation)
Real root cause of the "web login fails from a browser but works from curl":
the uIP httpd is built with UIP_CONF_MAX_CONNECTIONS = 1 and uses global
response state (outbuf/slen/session), i.e. it serves exactly one TCP connection
at a time and closes it after each response - but never advertises that via
the Connection header. A browser's HTTP/1.1 client therefore assumes the
connection may be persistent and can park it in its keep-alive pool for reuse;
a later request sent on that pooled connection hits one the server has already
closed, and a POST (unlike a GET) is never retried by the browser, so it can
be silently lost this way.

This adds "Connection: close" to every response so the browser does not pool
and reuse a connection the server is about to drop. On its own this did not
fully explain the reported login failures - the actual authentication bug is
fixed in the next commit (the Cookie header parsed at a fixed offset) - but it
is correct behaviour for a server that only ever handles one connection, and
removes one source of dropped requests.
2026-07-21 06:47:44 +02:00
d00f ed74ec1e97 httpd/login: complete CSP + password autocomplete hint
Two hygiene fixes for the web UI, prompted by a login that appeared to fail
under privacy shields (Brave Shields / NoScript-family extensions):

- httpd: replace the partial "style-src 'self' 'unsafe-inline'" CSP with a
  complete, first-party policy (default-src 'self'; script-src 'self'
  'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
  connect-src 'self'; form-action 'self'). Everything the UI needs is
  same-origin; the explicit policy stops shields injecting their own
  restrictive report-only probes (the noisy script-src-elem 'none' console
  spam) and passes a strict-CSP audit. Verified: no CSP violations in-browser.
- login.html: add autocomplete="current-password" so password managers
  recognise the field (they showed "unknown password" without it).

NOTE: these do NOT bypass a browser's LAN-device protection (NoScript "lan" /
Brave Shields), which strips the POST body of requests to a LAN address and is
why the login can fail in-browser while the same credentials work over curl.
That is a deliberate browser security feature; the user must allow the site in
their shields to log in. The backend password (default 1234) is unchanged and
correct.
2026-07-21 06:47:10 +02:00