-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix string interpolation and platform where 'ip' command (from iproute2) is unavailable. #4734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
8a84284
9681d00
f34f3ad
d627ca2
88d7c11
21cb8bf
7e28665
1527ae6
09a92b1
9628328
154ea38
a155155
ce8f113
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2240,59 +2240,82 @@ function i18n() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function get_networks() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces = array(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces = array(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (defined('ZM_PATH_IP') and ZM_PATH_IP and file_exists(ZM_PATH_IP)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(ZM_PATH_IP.' link', $output, $status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( $status ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $html_output = implode('<br/>', $output); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Error("Unable to list network interfaces, status is '$status'. Output was:<br/><br/>$html_output"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach ( $output as $line ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( preg_match('/^\d+: ([[:alnum:]]+):/', $line, $matches ) ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( $matches[1] != 'lo' ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces[$matches[1]] = $matches[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Debug("No match for $line"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (defined('ZM_PATH_IP') and ZM_PATH_IP and file_exists(ZM_PATH_IP)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(ZM_PATH_IP.' link', $output, $status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( $status ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $html_output = implode('<br/>', $output); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Error("Unable to list network interfaces, status is '$status'. Output was:<br/><br/>$html_output"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach ( $output as $line ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( preg_match('/^\d+: ([[:alnum:]]+):/', $line, $matches ) ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( $matches[1] == 'lo' ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces[$matches[1]] = $matches[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Debug("No match for $line"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
connortechnology marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $routes = array(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec(ZM_PATH_IP.' route', $output, $status); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ($status) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $html_output = implode('<br/>', $output); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Error("Unable to list network interfaces, status is '$status'. Output was:<br/><br/>$html_output"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| foreach ($output as $line) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( preg_match('/^default via [.[:digit:]]+ dev ([[:alnum:]]+)/', $line, $matches) ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces['default'] = $matches[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if ( preg_match('/^([.[:digit:]]+\/[[:digit:]]+) dev ([[:alnum:]]+)/', $line, $matches) ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $interfaces[$matches[2]] .= ' ' . $matches[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ZM\Debug("Matched $line: $matches[2] .= $matches[1]"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $routes = array(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $routes = array(); |
Copilot
AI
Apr 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$interfaces[$matches[2]] .= ... assumes the interface key already exists. If the link parsing missed an interface (or route output references an interface not listed), this will raise a notice and produce a malformed label. Initialize the entry when it’s not set before appending.
| } else if ( preg_match('/^([.[:digit:]]+\/[[:digit:]]+) dev ([[:alnum:]]+)/', $line, $matches) ) { | |
| } else if ( preg_match('/^([.[:digit:]]+\/[[:digit:]]+) dev ([[:alnum:]]+)/', $line, $matches) ) { | |
| if ( !isset($interfaces[$matches[2]]) ) { | |
| $interfaces[$matches[2]] = $matches[2]; | |
| } |
Copilot
AI
Apr 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Route parsing uses the same [[:alnum:]]+ constraint for interface names, which can miss valid interface names containing -, ., or @. This can prevent default-route detection and subnet annotations from working on common setups (bridges/VLANs/veth). Widen the interface-name match here as well.
Copilot
AI
Apr 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The strpos checks against the flags string use loose comparisons (== false / != false), which mis-handle matches at position 0 (e.g., strpos(...) returning 0). Use strict comparisons (=== false and !== false) so LOOPBACK/RUNNING detection is correct in all cases.
| (strpos($matches[3], "LOOPBACK") == false) && (strpos($matches[3], "RUNNING") != false)) | |
| (strpos($matches[3], "LOOPBACK") === false) && (strpos($matches[3], "RUNNING") !== false)) |
Copilot
AI
Apr 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the FreeBSD default-route block, the error log builds $html_output from $output (the ifconfig output), not from $defaultIface (the route get default output), so failures will report the wrong command output. Also, this uses a shell pipeline (route | grep | awk) and unqualified command names, which is brittle if the web server PATH doesn’t include these tools; prefer executing the route command directly and parsing in PHP (or using configured full paths).
| exec("route get default | grep interface | awk '{print $2}'", $defaultIface, $status); | |
| if ($status) { | |
| $html_output = implode("\n", $output); | |
| ZM\Error("Unable to get default ip route, status is '$status'. Output was:$html_output"); | |
| } else { | |
| if (isset($defaultIface) && isset($defaultIface[0])) { | |
| $interfaces['default'] = $defaultIface[0]; | |
| $defaultIface = array(); | |
| $routeCommand = 'route'; | |
| if (is_executable('/sbin/route')) { | |
| $routeCommand = '/sbin/route'; | |
| } else if (is_executable('/usr/sbin/route')) { | |
| $routeCommand = '/usr/sbin/route'; | |
| } | |
| exec($routeCommand.' get default', $defaultIface, $status); | |
| if ($status) { | |
| $html_output = implode("\n", $defaultIface); | |
| ZM\Error("Unable to get default ip route, status is '$status'. Output was:$html_output"); | |
| } else { | |
| foreach ($defaultIface as $routeLine) { | |
| if (preg_match('/^\s*interface:\s*(\S+)/', $routeLine, $matches)) { | |
| $interfaces['default'] = $matches[1]; | |
| break; | |
| } |
Copilot
AI
Apr 14, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ifconfig fallback runs exec(ZM_PATH_IFCONFIG, ...) and then immediately runs exec('ifconfig', ...), overwriting $output. If ZM_PATH_IFCONFIG succeeds but ifconfig isn’t in the web server PATH (common for /sbin tools), this will incorrectly fail. Use ZM_PATH_IFCONFIG for the actual call (and remove the redundant first/second exec).
| exec('ifconfig', $output, $status); | |
| if ($status) { | |
| $html_output = implode("\n", $output); | |
| ZM\Error("Unable to list network interfaces, status is '$status'. Output was:$html_output"); | |
| } else { | |
| array_walk($output, function($value, $key) use (&$interfaces) { | |
| $retval = preg_match("/^([A-Za-z0-9]*):\s+flags=([0-9]*)<([A-Z,]*)>.*/ims", $value, $matches); | |
| ZM\Debug(print_r($matches, true)); | |
| if (($retval == 1) && (strlen($matches[3]) > 0) && | |
| (strpos($matches[3], "LOOPBACK") == false) && (strpos($matches[3], "RUNNING") != false)) | |
| { | |
| $interfaces[$matches[1]] = $matches[1]; | |
| } | |
| }); | |
| if (strcmp($osname, 'freebsd') == 0) { | |
| // Get default route iface | |
| exec("route get default | grep interface | awk '{print $2}'", $defaultIface, $status); | |
| if ($status) { | |
| $html_output = implode("\n", $output); | |
| ZM\Error("Unable to get default ip route, status is '$status'. Output was:$html_output"); | |
| } else { | |
| if (isset($defaultIface) && isset($defaultIface[0])) { | |
| $interfaces['default'] = $defaultIface[0]; | |
| } | |
| array_walk($output, function($value, $key) use (&$interfaces) { | |
| $retval = preg_match("/^([A-Za-z0-9]*):\s+flags=([0-9]*)<([A-Z,]*)>.*/ims", $value, $matches); | |
| ZM\Debug(print_r($matches, true)); | |
| if (($retval == 1) && (strlen($matches[3]) > 0) && | |
| (strpos($matches[3], "LOOPBACK") == false) && (strpos($matches[3], "RUNNING") != false)) | |
| { | |
| $interfaces[$matches[1]] = $matches[1]; | |
| } | |
| }); | |
| if (strcmp($osname, 'freebsd') == 0) { | |
| // Get default route iface | |
| exec("route get default | grep interface | awk '{print $2}'", $defaultIface, $status); | |
| if ($status) { | |
| $html_output = implode("\n", $output); | |
| ZM\Error("Unable to get default ip route, status is '$status'. Output was:$html_output"); | |
| } else { | |
| if (isset($defaultIface) && isset($defaultIface[0])) { | |
| $interfaces['default'] = $defaultIface[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are actually security issues around just running things and expecting PATH to find them. Sometimes the ZM admin is NOT the system admin. So system admin sets ZM_PATH_IFCONFIG and ZM admin just has to live with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
ip linkinterface-name regex only matches[[:alnum:]]+, but Linux interface names can include characters like-,., and@(e.g.br-...,eth0.100,veth...@if...). This will silently drop valid interfaces. Consider matching everything up to the trailing:(or using a broader allowed set) and then filtering/normalizing.