Add a standalone fixture using the real topology renderer and styles.
Check painted pixels in WebKit, Chromium and Firefox because DOM bounds
can remain correct when WebKit paints foreignObject content incorrectly.
Exercise node clicks, collapse/expand, wheel zoom and drag pan, including
assertions that pointer gestures change the SVG transform.
Document dependency installation and execution separately from pytest.
The regression fails with the original frontend and passes with the fix.
Keep encode_python_string() in front/php/server/util.php, replacing
encode_single_quotes() in place, and remove the standalone
app_conf_encode.php helper file and its require.
Rework the regression test to dispatch the real util.php savesettings
path through the PHP CLI against temporary synthetic config, API and
session directories, covering both scalar string and array string
settings: the generated app.conf must compile with warnings as errors
and parse back to the typed values.
The existing {s-quote} lifecycle is preserved (single quotes are still
written as {s-quote} and converted back by the same consumers), and the
app.conf readers in initialise.py and plugin_helper.py are unchanged.
app.conf is written by util.php saveSettings() as Python source and later
compiled/exec'd by the backend. String settings were emitted into
single-quoted Python literals with only ' encoded (as {s-quote}), so
backslashes were written raw. A regex such as 192\.0\.2\..* then produced
"SyntaxWarning: invalid escape sequence '\.'", valid Python escapes such as
\n were silently reinterpreted, and a trailing backslash made the file fail
to compile.
Move the encoder into a side-effect-free helper, app_conf_encode.php, as
encode_python_string(). It now doubles backslashes before the existing
{s-quote} replacement, so backslashes round-trip unchanged through app.conf
serialization and Python parsing, while single quotes keep using the legacy
{s-quote} placeholder. Both scalar string and array string serialization use
the helper.
Add regression tests that run the real PHP helper through the PHP CLI,
compile the generated source with warnings promoted to errors, and check
the scalar and array round trips.
check_trusted_aps() evaluated a rogue AP once per trusted_aps entry sharing
its SSID, so the documented main-AP+extender pattern (same SSID, two
entries) produced duplicate (bssid, motor) rows for a real clone - a
problem once next_release's per-plugin identity-hash dedup guard lands in
main, since it drops a plugin's entire batch on any internal duplicate.
Fixed by deduping once in main() after collecting from all check_*
functions. Also added iw + its setcap to Dockerfile.debian and
.devcontainer/Dockerfile, which the original PR missed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011meLPKCzVpdZyAUfv5U6mm
- check_trusted_aps() was evaluating every AP sharing a protected SSID
against every trusted entry for that SSID, not just its own. A main AP
requiring a stricter accepted security set (e.g. wpa3-only) than a
separately-trusted extender (e.g. wpa2) caused the extender to be
flagged as evil_twin/absent_baseline_clone - it was being judged
against the main AP's accepted set instead of its own. Fixed by
excluding, from each trusted entry's evaluation, any BSSID that has its
own separate trusted entry for the same SSID.
- README's "iw isn't in the published image yet" section was already
stale within the same PR - this branch's own Dockerfile change adds
iw + setcap, so the image ships it. Replaced with one sentence.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Periodic iw-scan-based detection of the 6 heuristics that don't need
monitor-mode hardware (see issue #1789): pwnagotchi/Pineapple signatures,
evil-twin/open clones, baseline-AP-absent-with-clone, security downgrades,
and duplicate-SSID/different-vendor - all evaluated against a user-curated
trusted-AP baseline (WIFICANARY_trusted_aps). A detection creates a
flagged Devices entry even for BSSIDs that never associate, per the
addendum on the same issue.
- WIFICANARY_TRUSTED_SECURITY is multi-select: an observed encryption
exactly matching any selected value is accepted; otherwise it's flagged
if weaker than the strongest selected value (deliberate - comparing
against the weakest would make multi-select pointless, since anything
at/above the weakest would silently pass regardless of the rest of the
selection).
- Added a "known device turned rogue" motor: escalate_known_devices()
cross-references each detection's BSSID against the Devices table via
the new DeviceInstance.getAllByMacs(). This covers the BSSID-identity
half of the issue #1789 addendum's motor 10; the deauth/probe-source-MAC
half still needs monitor-mode data this plugin doesn't have.
- Vendor is deliberately not looked up by this plugin - any device it
creates gets devVendor filled in for free by core's own vendor_update
plugin on its next pass.
43 wificanary unit tests + 10 DeviceInstance.getAllByMacs() tests, all
test_plugin_conventions.py checks pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011meLPKCzVpdZyAUfv5U6mm
Maps DOCKERDISC to CurrentScan (scanMac/scanCreatesDevice/scanParentMAC/
scanLastIP) so a container on a macvlan/ipvlan network can opt into
creating or confirming its own device, parented to its Docker host.
Gated by a new DOCKERDISC_CREATE_DEV setting (default off). A container
without its own MAC (bridge/overlay/etc.) never creates a device either
way - the framework's blank-scanMac guard blocks the whole group
regardless of the setting.
Reuses the existing objectPrimaryId/extra column definitions (already
host MAC / container IP) to also feed scanParentMAC/scanLastIP, so every
promoted container is auto-parented to its host with no extra plugin
logic. Two new hidden columns (helpVal1/helpVal2) carry the per-container
scanMac/scanCreatesDevice values.
Tests: 33 -> 35, both DOCKERDISC_CREATE_DEV on/off paths asserted.
Live-verified end to end against a real built image (docker-socket-proxy
+ isolated macvlan/bridge test containers), since IMPORT_ON isn't in any
released NetAlertX image yet.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011meLPKCzVpdZyAUfv5U6mm