{"record":{"id":"d786789919715880","repo":"symfony/http-foundation","slug":"cannot-anonymize-less-than-0-bytes","errorCode":null,"errorMessage":"Cannot anonymize less than 0 bytes.","messagePattern":"Cannot anonymize less than 0 bytes\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"IpUtils.php","lineNumber":210,"sourceCode":"                return self::setCacheResult($cacheKey, false);\n            }\n        }\n\n        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, ']')) {","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/symfony/http-foundation/blob/5aea19cd678fa4140f6108406f1096de5e9ed6e4/IpUtils.php#L192-L228","documentation":"IpUtils::anonymize() truncates IP addresses for GDPR-style anonymization. The $v4Bytes and $v6Bytes parameters are documented as int<0,4> and int<0,16>; passing a negative byte count would mean removing more bits than the address has or producing nonsensical output, so the method throws this InvalidArgumentException before doing any work.","triggerScenarios":"Calling IpUtils::anonymize($ip, -1) or IpUtils::anonymize($ip, 4, -8); computing a byte count from unvalidated config (e.g. a privacy setting that can go negative) and passing it through.","commonSituations":"Config values like 'anonymize_bytes: -1' read from yaml/env and cast to int without validation; arithmetic that underflows (e.g. $bytes = $userSetting - $extra); tests probing boundary behavior.","solutions":["Clamp the values: $v4Bytes = max(0, min(4, $v4Bytes)); $v6Bytes = max(0, min(16, $v6Bytes));","Validate user/config-supplied byte counts before calling anonymize and reject negatives.","Pass 0 explicitly if you want no truncation (0 is a legal value).","Use native PHP types (int<0,4>) with static analysis to catch negatives before runtime.","Log the offending input value to find which config source produced the negative number."],"exampleFix":"// before\nIpUtils::anonymize($ip, $configBytes); // $configBytes = -1: throws\n\n// after\n$v4 = max(0, min(4, $configBytes));\n$v6 = max(0, min(16, $configBytes));\nIpUtils::anonymize($ip, $v4, $v6);","handlingStrategy":"validation","validationCode":"// before calling anonymize\n$v4Bytes = max(0, (int) $v4Bytes);\n$v6Bytes = max(0, (int) $v6Bytes);\nif ($v4Bytes < 0 || $v6Bytes < 0) {\n    throw new \\InvalidArgumentException('Byte counts must be non-negative.');\n}","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); // safe defaults (1 byte v4, 8 bytes v6)\n}","preventionTips":["Clamp config-derived byte counts with max(0, ...) before calling.","Use native int<0,4> / int<0,16> docblock types and static analysis.","Validate environment/yaml privacy settings at config-load time, not at request time.","Remember 0 is legal (no truncation); negatives never are."],"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"}