- LuCI: rework the wireless scan dialog; card list instead of the result table, mobile friendly
- LuCI: add the per uplink revive option to the uplink dialog (off / 10 / 30 / 60 run cycles)
Signed-off-by: Dirk Brenken <dev@brenken.org>
CARTO now requires an API key for the raster basemaps at basemaps.cartocdn.com and watermarks every unauthenticated tile request, so the GeoIP map currently renders "API KEY REQUIRED" across the whole viewport.
The basemap is now drawn from country outlines that ship with the package: Natural Earth 1:110m, public domain, all attributes stripped, simplified to 36 kB (~10 kB in the apk).
Signed-off-by: Dirk Brenken <dev@brenken.org>
CARTO now requires an API key for the raster basemaps at basemaps.cartocdn.com and watermarks every unauthenticated tile request, so the GeoIP map currently renders "API KEY REQUIRED" across the whole viewport.
The basemap is now drawn from country outlines that ship with the package: Natural Earth 1:110m, public domain, all attributes stripped, simplified to 36 kB (~10 kB in the apk).
Signed-off-by: Dirk Brenken <dev@brenken.org>
Companion frontend for the shunt policy-based routing daemon
(openwrt/packages#30365). Provides an overview with live service state,
per-policy rule and route counters and learned-address totals, a policy
editor covering all selectors (client address and MAC, destination,
domain, port, protocol), set reporting with per-element counters, and a
filtered processing log. Status data comes from a ucode rpcd object
shipped with the base package.
Signed-off-by: Dirk Brenken <dev@brenken.org>
Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Dirk Brenken <dev@brenken.org>
Top- and Worst-Lists rendered the identical 'elements / packets' string, so a
set appearing in both showed the same numbers twice with two very
different bars. Worst Sets now shows the value it is ranked by.
Signed-off-by: Dirk Brenken <dev@brenken.org>
- rework the Set report view into a card based dashboard layout
- rework the banIP frontpage into a card based dashboard layout
Signed-off-by: Dirk Brenken <dev@brenken.org>
- rework the travelmate frontpage into a card based dashboard layout
- sync with base package, release 2.4.7-1
Signed-off-by: Dirk Brenken <dev@brenken.org>
- rework the DNS report view into a card based dashboard layout
- rework the adblock frontpage into a card based dashboard layout
Signed-off-by: Dirk Brenken <dev@brenken.org>
rpcd e37ed9d8 ("file: re-authorize ACL against resolved path to close
symlink bypass", GHSA-q5gr-86pq-vvwr) re-resolves the requested path
with realpath() and re-runs the ACL check against the resolved target
for file.read, file.write, file.md5, file.list and file.stat.
On OpenWrt /var is a symlink to /tmp (or /var/run to /tmp/run with
CONFIG_TARGET_ROOTFS_PERSIST_VAR), so every "/var/run/travelmate/*"
grant resolves to "/tmp/run/travelmate/*", which is not covered by the
ACL. The second check therefore fails with EACCES.
Independently of that, rpcd authorizes file.stat with the "list"
permission while the ACL only granted "read", and
rpc_session_acl_allowed() matches the permission literally via
fnmatch().
Both defects hit the same call sites: the fs.stat() probes in
overview.js and stations.js gate the subsequent fs.read_direct(), so
the runtime status poll and the station refresh handler returned early
without ever loading their payload. The errors stay invisible because
the probes are wrapped in L.resolveDefault(). Reading the payload
itself is unaffected since fs.read_direct() and fs.exec_direct() go
through cgi-io, which does not resolve symlinks, and fs.remove() is
exempt because file.remove keeps its no-follow semantics.
Also grant "list" on the /etc/travelmate directory itself. The auto
login script option is a form.FileUpload with root_directory set to
that path, and ui.FileUpload lists a directory via fs.list(path),
which is authorized against the directory. The existing
"/etc/travelmate/*.login" grant only covers the fs.stat() of an
already selected file, so the browser listing came back empty. This
is a pre-existing defect unrelated to the rpcd change.
- grant the resolved /tmp/run counterpart for every /var/run path;
both names denote the same file, so no additional object becomes
reachable
- keep the /var/run entries so the ACL stays valid on targets where
/var is a real directory
- grant "list" on the objects probed with fs.stat()
- grant "list" on /etc/travelmate for the file browser listing
- bump PKG_RELEASE
Signed-off-by: Dirk Brenken <dev@brenken.org>
rpcd e37ed9d8 ("file: re-authorize ACL against resolved path to close
symlink bypass", GHSA-q5gr-86pq-vvwr) re-resolves the requested path
with realpath() and re-runs the ACL check against the resolved target
for file.read, file.write, file.md5, file.list and file.stat.
On OpenWrt /var is a symlink to /tmp (or /var/run to /tmp/run with
CONFIG_TARGET_ROOTFS_PERSIST_VAR), so every "/var/run/banIP/*" grant
resolves to "/tmp/run/banIP/*", which is not covered by the ACL. The
second check therefore fails with EACCES.
Effects in the set report view: the banIP.report flag file can no
longer be written or polled, so every 'Refresh' run ends in the
report error handler after the retry limit, and the IP search dialog
fails the same way. Both errors stay invisible because the calls are
wrapped in L.resolveDefault(). Report generation itself is unaffected
since fs.exec_direct() goes through cgi-io, which does not resolve
symlinks.
Independently of that, rpcd authorizes file.stat with the "list"
permission while the ACL only granted "read", and
rpc_session_acl_allowed() matches the permission literally via
fnmatch(). The fs.stat() probes in overview.js, allowlist.js,
blocklist.js and feeds.js were therefore denied, so the runtime status
poll returned early and the list views fell back to an empty result.
- grant the resolved /tmp/run counterpart for every /var/run path
- keep the /var/run entries so the ACL stays valid on targets where
/var is a real directory
- grant "list" on the objects probed with fs.stat(); this does not
widen the path scope, the same objects are already readable
- bump PKG_RELEASE
Signed-off-by: Dirk Brenken <dev@brenken.org>
rpcd e37ed9d8 ("file: re-authorize ACL against resolved path to close
symlink bypass", GHSA-q5gr-86pq-vvwr) re-resolves the requested path
with realpath() and re-runs the ACL check against the resolved target
for file.read, file.write, file.md5, file.list and file.stat.
On OpenWrt /var is a symlink to /tmp (or /var/run to /tmp/run with
CONFIG_TARGET_ROOTFS_PERSIST_VAR), so every "/var/run/adblock/*" grant
resolves to "/tmp/run/adblock/*", which is not covered by the ACL. The
second check therefore fails with EACCES.
Effects in the DNS report view: the adblock.report flag file can no
longer be written or polled, so every 'Refresh...' run ends in
"Failed to generate adblock report!" after the retry limit. The
'Blocklist Search...' dialog fails the same way. Both errors stay
invisible because the calls are wrapped in L.resolveDefault(). Report
generation itself is unaffected since fs.exec_direct() goes through
cgi-io, which does not resolve symlinks.
Independently of that, the runtime status polling in overview.js gates
on fs.stat(), and rpcd authorizes file.stat with the "list" permission
while the ACL only granted "read". rpc_session_acl_allowed() matches
the permission literally via fnmatch(), so the call was denied and the
poll callback returned early without updating the status.
- grant the resolved /tmp/run counterpart for every /var/run path
- keep the /var/run entries so the ACL stays valid on targets where
/var is a real directory
- grant "list" on adblock.runtime.json for the fs.stat() probe
- bump PKG_RELEASE
Signed-off-by: Dirk Brenken <dev@brenken.org>
Requiring a non-empty as, city and countryCode discarded every entry whose GeoIP lookup returned
those fields empty, which happens regularly. Only the coordinates are mandatory now, missing
fields render as "n/a". Coordinate validation relied on truthiness, so entries at latitude or
longitude 0 were dropped as well, and coordinates are range checked now.
Popups are built as DOM nodes instead of concatenated HTML, so neither the blocked domain nor the
city and AS fields returned by the GeoIP service can be interpreted as markup.
The setView() call ran inside the home marker loop, so on a dual stack setup only the last local
IP decided the initial viewport. Center on a single home IP, fit the bounds of all of them
otherwise, and fall back to the blocked domains when no home IP could be resolved.
Blocked ad domains cluster heavily on a few CDN endpoints,
so several of them regularly resolve to one coordinate.
Markers sharing a coordinate now carry all of their domains and list them as a domain
with an indented IP and AS, capped at five per popup.
The same grouping reports a second local
uplink resolving to the same location instead of dropping it.
The blocked marker radius goes from 2 to 3 to match luci-app-banip.
Home markers are rendered after the blocked ones so the local uplink stays visible and clickable.
The fixed 97vh map height is replaced by height: 100%, so the map fills the report modal exactly.
Signed-off-by: Dirk Brenken <dev@brenken.org>
The setView() call ran inside the home marker loop, so on a dual stack setup only the last local
IP decided the initial viewport. Center on a single home IP, fit the bounds of all of them
otherwise, and fall back to the blocked IPs when no home IP could be resolved.
Requiring a non-empty as, city and countryCode discarded every IP whose GeoIP lookup returned
those fields empty, which happens regularly. Only the coordinates are mandatory now, missing
fields render as "n/a".
Coordinate validation relied on truthiness, so entries at latitude or longitude 0 were dropped as
well. Coordinates are now range checked.
Popups are built as DOM nodes instead of concatenated HTML, so the city and AS fields returned by
the GeoIP service can no longer be interpreted as markup.
Add a toolbar above the map holding a single select, listing every Set with mappable entries plus
its point count. Selecting a Set renders that Set only and zooms to it. No cap is applied, so the
rendering does not depend on how f_report() orders or truncates set_elements.
Markers sharing a coordinate are collapsed per Set instead of globally. The previous deduplication
compared lat/lon only, so the Set that reached a coordinate first swallowed the points of every
later Set, which would make both the per-Set counts and the filter depend on the order of
ban_map.jsn. Collapsing per Set keeps every Set complete, and the number of IPs behind a marker is
reported in its popup, since stacked markers are indistinguishable and only the topmost one can be
opened.
The marker radius is raised from 2 to 3 to keep the denser plot readable, and the map
renders with the canvas backend to keep the volume cheap.
Home markers are raised to the front
after every redraw, so the local uplink stays visible and clickable.
The fixed 97vh map height is replaced by a flex column layout, so the map fills the space left by
the toolbar inside the report modal. The toolbar uses the CSS system colors Canvas, CanvasText and
ButtonBorder under color-scheme: light dark, which also keeps it consistent with the natively
rendered select.
Co-authored-by: Renato Yamane <renatoyamane@gmail.com>
Signed-off-by: Dirk Brenken <dev@brenken.org>
The frontend calls fs.stat(), fs.read() and fs.write(), which are ubus
calls on the "file" object and require the corresponding ubus ACL in
addition to the already existing path ACLs. Only "list" and "remove" are
granted by luci-base, so for non-root sessions every stat resolved to
null and saving the allow-/blocklist and the custom feeds failed.
Signed-off-by: Dirk Brenken <dev@brenken.org>
The frontend calls fs.stat(), fs.read() and fs.write(), which are ubus
calls on the "file" object and require the corresponding ubus ACL in
addition to the already existing path ACLs. Only "list" and "remove" are
granted by luci-base, so for non-root sessions every stat resolved to
null and saving the allow-/blocklist and the custom feeds failed.
Signed-off-by: Dirk Brenken <dev@brenken.org>
Two remaining ACL gaps for non-root sessions:
* The status poll calls fs.stat() before reading the runtime file, and
the uplink handling calls fs.exec(). Both are ubus calls on the "file"
object and therefore require the corresponding ubus ACL, which was
never granted. luci-base only grants "list" and "remove". The stat call
silently resolved to null, the poll returned early and the spinner of
the information section kept running without any error message.
* Creating an uplink adds a wifi-iface section to the wireless config,
but the write section only granted uci access to the travelmate config.
This went unnoticed as long as the session also held write access via
luci-mod-network-config.
The ubus grants are placed in the read section, matching the section the
corresponding file path ACLs already live in.
Fixes: https://github.com/openwrt/luci/issues/8850
Fixes: https://github.com/openwrt/luci/issues/8849,
please note: luci-base is still a mandatory baseline grant for every LuCI user!
Signed-off-by: Dirk Brenken <dev@brenken.org>
Three related ACL defects:
* The frontend reads the feed, category and list files as well as the
runtime information via fs.read_direct(), which is served by cgi-io's
download handler. That handler requires the "cgi-io"/"download" scope,
which was not granted, so all of these requests failed with a 403 for
non-root sessions.
* The custom feed import uses ui.uploadFile(), which is served by
cgi-io's upload handler and requires the "cgi-io"/"upload" scope in
the write section. It was missing as well.
* All file entries lived in the write section, including the read-only
ones. Since rpcd pulls a section in via the matching list in
/etc/config/rpcd, a session holding only "list read luci-app-banip"
had no read access to any of the files at all. Move the read
permissions to the read section and keep only the write permissions in
the write section.
The effective permission set of a session holding both lists is
unchanged.
Signed-off-by: Dirk Brenken <dev@brenken.org>
Three related ACL defects:
* The frontend reads the feed, category and list files as well as the
runtime information via fs.read_direct(), which is served by cgi-io's
download handler. That handler requires the "cgi-io"/"download" scope,
which was not granted, so all of these requests failed with a 403 for
non-root sessions.
* The custom feed import uses ui.uploadFile(), which is served by
cgi-io's upload handler and requires the "cgi-io"/"upload" scope in
the write section. It was missing as well.
* All file entries lived in the write section, including the read-only
ones. Since rpcd pulls a section in via the matching list in
/etc/config/rpcd, a session holding only "list read luci-app-adblock"
had no read access to any of the files at all. Move the read
permissions to the read section and keep only the write permissions in
the write section.
The effective permission set of a session holding both lists is
unchanged. Also drop the stale adblock.pid entry, it has no consumer in
the frontend anymore.
Signed-off-by: Dirk Brenken <dev@brenken.org>
- added the missing "download" scope to cgi-io Path access remains restricted by the
existing file ACL, so no additional file becomes readable. Also drop the
stale travelmate.pid entry, it has no consumer in the frontend anymore.
Fixes: https://github.com/openwrt/luci/issues/8840
Signed-off-by: Dirk Brenken <dev@brenken.org>
The nftables status view rendered the byte/packet badge by reading
.bytes/.packets directly off a rule's counter statement. That only
holds for anonymous (inline) counters, whose JSON statement is an
object. Named counter objects (counter name "<n>") are serialized as
a bare name string instead, so .bytes was undefined and the badge
showed "undefinedB". Named counters are used in banIP for example.
Resolve named counter references against the standalone counter
objects of the same family and table before rendering, and skip the
badge when a reference cannot be resolved instead of emitting garbage.
While at it, replace some withespaces with tabs.
Signed-off-by: Dirk Brenken <dev@brenken.org>
* ucode fixes:
- tighten `is_valid_lxc_name` regex to `^[A-Za-z0-9_][A-Za-z0-9_-]{0,63}$`
- apply the validator in `lxc_configuration_get` and `lxc_configuration_set` before any filesystem access
- reject the `'lxc error: …'` sentinel string returned by `lxc_get_config_path()` on failure,
rather than concatenating it into a path.
- shellquote `LXC_URL` in `lxc_get_downloadable` and `lxc_create`
* ACL fix: add `depends.acl = ["luci-app-lxc"]` to each of the five backend entries,
so the routes share the same authorization gate as the view
Signed-off-by: Dirk Brenken <dev@brenken.org>
- fixed promise chaining and ifup race in handleAction()
- fixed WPA3/SAE QR-codes and escape special characters
- stabilized the setup modal handler (return promise, add catch)
- fixed the cipher classification for WPA/WPA2 scan results: resolveCipher()
- various other fixes and improvements
Signed-off-by: Dirk Brenken <dev@brenken.org>
Fixes errors in the display/workflow of scan results:
- Display of stations with a strength of "100"
- Display/Workflow of "hidden" SSIDs
Signed-off-by: Dirk Brenken <dev@brenken.org>