{"record":{"id":"854065df6766bb4d","repo":"symfony/http-foundation","slug":"cannot-anonymize-more-than-4-bytes-for-ipv4-and-16-bytes-for","errorCode":null,"errorMessage":"Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6.","messagePattern":"Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"IpUtils.php","lineNumber":214,"sourceCode":"        return self::setCacheResult($cacheKey, true);\n    }\n\n    /**\n     * Anonymizes an IP/IPv6.\n     *\n     * Removes the last bytes of IPv4 and IPv6 addresses (1 byte for IPv4 and 8 bytes for IPv6 by default).\n     *\n     * @param int<0, 4>  $v4Bytes\n     * @param int<0, 16> $v6Bytes\n     */\n    public static function anonymize(string $ip, int $v4Bytes = 1, int $v6Bytes = 8): string\n    {\n        if ($v4Bytes < 0 || $v6Bytes < 0) {\n            throw new \\InvalidArgumentException('Cannot anonymize less than 0 bytes.');\n        }\n\n        if ($v4Bytes > 4 || $v6Bytes > 16) {\n            throw new \\InvalidArgumentException('Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6.');\n        }\n\n        /*\n         * If the IP contains a % symbol, then it is a local-link address with scoping according to RFC 4007\n         * In that case, we only care about the part before the % symbol, as the following functions, can only work with\n         * the IP address itself. As the scope can leak information (containing interface name), we do not want to\n         * include it in our anonymized IP data.\n         */\n        if (str_contains($ip, '%')) {\n            $ip = substr($ip, 0, strpos($ip, '%'));\n        }\n\n        $wrappedIPv6 = false;\n        if (str_starts_with($ip, '[') && str_ends_with($ip, ']')) {\n            $wrappedIPv6 = true;\n            $ip = substr($ip, 1, -1);\n        }\n","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/symfony/http-foundation/blob/5aea19cd678fa4140f6108406f1096de5e9ed6e4/IpUtils.php#L196-L232","documentation":"IpUtils::anonymize() caps truncation at 4 bytes for IPv4 and 16 bytes for IPv6, because that is the entire address; anything larger is meaningless and would attempt to shift more bits than exist. Passing an exceeding byte count throws this InvalidArgumentException before any anonymization happens.","triggerScenarios":"Calling IpUtils::anonymize($ip, 5) or IpUtils::anonymize($ip, 4, 17); using a single shared 'bytes' config value applied to both IPv4 and IPv6 without clamping to each limit (e.g. 8 or 16 bytes, valid for v6 but over the v4 cap of 4).","commonSituations":"One privacy config (e.g. 'anonymize: 16') fed to both v4/v6 arguments; developers assuming the limit is uniform across families; off-by-one when the intent was 'anonymize fully' (use 4/16 exactly).","solutions":["Clamp per family: $v4Bytes = min(4, $v4Bytes); $v6Bytes = min(16, $v6Bytes);","If the goal is full anonymization, pass the exact maxima: anonymize($ip, 4, 16).","Separate the IPv4 and IPv6 byte settings in your configuration instead of sharing one value.","Validate config-supplied integers against int<0,4> / int<0,16> at load time.","Use filter_var to detect address family first and apply only the matching limit."],"exampleFix":"// before\nIpUtils::anonymize($ip, $bytes, $bytes); // $bytes = 16: v4 limit is 4, throws\n\n// after\nIpUtils::anonymize($ip, min(4, $bytes), min(16, $bytes));","handlingStrategy":"validation","validationCode":"// before calling anonymize\n$v4Bytes = min(4, max(0, (int) $v4Bytes));\n$v6Bytes = min(16, max(0, (int) $v6Bytes));\nIpUtils::anonymize($ip, $v4Bytes, $v6Bytes);","typeGuard":"function isValidAnonymizeBytes(int $v4, int $v6): bool {\n    return $v4 >= 0 && $v4 <= 4 && $v6 >= 0 && $v6 <= 16;\n}","tryCatchPattern":"try {\n    $anon = IpUtils::anonymize($ip, $v4Bytes, $v6Bytes);\n} catch (\\InvalidArgumentException $e) {\n    $anon = IpUtils::anonymize($ip, 4, 16); // full anonymization fallback\n}","preventionTips":["Never share one byte-count config value across IPv4 and IPv6; clamp each to its own cap.","Use min(4, ...) for v4 and min(16, ...) for v6 at the call site.","For 'fully anonymize', pass the exact maxima 4 and 16.","Document the per-family limits next to your privacy configuration keys."],"tags":["php","symfony","argument-out-of-range","ip-anonymization"],"backgroundTag":"argument-out-of-range","analyzedSha":"5aea19cd678fa4140f6108406f1096de5e9ed6e4","analyzedAt":"2026-09-13T01:52:22.855Z","contentChangedAt":"2026-09-13T01:52:22.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}