{"record":{"id":"2f89fc5fbf784158","repo":"symfony/http-foundation","slug":"invalid-uri-userinfo-is-malformed","errorCode":null,"errorMessage":"Invalid URI: Userinfo is malformed.","messagePattern":"Invalid URI: Userinfo is malformed\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"Request.php","lineNumber":403,"sourceCode":"            'SERVER_PROTOCOL' => 'HTTP/1.1',\n            'REQUEST_TIME' => time(),\n            'REQUEST_TIME_FLOAT' => microtime(true),\n        ], $server);\n\n        $server['PATH_INFO'] = '';\n        $server['REQUEST_METHOD'] = strtoupper($method);\n\n        if (($i = strcspn($uri, ':/?#')) && ':' === ($uri[$i] ?? null) && (strspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.') !== $i || strcspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'))) {\n            throw new BadRequestException('Invalid URI: Scheme is malformed.');\n        }\n        if (false === $components = parse_url(\\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {\n            throw new BadRequestException('Invalid URI.');\n        }\n\n        $part = ($components['user'] ?? '').':'.($components['pass'] ?? '');\n\n        if (':' !== $part && \\strlen($part) !== strcspn($part, '[]')) {\n            throw new BadRequestException('Invalid URI: Userinfo is malformed.');\n        }\n        if (($part = $components['host'] ?? '') && !self::isHostValid($part)) {\n            throw new BadRequestException('Invalid URI: Host is malformed.');\n        }\n        if (false !== ($i = strpos($uri, '\\\\')) && $i < strcspn($uri, '?#')) {\n            throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.');\n        }\n        if (\\strlen($uri) !== strcspn($uri, \"\\r\\n\\t\")) {\n            throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.');\n        }\n        if ('' !== $uri && (\\ord($uri[0]) <= 32 || \\ord($uri[-1]) <= 32)) {\n            throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');\n        }\n\n        if (isset($components['host'])) {\n            $server['SERVER_NAME'] = $components['host'];\n            $server['HTTP_HOST'] = $components['host'];\n        }","sourceCodeStart":385,"sourceCodeEnd":421,"githubUrl":"https://github.com/symfony/http-foundation/blob/5aea19cd678fa4140f6108406f1096de5e9ed6e4/Request.php#L385-L421","documentation":"Request::create() validates every component of the URI it is asked to turn into a Request before constructing it. This check guards against userinfo-spoofing/open-redirect tricks: if the userinfo part (user:pass before @) contains '[' or ']', the URI is ambiguous or malicious (e.g. 'http://normal.com[@evil.com/') and the library rejects it with a BadRequestException (HTTP 400). It means the caller passed a syntactically unsafe URI string, not a transient failure.","triggerScenarios":"Calling Request::create() (or Request::createRequestUri-based factory paths) with a URI whose userinfo component contains square brackets, e.g. 'http://user[name]:pass@host/' or spoofing forms like 'http://normal.com[@vulndetector.com/' or 'http://[normal.com@vulndetector.com/'. parse_url() accepted the URI but the strict post-parse check in Request.php:401-403 (strlen($part) !== strcspn($part, '[]')) fails.","commonSituations":"Constructing requests programmatically from user-supplied URLs; SSRF/open-redirect test suites; proxies or crawlers feeding raw URLs into Request::create(); copy-pasted URLs containing brackets in credentials; security scanners deliberately probing host-header confusion.","solutions":["URL-encode or remove '[' and ']' from the userinfo (user:password) portion of the URI before passing it to Request::create().","Use rawurlencode() on the username and password separately and rebuild the URI: scheme://rawurlencode($user).':'.rawurlencode($pass).'@host'.","If the brackets are meant for the host (IPv6 literal like http://[::1]/), ensure they are in the host component, not the userinfo, and that the URI parses as intended.","Wrap the call in try/catch for BadRequestException when URLs come from untrusted input and return a 400 to the client."],"exampleFix":"// before\n$request = Request::create('http://normal.com[@vulndetector.com/');\n// after\n$uri = 'http://' . rawurlencode('normal.com[') . '@vulndetector.com/';\n// or better: reject it up front\nif (preg_match('#^[a-z][a-z0-9+.-]*://[^/?#@]*\\[|^https?://[^/?#]*\\][^/]#i', $uri)) {\n    throw new \\InvalidArgumentException('URI userinfo contains brackets');\n}\n$request = Request::create($uri);","handlingStrategy":"validation","validationCode":"function hasSafeUserinfo(string $uri): bool\n{\n    $u = parse_url($uri);\n    if (!isset($u['user']) && !isset($u['pass'])) {\n        return true;\n    }\n    $part = ($u['user'] ?? '') . ':' . ($u['pass'] ?? '');\n\n    return ':' === $part || strlen($part) === strcspn($part, '[]');\n}\nif (!hasSafeUserinfo($uri)) {\n    throw new \\InvalidArgumentException('URI userinfo contains brackets');\n}","typeGuard":"function isCreatableUri(string $uri): bool\n{\n    return '' !== $uri\n        && false === strpos($uri, '\\\\')\n        && strlen($uri) === strcspn($uri, \"\\r\\n\\t\")\n        && ord($uri[0]) > 32 && ord($uri[-1]) > 32;\n}","tryCatchPattern":"use Symfony\\Component\\HttpFoundation\\Request;\nuse Symfony\\Component\\HttpFoundation\\Exception\\BadRequestException;\n\ntry {\n    $request = Request::create($uri);\n} catch (BadRequestException $e) {\n    return new Response('Invalid URL supplied.', 400);\n}","preventionTips":["Always rawurlencode() username and password when embedding credentials in a URI","Validate untrusted URLs (host, userinfo, no control chars) before handing them to Request::create()","Treat brackets in userinfo as a hostile-input signal and reject rather than repair","Add unit tests with the spoofing payloads 'http://a[@b/' and 'http://[a@b/' to your URL ingestion code"],"tags":["http","uri-validation","request","security"],"backgroundTag":"invalid-url-format","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"}