|
@@ -291,15 +291,21 @@ final class AppFactory
|
|
|
// matched as an IP.
|
|
// matched as an IP.
|
|
|
$admin->get('/ips/countries', [$ips, 'countries'])
|
|
$admin->get('/ips/countries', [$ips, 'countries'])
|
|
|
->add(RbacMiddleware::require($rf, Role::Viewer));
|
|
->add(RbacMiddleware::require($rf, Role::Viewer));
|
|
|
- // SEC_REVIEW F43: pin to an IP-shaped charset rather than `.+`.
|
|
|
|
|
- // 0-9, a-f/A-F (IPv6 hex), `.` (IPv4), `:` (IPv6), and `%`
|
|
|
|
|
- // (so a UI `rawurlencode('2001:db8::1')` form like
|
|
|
|
|
- // `2001%3Adb8%3A%3A1` still matches before the controller's
|
|
|
|
|
- // `rawurldecode`). Anything else — `/`, `..`, `?`, spaces,
|
|
|
|
|
- // dashes — fails to match the route and 404s before the
|
|
|
|
|
- // handler can use the param as a filename / log key /
|
|
|
|
|
- // downstream URL component.
|
|
|
|
|
- $admin->get('/ips/{ip:[0-9a-fA-F.:%]+}', [$ips, 'show'])
|
|
|
|
|
|
|
+ // SEC_REVIEW F43 + F72: pin to an IP-shaped charset AND
|
|
|
|
|
+ // cap the length so a Viewer can't hand the handler a
|
|
|
|
|
+ // multi-megabyte string. 0-9, a-f/A-F (IPv6 hex), `.`
|
|
|
|
|
+ // (IPv4), `:` (IPv6), and `%` (so a UI
|
|
|
|
|
+ // `rawurlencode('2001:db8::1')` form like
|
|
|
|
|
+ // `2001%3Adb8%3A%3A1` still matches before the
|
|
|
|
|
+ // controller's `rawurldecode`). Length cap 80: an
|
|
|
|
|
+ // IPv4 dotted-quad is ≤ 15 chars, a canonical IPv6 is
|
|
|
|
|
+ // ≤ 39 chars, and the same IPv6 with every colon
|
|
|
|
|
+ // urlencoded (`%3A`) is ≤ 53 chars. 80 leaves ~50%
|
|
|
|
|
+ // headroom; anything past that is malformed-or-malicious.
|
|
|
|
|
+ // Anything else — `/`, `..`, `?`, spaces, dashes,
|
|
|
|
|
+ // multi-megabyte strings — fails to match the route
|
|
|
|
|
+ // and 404s before the handler reads `$args['ip']`.
|
|
|
|
|
+ $admin->get('/ips/{ip:[0-9a-fA-F.:%]{1,80}}', [$ips, 'show'])
|
|
|
->add(RbacMiddleware::require($rf, Role::Viewer));
|
|
->add(RbacMiddleware::require($rf, Role::Viewer));
|
|
|
|
|
|
|
|
/** @var StatsController $stats */
|
|
/** @var StatsController $stats */
|