Skip to content

Release 1.0.7 - #27

Merged
anothaDev merged 30 commits into
mainfrom
release-1.0.7
Oct 14, 2026
Merged

anothaDev merged 30 commits into
mainfrom
release-1.0.7

Conversation

@anothaDev

Copy link
Copy Markdown
Owner

Fixes from the 1.0.6 review round, a sweep of real OpenWrt 25.12 and 24.10 routers, and two Codex checks: firewall overview now follows fw4's semantics, row action menus fold less, nftables/log/phone-card styling, and three LuCI 24.10 markup fixes.

Fixes #26.

…xt, chart picker across the bar on phones, restored history bounded to the rate ceiling
The row-folding rule hid the reason a few pages exist: Wake on LAN's
Wake went into the menu, Startup kept only the boot toggle visible, and
Processes kept Hang Up and hid Terminate and Kill. Wireless radio rows
hid Add, which is how a radio gets a network.

A row now folds only when it has a main action (Edit, else LuCI's
"important" button) and at least two other buttons to put in the menu.
Add, "important" and plain action buttons (an app's own) stay visible,
and a menu is never built for a single item. Interfaces, Wireless
network rows, Routing, the firewall tables and DDNS fold as before;
Wake on LAN, Startup, Processes and radio rows are back to their 1.0.5
layout.
An action chosen from a ⋯ menu runs on its hidden original, where LuCI
puts its spinner, so a multi-second restart looked ignored. ⋯ now shows
the spinner (and aria-busy) while any hidden original spins, and stays
clickable for the row's other actions. A per-row observer picks up the
class, disabled, hidden and label changes LuCI makes, which the body's
childList observer misses; it writes only what changed.

Keyboard: Escape closes a menu opened with the mouse or a tap (a
document listener while open, in the capture phase), Tab from ⋯ closes
it, and Tab or Shift+Tab inside the menu returns focus to ⋯ first, so
focus moves on from the row instead of the page's start or end.

Also: ⋯ has aria-controls; items carry LuCI's button titles (and use
them as the label for a button without text); an open menu is refilled
when LuCI disables, relabels or hides an original, and closes when the
row no longer folds; when neither side has room (a landscape phone) the
menu takes the roomier side with a max-height and scrolls, instead of
covering its own button. Scrolling inside the menu no longer closes it.
Light mode: the Reject/Drop badges (--err on --err-soft) fell to 4.40:1
on a hovered row, the NAT/forward badges to 4.53:1 and the amber input
policy of the masquerading zone to 4.31:1. New --err-text, --info-text
and --warn-text tokens (darker in light mode, the same colours in dark
mode) carry the text; borders and fills stay as they were. The danger
button's hover text uses --err-text too (4.40 -> 5.23:1). Measured with
backgrounds composited: badges 5.23, 5.13 and 5.25:1 hovered.

A switched-off rule was faded to opacity .45, which put its name at
2.9:1 and the family line and verdict at 1.9:1, and faded the row's
separator. It is now greyed instead: text in --text-3 (5.5:1 light,
5.6:1 dark) and the verdict and zone badges in grayscale, so it reads
as off and stays readable.

The test computes the badge and warning contrast from the tokens, at
rest and on the hovered row, in both schemes.
…4 px rule switch on phones

Firewall -> General Settings: from 721 to 985 px the zones table was
wider than its card and Edit/Delete scrolled out of view with no cue
(new at 901-985 px, because "Intra zone forward" and "IPv4
Masquerading" did not wrap). Its headers now wrap and the policy
selects keep 6.5em, so the table fits down to about 880 px again (814
-> 775 px wide). Below that it scrolls inside the card as before, and
a card holding a table section now shows a shade on the side that has
more table: covers in the card colour scroll with the content and hide
the shade at either end, so a table that fits looks unchanged.

Phone rule cards: the on/off box in the corner was a 20x20 target.
LuCI's own empty <label for> the box is stretched over a 44x44 square
around it, so a tap there toggles the box; the box keeps its size.
luci-app-vantage: fwglance now follows fw4's own parsing and chain
layout (firewall4 as 25.12 ships it) instead of the raw uci fields, so
the overview no longer says "Nothing is open" for configurations that
let the internet in.

- Options parse like fw4's parse_options: one boolean parser
  (1/on/true/yes, 0/off/false/no), policies and targets by prefix, and a
  value fw4 refuses skips the section, listed as not in effect.
- Disabled or skipped zones do not exist: rules naming them are
  skipped, and their networks, uplinks no zone holds and the family a
  zone leaves out meet the defaults policies. Policies fall back to
  defaults, then DROP.
- Forwardings from '*', defaults.forward ACCEPT, and unrestricted accept
  rules into a zone warn as internet-to-network paths; the zone map shows
  the default-policy fallback.
- Protocols (6/17/1/58, tcpudp, all ignoring ports, a dropped '!'),
  effective ports and sources (/0 and negated addresses are no
  restriction) are normalised once and drive descriptions, counts and the
  admin-port warning; descriptions carry the address family.
- Redirects: every target but snat is a DNAT, without dest_ip (or to a
  router address) it is local, with external and internal ports and the
  admin check on the internal port; DNAT from '*' or without a source is
  skipped.
- IKE/ESP/AH to the router itself is an opening; only forwarded IPsec
  stays housekeeping.
- Public uplink addresses follow the IANA special-purpose registry
  (non-global entries, most specific wins; 192.88.99.2 included).
- Includes are "configured"; a script include without fw4_compatible is
  shown as ignored.
- A failed config read is an error state; no internet-facing zone is a
  neutral "unknown" state, never green.

tests: a regression per review reproduction, the registry table, the
view's wording, and an optional run of the shipped fw4.uc (UCODE and the
rootfs dump) checking the ruleset it writes for the same configs.
…only

security-tests: the firewall overview's module closure is pinned
(view, rpc, ui, vantage.fwglance, baseclass), no app file may require a
LuCI module that loads other configs (network, firewall, tools.*, form,
uci, fs) or call L.require with anything but a literal vantage.* name,
and require directives are read with luci.js's own scanner. A declared
rpc function may only be called directly, so an alias, .call or passing
it around fails, and declares are counted however they are spelled.
Both checks have negative controls that mutate an in-memory copy of
the sources and must be caught for the expected reason. No ACL change.
docs/SPEC.md and README.md: the overview reads the config as fw4 does,
lists what is ignored, shows uplinks and families in no zone, covers
forwarding paths through '*' and the default policy, treats IKE/ESP to
the router as an opening, says includes are configured, and has
unknown and error states.
…clipped at mid widths, palette keeps Escape, lanes label between the lanes, client history bounded, per-file ACL call check, plural rule counts
…narrow tiles, ellipsis on node text, CPU sparkline under the numbers when narrow
luci-theme-vantage: Status -> Firewall (nftables) had no theme styling
on LuCI 26.x: each chain is now a card with its heading as the header
row, hook and policy under it and the rules edge to edge. The view
injects its own cell padding with !important, so the cells outweigh it
by id; matches are inline text, so on phones they wrap instead of
turning every text run into a flex column, and the comment and counter
marks are muted.

System Log and Kernel Log: the new filter controls are laid out as
wrapping rows (LuCI's inline margins give way to the gap; fields on
their own line on phones). The read-only log is a viewer: it keeps
its 12 px text on touch instead of the 16 px field size, and wraps on
phones (LuCI sets wrap="off" and an inline white-space).

tests: CSS checks for both.
luci-theme-vantage, from a look-only sweep of OpenWrt 25.12.5 (LuCI
26.180):
- Interfaces: the device details tooltip is a badge in a cell, and the
  cell's badge reset left it transparent over the rows below; it keeps
  the tooltip box, icon beside the details.
- The crumb switch caret was a filled shape, black on the dark top bar;
  it is stroked like the other shell icons.
- Unzoned items (LuCI's 238, 238, 238, written with and without a space)
  use the theme's neutrals instead of the brightest edge on a dark page.
- Phone cards: one inset for every cell, also in free-standing tables
  (Software, Diagnostics); empty cells go with their label; LuCI's wide
  columns (col-7 and up: package descriptions, connection addresses)
  take the card's width and long IPv6 addresses wrap; a zone and its
  forwards read across the card; a div.btn alone in the actions cell
  (Software's Install) stays an inline button.
- Diagnostics: three equal columns; Software: the controls line up with
  the page and "Update lists" is a plain button; Connections: "Enable
  DNS lookups" is a view toggle, not the lime primary.
- Repo Public Keys: a header row short of the actions column gets the
  missing cell, so its line runs the table's width.
- Forms: Sync buttons sit 8 px under Local Time, a short free-text
  option (Notes, no rows) is as wide as the fields, help under a
  checkbox sits 8 px under the box, ComboButton labels no longer clip
  their descenders, and "No active leases found" is upright like the
  other placeholders.

docs: SPEC notes the stock-page rules. tests: CSS checks for each.
luci-theme-vantage, from a look-only sweep of OpenWrt 24.10.8. Each fix
matches 24.10's markup, not its version, and matches nothing on 25.12:
- Status -> Overview: 24.10's sections are #view's children with a bare
  h3 first (25.12 wraps it in .cbi-title), so "#view > div > h3" pushed
  the card's header row down; the heading keeps the header-row bleed.
- The memory and storage meters (menu-vantage.js statusSections) also
  find that bare heading, so the enhancers run on 24.10.
- Traffic Rules, Port Forwards, NAT Rules: 24.10 renders no name cell
  and leaves the row's data-title for the theme to draw. The name is
  drawn as the first cell (and "Name" in the header row, which only 24.10
  marks with data-title), and heads the card on phones; 25.12's rows,
  which have a real name cell, are excluded.

docs: SPEC notes the 24.10 support. tests: CSS checks against 24.10
and 25.12 markup, and the enhancers against both overview layouts in a
small fake DOM.
…ow menu exists before aria-controls names it
security-tests: the loader check accepted a quoted vantage.* prefix and
ignored what followed it, so L.require('vantage.x' + y) passed. The
literal must now close the call. Negative controls for a concatenated
name and for a second argument.
luci-app-vantage, view/vantage/firewall.js: both calls declared an
`expect` default, so LuCI's rpc.js resolved a ubus error status (permission
denied, unspecified error, ...) to {} or []. An unreadable firewall config
then read as an access point, and a failed interface dump as a complete
one, which hid an uplink outside every zone behind "Nothing is open".
Both now reject on a ubus error; load() checks the reply's shape and
treats anything else as no data: an error page for the config, unknown
uplinks for the dump. The ACL and the two calls are unchanged.

tests: the view stubs return the ubus reply as rpc.js hands it over, and
new regressions feed status frames ([6], [9], [0], malformed values or
interface lists, a JSON-RPC error) through LuCI's own rpc.js from the
rootfs dump (skipped without it), plus a check of the declarations.
luci-stub defines N_.
luci-app-vantage, vantage/fwglance.js: fw4 matches a zone on the devices
its networks resolve to, so networks sharing a device are one zone member.
The model compared network names, so a modem's dynamic wan_4 (on wan's
wwan0) read as outside every zone, and IPv4 wan and IPv6 wan6 zones on
one device each reported the other family as unzoned: false "in no zone"
warnings. With the interface dump, network entries now resolve to their
device (fw4's l3_device, else device; a network without one matches
nothing), networks the dump assigns a zone (data.zone) join it, and the
complementary-family check asks whether another zone takes everything
the family-limited zone takes. Device wildcards and exclusions match as
before; fw4 skips a wildcard that is not at the end. Without the dump,
networks still compare by name.

tests: Codex's modem alias and split-family cases, wildcard and exclusion
guards, and the same cases against fw4's own ruleset and the overview.
luci-app-vantage, vantage/fwglance.js: a redirect counted as local only
when its dest_ip was spelled exactly as an address in the dump, so
192.0.2.1/32 to the router's 192.0.2.1 read as a forward into the
network, without the admin warning, while fw4 DNATs it to the router.
Addresses are now parsed into bytes the way inet_pton reads them (no
leading zeros, IPv6 compression and embedded IPv4), and dest_ip is
compared after the masking fw4 applies (192.0.2.1/24 is 192.0.2.0, a
host) against every address of the router, IPv6 prefix-assignment local
addresses included; a range counts when it holds one. fw4 refuses a
non-contiguous mask in dest_ip, and so does the model. Without the dump
the target is unknown: the redirect stays under "Into your network" and
an admin port on it raises a new caution, admin-unsure, instead of
passing as a host.

view/vantage/firewall.js: the admin-unsure warning text. po: two new
strings, translated to zh_Hans.

tests: masked, mask-spelled, ranged and IPv6 spellings of the router's
address, subnet bases and hosts, the no-dump case, two existing tests
that now pass a dump, and the masked case against fw4 and the overview.
luci-app-vantage, vantage/fwglance.js: a zone value fw4 refuses in an
option the model did not parse (log 'yes', an invalid subnet, an unknown
masq_src network, ...) left the zone in effect, so with an accepting
default Input policy the page said "Nothing is open" while fw4 skipped
the zone and its networks met that policy. Zones now go through fw4's
whole option list with its parsers (ucode's number conversion for log,
rate limits, the helpers firewall4 defines, networks resolved through
the dump) and a refused value skips the zone, which the existing
outside-input warning then reports. A network name the model cannot
check without the dump keeps the zone; the accepting default then
already raises uplinks-unknown. The defaults section's limit, number and
reject-code options are checked the same way.

A valid subnet narrows a zone too: fw4 takes only those addresses, and
of a family without one, none, so the rest of the internet meets the
default policy. An internet-facing zone with covered subnets under an
accepting default Input now raises a new error, zone-subnet.

view/vantage/firewall.js: the zone-subnet warning text. po: two new
strings, translated to zh_Hans.

tests: each refused option, accepted spellings, defaults options, the
covered-subnet cases and the no-dump caution, against fw4's ruleset and
the overview as well.
luci-app-vantage, vantage/fwglance.js: any positive ipset counted as a
source restriction, so a rule matching a destination set (fw4: tcp dport
22 ip daddr @mine accept) read as "from @mine" and dropped the router
admin warning, though anyone may connect. The model now reads the
config's ipset sections with fw4's options and checks, and a set
restricts the source only when one of its address fields (ip or net)
matches the source: the reference's direction ('mine dst'), else the
set's own match. A destination, negated, MAC- or port-only set, or a
set the config does not define (it may come from a service at run time,
or fw4 skips the rule), is no allowlist. The ipset option is one value,
so 'mine dst' is one set; the set's family is the rule's, as fw4 infers
it.

tests: destination, redirected, negated, unknown, off and refused sets,
genuine source sets, and the destination-set case against fw4 and the
overview.
# Conflicts:
#	luci-app-vantage/po/templates/vantage.pot
#	luci-app-vantage/po/zh_Hans/vantage.po
@anothaDev
anothaDev merged commit ee1cabd into main Oct 14, 2026
4 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.

Bug

1 participant