Skip to content

fix: harden Mactrack release boundaries#336

Draft
somethingwithproof wants to merge 10 commits into
Cacti:developfrom
somethingwithproof:fix/mactrack-release-hardening
Draft

fix: harden Mactrack release boundaries#336
somethingwithproof wants to merge 10 commits into
Cacti:developfrom
somethingwithproof:fix/mactrack-release-hardening

Conversation

@somethingwithproof

Copy link
Copy Markdown
Contributor

Summary

  • Harden SQL, XSS, path, shell-command, deserialization, authorization, and PHP 8 compatibility boundaries using Cacti 1.2.31 APIs.
  • Replace the vendored Net_DNS2 copy with Composer-managed mikepultz/netdns2 1.5, retaining the existing Net_DNS2_* resolver API.
  • Add PHP 8.2 Composer/Pest/PHPUnit/Psalm scaffolding and a disposable Cacti 1.2.31 + MariaDB install/enable E2E harness.

Root cause

The initial dependency migration selected NetDNS2 2.x, which is namespaced and incompatible with Mactrack's legacy resolver API. This branch pins the maintained 1.5 compatibility line, and the E2E suite verifies the plugin enables successfully.

Validation

  • PHP 8.2 lint and Composer validation
  • Pest: 4 passed (unit, integration, endpoint-security regression)
  • Disposable Cacti 1.2.31 + MariaDB: core install, Mactrack install/enable, table and resolver smoke checks passed
  • Semgrep PHP/security audit: one reviewed false positive at a MAC-validated, html_escape()-encoded display value

Follow-up

  • Psalm runs but is not clean: legacy Cacti runtime APIs and collector code currently yield 2,318 findings.
  • Measured 100% coverage has not been achieved; the new suite is a regression baseline, not a coverage claim.
  • Vendor collector specifications need a separate documented validation pass.

somethingwithproof and others added 9 commits July 12, 2026 19:06
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
…polations in view pages

The string filters in the view/report pages were already routed through db_qstr(); this closes the remaining raw interpolations of numeric request vars (site_id, device_id, status, vlan, authorized, host_id, graph_template_id, device_type_id) into WHERE clauses by casting them with (int) at the concatenation point. These are declared FILTER_VALIDATE_INT in each validate_store_request_vars() array, so this is defense-in-depth that removes reliance on upstream validation and matches the intval() approach already used in mactrack_get_devices(). 1.2.31-idiomatic, PHP 7.4-safe.

Files: mactrack_view_{arp,ips,dot1x,interfaces,sites,graphs,macs}.php

Co-Authored-By: Oz <oz-agent@warp.dev>
mactrack_macauth.php: api_mactrack_maca_save() interpolated the user-supplied MAC address into an UPDATE ... LIKE "...%" (first-order SQLi); now bound via db_execute_prepared() with a ? placeholder. api_mactrack_maca_remove() called db_execute_prepared() while concatenating the (stored) mac_address into the SQL with no bound params (second-order SQLi + misuse of the prepared helper); the three DELETEs now bind mac_address as a parameter.

mactrack_devices.php: Copy SNMP Settings (drp_action==7) built its UPDATE SET clause by interpolating host-table values with bare quotes; values are now bound with placeholders (column name stays interpolated as a trusted static-array key). Mirrors the fix already applied on the Cacti#334 branch.

mactrack_device_types.php, mactrack_sites.php, mactrack_snmp.php: (int)-cast the remaining raw numeric request vars (type_id, site_id, and id used in snmp_id= fragments for move_item_up/down and get_sequence) — defense-in-depth over the FILTER_VALIDATE_INT request validation. 1.2.31-idiomatic, PHP 7.4-safe.

Co-Authored-By: Oz <oz-agent@warp.dev>
@somethingwithproof

Copy link
Copy Markdown
Contributor Author

SQL-injection hardening complete — requesting final review

Two follow-up commits finish parameterizing the remaining raw SQL in this PR. All bound via db_execute_prepared() / db_qstr() / (int); column names stay interpolated only where they are trusted keys from static $fields_* definitions. Cacti 1.2.31-idiomatic and PHP 7.4-safe.

88e46a8 — view / report pages

Int-cast the remaining numeric request-var interpolations (site_id, device_id, status, vlan, authorized, host_id, graph_template_id, device_type_id) in mactrack_view_{arp,ips,dot1x,interfaces,sites,graphs,macs}.php. The string filters were already routed through db_qstr().

9b3441c — non-view files

  • mactrack_macauth.php — fixed a first-order SQLi: api_mactrack_maca_save() interpolated the user-supplied MAC address into UPDATE ... WHERE mac_address LIKE "...%", now a bound ?. Also api_mactrack_maca_remove() called db_execute_prepared() while concatenating the stored MAC with no params (second-order + helper misuse) — the three DELETEs now bind mac_address.
  • mactrack_devices.php — Copy SNMP Settings built its UPDATE ... SET clause from host-table values with bare quotes; values are now bound with placeholders.
  • mactrack_device_types.php, mactrack_sites.php, mactrack_snmp.php(int)-cast the remaining numeric request vars (type_id, site_id, and id used in snmp_id= fragments), defense-in-depth over the FILTER_VALIDATE_INT request validation.

Tests (local)

  • php -l clean on all 12 changed files.
  • Standalone suite passes: tests/Unit/test_device_type_sql_safety.php, tests/Unit/test_filter_option_escaping.php, tests/Integration/test_mactrack_filter_output_wiring.php, tests/e2e/test_mactrack_no_raw_filter_labels.php.
  • Not run locally (covered by CI): the mactrack_smoke.php docker e2e, which needs a live Cacti 1.2.31 + MariaDB stack.

Reviewer notes

…lver

poller_mactrack.php: the aggregated-ports INSERT ... SELECT interpolated $scan_date (server date()) into an AND t2.scan_date = "..." predicate; switched to db_execute_prepared() with a ? placeholder (the same value is already bound at the earlier temp-ports update). No behavior change; removes the last raw interpolation on the collection path.

mactrack_resolver.php: the DNS-resolution INSERT already routes every string column (including the untrusted PTR/dns_hostname) through db_qstr(); (int)-cast the two remaining raw numeric columns (site_id, device_id) for defense-in-depth.

Note: the primary SNMP-value sinks db_store_device_port_results() and db_update_device_status() (lib/mactrack_functions.php) were already fully parameterized. 1.2.31-idiomatic, PHP 7.4-safe.

Co-Authored-By: Oz <oz-agent@warp.dev>
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.

1 participant