Unvalidated IPv6 zone allows arbitrary bytes
medium swesweep-fasthttp · ipv6.go · validateIPv6Literal · run #4
Description
When a '%' is present, the code only rejects an empty zone and removes everything from the first percent onward. The zone may contain colons, additional percents, and other control characters. If the validated host is later placed in a Host header, request line, or dial string, this can produce ambiguous or unsafe host values. A strict HTTP IPv6 host validator should restrict zone characters or reject zones in this context.
Triage
- Verdict
- INVALID confidence 0.17
- Model
- qwen/qwen3.8-27b
- Rounds
- 6
- Cost
- $0.0218 · 763.1 s
[ARBITER] The reported vulnerability claims that unvalidated characters in the IPv6 zone (part after '%') can lead to security issues like Host header injection if the host is later used in a request. However, the threat model for a library focuses on memory safety, correctness on malformed input, and resource bounds reachable via public APIs. The function `validateIPv6Literal` correctly validates the IPv6 address structure. It intentionally ignores the zone content for the purpose of validating the IP address format, which is standard behavior. The potential for 'Host header injection' relies on the assumption that the caller will use the unvalidated zone string directly in a security-sensitive context (like a raw HTTP request buffer) without further sanitization. In the context of `fasthttp`, the library generally handles the construction of HTTP headers safely (e.g., escaping or rejecting invalid characters when writing headers, or relying on the caller to provide valid data). More importantly, the report does not demonstrate a concrete path from the `fasthttp` public API to a memory safety violation or unbounded resource consumption caused by the zone characters. The 'ambiguity' argument is a quality-of-implementation or specific-use-case concern, not a fundamental security flaw in the library's parsing logic under the provided threat model. Furthermore, standard IPv6 zone identifiers (like 'eth0') do not contain CRLF, and if a user crafts a malicious zone, that is input validation of the user's own data, which is typically the application's responsibility unless the library explicitly claims to sanitize all output for raw HTTP transmission (which is usually handled by the header writer). Therefore, this is not a valid security vulnerability under the strict threat model.
All rounds
[
{
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"verdict": "INVALID",
"reasoning": "The reported vulnerability claims that the code fails to validate the content of the IPv6 zone (the substring following '%'), allowing arbitrary bytes. However, the provided code in `validateIPv6Literal` (lines 24-28) explicitly handles the zone: it identifies the start of the zone (`zi`), checks if it is empty (`zi == len(addr)-1`), and then crucially, truncates the `addr` variable to exclude the zone (`addr = addr[:zi]`). The subsequent validation logic (`parseIPv6Hextets` or `validIPv4`) operates only on this truncated `addr`. Therefore, any arbitrary bytes in the zone are completely ignored by the validator. If the host is invalid, an error is returned. If it is valid, the function returns `nil`, and the caller (which is not shown but implied to be the public API) is responsible for handling the original host string. Since the validator does not accept the zone into the validated address structure and the error path prevents invalid hosts from proceeding, there is no security vulnerability in this specific function. The 'arbitrary bytes' remain in the original `host` slice, but they do not affect the validation logic or cause a bypass, because the validator only cares about the address part. If the caller uses the original `host` for networking, that is a concern for the caller, but the validator correctly identifies the address as valid/invalid. The scanner's claim that this leads to 'ambiguous or unsafe host values' implies a lack of validation, but the validation is correct for the address part. The zone is simply stripped out of the validation target. There is no crash or bypass demonstrated by this code snippet.\n\nCRUX: The code truncates `addr` to exclude the zone before validation, meaning the zone content is not processed by the address validators.\nGREP: addr = addr[:zi]",
"elapsed": 213.9,
"tokens": 3609,
"tokens_in": 3208,
"tokens_out": 401,
"cost_usd": 0.0025503599999999998,
"file": "ipv6.go",
"round": 1,
"grep_used": true,
"grep_results": "GREP `addr = addr[:zi]`:\n```\nipv6_test.go:36:\t\taddr = addr[:zi]\nipv6.go:29:\t\taddr = addr[:zi]\n```"
},
{
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"verdict": "UNCERTAIN",
"reasoning": "The reported vulnerability is that `validateIPv6Literal` allows arbitrary bytes in the IPv6 zone (the part after `%`), which could lead to 'ambiguous or unsafe host values' if placed in a Host header or request line. The code shows that `validateIPv6Literal` extracts the address part `addr` (from `[` to `]`), finds the `%` index `zi`, checks if the zone is empty, and then truncates `addr` to `addr[:zi]` for validation. This means the validator only checks the address part and ignores the zone content entirely. The question is whether this leads to a security issue. The threat model states that the public API must validate all inputs. If `validateIPv6Literal` returns `nil` (valid), the caller might use the original `host` string (including the arbitrary zone) in a context where the zone is not allowed or is parsed differently, leading to a security issue (e.g., Host header injection or request smuggling). However, we need to verify if the caller actually uses the original `host` or a sanitized version. More importantly, we must check if the function `validateIPv6Literal` is called from public APIs that accept attacker-controlled input. Grep for `validateIPv6Literal` to find its callers.\n\nCRUX: Whether the callers of `validateIPv6Literal` use the original host string (with the arbitrary zone) in a security-sensitive context after validation passes, or if they sanitize it.\nGREP: validateIPv6Literal",
"elapsed": 89.7,
"tokens": 4121,
"tokens_in": 3790,
"tokens_out": 331,
"cost_usd": 0.0025848,
"file": "ipv6.go",
"round": 2,
"grep_used": true,
"grep_results": "GREP `validateIPv6Literal`:\n```\nipv6_test.go:77:\t\tgotErr := validateIPv6Literal(host)\nipv6_test.go:81:\t\t\tt.Fatalf(\"mismatch for %q: validateIPv6Literal err=%v, oracleValid=%v\",\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\nipv6.go:14:func validateIPv6Literal(host []byte) error {\n```"
},
{
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"verdict": "INVALID",
"reasoning": "The reported vulnerability claims that unvalidated bytes in the IPv6 zone (after '%') can lead to security issues. The code in `validateIPv6Literal` correctly validates the IPv6 address part and explicitly ignores the zone content for validation purposes, only checking that the zone is not empty. This is consistent with standard IPv6 literal parsing where the zone is a separate component. The function returns an error if the address is invalid, preventing malformed addresses from passing validation. If the validation passes, the original host string (including the zone) is returned to the caller. The caller (in `uri.go`) uses the validated host to construct URL components. In standard HTTP/URI handling, IPv6 zones are typically only valid in certain contexts (e.g., link-local addresses in specific network configurations) and are not generally allowed in standard HTTP Host headers or URIs as defined by RFC 3986 or RFC 2732. However, the function `validateIPv6Literal` is specifically designed to validate the *literal* IPv6 address part, and the presence of a zone is syntactically valid for IPv6 literals in some contexts. The key question is whether the caller (`uri.go`) sanitizes or rejects zones in a security-sensitive context (like the Host header). If `uri.go` uses the original host string with the zone in a context where zones are not allowed or where they could cause parsing ambiguities (like request smuggling or header injection), that would be a vulnerability. But the function itself is correctly validating the address. The threat model states that the public API must validate all inputs. If the caller does not properly handle the zone, that is a caller issue. However, without evidence that the caller misuses the zone in a way that leads to a security breach (like bypassing validation or causing memory corruption), the code in `ipv6.go` is not inherently vulnerable. The 'arbitrary bytes' in the zone do not affect the validation of the address, and the function returns an error for invalid addresses. The lack of strict zone character validation is a design choice that may be acceptable depending on the context, but it does not constitute a memory safety or integrity vulnerability in this function. Since the function correctly validates the address and rejects invalid ones, and the zone is ignored for validation, there is no bypass or memory corruption. The potential issue would be in how the caller uses the result, but that is outside the scope of this function. Given the code provided, the bug pattern (lack of zone validation) exists, but it does not lead to a direct security vulnerability in this function. The caller might need to handle zones differently, but that is a different issue. Therefore, this is likely a false positive or a code quality issue rather than a security vulnerability in this specific function.\n\nCRUX: The function correctly validates the IPv6 address and ignores the zone for validation, preventing invalid addresses from passing. The potential issue is in how the caller uses the validated host with the zone, which is outside the scope of this function.\nGREP: validateIPv6Literal",
"elapsed": 165.8,
"tokens": 4851,
"tokens_in": 4211,
"tokens_out": 640,
"cost_usd": 0.00368862,
"file": "ipv6.go",
"round": 3,
"grep_used": true,
"grep_results": "GREP `validateIPv6Literal`:\n```\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nipv6_test.go:77:\t\tgotErr := validateIPv6Literal(host)\nipv6_test.go:81:\t\t\tt.Fatalf(\"mismatch for %q: validateIPv6Literal err=%v, oracleValid=%v\",\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
},
{
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"verdict": "UNCERTAIN",
"reasoning": "The report claims that unvalidated IPv6 zone characters could lead to security issues (like Host header injection) if the host is later placed in a request. To determine validity, I must verify how `validateIPv6Literal` is used by its caller in `uri.go`. The prior grep shows `uri.go:469` calls it. I need to inspect the surrounding code in `uri.go` to see if the original `host` (including the potentially malicious zone) is used after validation passes, or if it is sanitized/rejected. Specifically, I need to check if `fasthttp` rejects zones entirely in HTTP contexts or if it allows them. If it allows them, and if the zone can contain newlines or other control characters that break header parsing, it could be a vulnerability. However, `validateIPv6Literal` only checks the *address* part. It does not validate the zone characters. If `uri.go` simply checks `err == nil` and then uses the raw `host` bytes for the Host header, an attacker could craft a host like `[fe80::1]%0d%0aHost: evil.com` (if the parser doesn't stop at `%` correctly or if the zone allows newlines). Let's look at `uri.go` around line 469.\n\nCRUX: Does the caller in uri.go sanitize or reject the IPv6 zone before using the host in a security-sensitive context like the Host header?\nGREP: validateIPv6Literal",
"elapsed": 133.0,
"tokens": 5261,
"tokens_in": 4935,
"tokens_out": 326,
"cost_usd": 0.0030507,
"file": "ipv6.go",
"round": 4,
"grep_used": true,
"grep_results": "GREP `validateIPv6Literal`:\n```\nipv6_test.go:77:\t\tgotErr := validateIPv6Literal(host)\nipv6_test.go:81:\t\t\tt.Fatalf(\"mismatch for %q: validateIPv6Literal err=%v, oracleValid=%v\",\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\nipv6.go:14:func validateIPv6Literal(host []byte) error {\n```"
},
{
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"verdict": "VALID",
"reasoning": "The reported vulnerability claims that unvalidated bytes in the IPv6 zone (after '%') can lead to security issues. I need to determine if this constitutes a security vulnerability in the `fasthttp` library according to the threat model.\n\n1. **Bug Pattern Existence**: The code in `validateIPv6Literal` (ipv6.go) extracts the IPv6 address, strips the zone (everything from the first '%' onward), and validates only the address part. It does *not* validate the characters allowed in the zone. A zone like `%0d%0aHost: evil` would pass validation as long as the IPv6 address part is valid. This pattern of \"unvalidated zone\" exists.\n\n2. **Attacker Reachability**: The function `validateIPv6Literal` is called from `uri.go:469`. `fasthttp` is a high-performance HTTP client/server library. Host headers and URIs are typically received from external, untrusted sources (HTTP requests/responses). Therefore, an attacker can control the `host` byte slice passed to this function.\n\n3. **Security Relevance / Impact**: The threat model for a reusable library states: \"Assets and impact: Memory safety and integrity of the host process... correct behaviour on malformed input...\". If `fasthttp` allows arbitrary bytes in the zone and later uses the raw host string in a security-sensitive context (like the `Host` header in an outgoing request, or in server-side logic), this could lead to:\n - **HTTP Request Smuggling/Injection**: If the zone contains CRLF sequences (`%0d%0a`), and these bytes are written directly to the socket or used to construct headers without sanitization, an attacker could inject new headers or terminate the request early.\n - **Protocol Confusion**: Ambiguous host values might confuse downstream servers or proxies.\n\n However, I must verify if `fasthttp` actually *sanitizes* the host before use or if it rejects such hosts elsewhere. The report suggests the validator *allows* these bytes. Let's look at how `host` is used after validation in `uri.go`. Usually, if `validateIPv6Literal` passes, the host is considered a valid URI host. If the library then uses this raw `host` slice in the `Host` header of a request it sends, and if the zone contains CRLF, that is a vulnerability.\n\n Let's check if there is a specific check for CRLF or non-printable characters in the host validation path or if the `Host` header construction escapes these characters. In many HTTP libraries, the `Host` header is sent as-is if it's a valid host. If the library treats `[fe80::1]%CRLF` as a valid host and sends it in the `Host` header line, it bypasses standard HTTP framing.\n\n However, `fasthttp` is known for being strict. Let's check if `validateIPv6Literal` is the *only* validator or if there's a broader `validateHost` that checks for CRLF. Even if there is, if `validateIPv6Literal` is called specifically for bracketed hosts and it allows arbitrary zone bytes, and the resulting host string is used without re-checking for CRLF, it's a bug.\n\n More importantly, does `fasthttp` reject IPv6 zones in the Host header context entirely? RFC 7230 states that the Host header should not contain zones. But the URI syntax allows them. If `fasthttp` parses a URL with a zone and then constructs a request using that URI's host part, does it strip the zone or include it? The code `addr = addr[:zi]` is local to the function and does not modify the original `host` slice. The caller receives the `host` slice still containing the zone. If the caller uses `host` directly for the network dial or the `Host` header, the zone is included.\n\n If the zone contains CRLF, and `fasthttp` does not sanitize the host string before writing it to the network buffer, this is a valid vulnerability (CRLF injection in Host header).\n\n Let's grep for how `host` is used in `uri.go` or the request writer to see if it's escaped or if CRLF is filtered globally.\n\n Actually, a simpler angle: The threat model says \"Not a vulnerability... Missing parameter validation in non-public functions... unless the code is directly reachable from outside or a public path can deliver input that violates the invariant.\" Here, the public path (URI parsing) delivers the input. The invariant (safe host string) is violated if CRLF is allowed.\n\n I will mark this as VALID because the library allows arbitrary bytes in a field that is often used in network traffic (Host header/URI), and standard security practices for HTTP libraries require rejecting control characters in host fields to prevent CRLF injection. The fact that the validator ignores the zone content is the flaw.\n\n Wait, I need to be sure. Does `fasthttp` escape CRLF in headers? If it does, it's safe. If it doesn't, it's unsafe. Most raw HTTP writers do not escape CRLF in the Host header if it's passed as a string; they expect the string to be pre-validated. By allowing CRLF in the host validation, the library fails to enforce the safety invariant required for its own subsequent network writes.\n\n Therefore, the bug is real and attacker-reachable, and likely leads to CRLF injection if the host is used in a header.\n\n Let's double check if `fasthttp` has a global filter. Grep for \"CRLF\" or similar in the host handling.\n But based on the specific code snippet provided, `validateIPv6Literal` clearly accepts any bytes after `%`. If I assume standard HTTP library behavior where Host headers are sensitive to CRLF, this is a vulnerability.\n\n However, is it *possible* that `fasthttp` rejects zones entirely elsewhere? The report says \"Unvalidated IPv6 zone allows arbitrary bytes\". If I can't prove CRLF injection, is it just a code quality issue? The threat model includes \"correct behaviour on malformed input\". Allowing CRLF in a host is incorrect behavior for an HTTP library.\n\n I will stick with VALID because the pattern of allowing unvalidated control characters in a host string that is subsequently used in network requests is a well-known class of vulnerabilities (CRLF Injection).\n\nCRUX: Whether the 'host' slice, which retains the unvalidated zone containing arbitrary bytes (potentially CRLF), is used in a security-sensitive context (like the Host header) without sanitization.\nGREP: Host.*host|host.*Host",
"elapsed": 74.6,
"tokens": 6775,
"tokens_in": 5341,
"tokens_out": 1434,
"cost_usd": 0.00654522,
"file": "ipv6.go",
"round": 5,
"grep_used": true,
"grep_results": "GREP `Host.*host`:\n```\nuri_test.go:61:\t\tif string(u.Host()) != host {\nuri_test.go:62:\t\t\tt.Fatalf(\"unexpected host %q. Expecting %q\", u.Host(), host)\nuri_test.go:396:\tu.SetHost(host)\nuri_test.go:543:\t\tt.Fatalf(\"Unexpected host %q. Expected %q. host=%q, uri=%q\", u.Host(), expectedHost, host, uri)\nprefork/README.md:49:$ wrk -H 'Host: localhost' -H 'Accept: text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 4 http://localhost:8080\nstreaming_test.go:136:\treq.Header.SetHost(\"localhost\")\nipv6.go:9:\terrInvalidIPv6Host = errors.New(\"invalid IPv6 host\")\nclient_test.go:1894:\t\t\tinitialHost := hostnameFromURLString(tc.initialURL)\nclient_test.go:3760:\tr.lookupCountByHost[host]++\nallocation_test.go:108:\t\tctx.Request.Header.Set(\"Host\", \"localhost\")\nuri_timing_test.go:41:\tstrHost, strURI := []byte(host), []byte(uri)\nuri.go:250:// Host returns host part, i.e. aaa.com of http://aaa.com/foo/bar?baz=123#qwe .\nuri.go:259:// SetHost sets host for the uri.\nuri.go:260:func (u *URI) SetHost(host string) {\nuri.go:265:// SetHostBytes sets host for the uri.\nuri.go:266:func (u *URI) SetHostBytes(host []byte) {\nuri.go:291:\t\tscheme, newHost, newURI := splitHostURI(host, uri)\nuri.go:321:\tparsedHost, err := parseHost(u.host)\nuri.go:407:// parseHost parses host as an authority without user\nuri.go:413:func parseHost(host []byte) ([]byte, error) {\nuri.go:896:func splitHostURI(host, uri []byte) ([]byte, []byte, []byte) {\nclient.go:508:\t\treturn fmt.Errorf(\"invalid host %q. Use HostClient for multiple hosts\", host)\nclient.go:722:// HostClient balances http requests among hosts listed in Addr.\nclient.go:724:// HostClient may be used for balancing load among multiple upstream hosts.\nclient.go:1163:\tinitialHost := hostnameFromURLString(url)\nclient.go:1245:\treturn hostnameFromHostPortBytes(host)\nclient.go:1248:func hostnameFromHostPortBytes(hostPort []byte) []byte {\nclient.go:1249:\thost, _ := splitHostPortBytes(hostPort)\nclient.go:1269:func splitHostPortBytes(hostPort []byte) ([]byte, []byte) {\nheader_test.go:478:\tif h.String() != \"GET / HTTP/1.0\\r\\nHost: foobar\\r\\nUser-Agent: ua\\r\\nNon-Special: val\\r\\nhost: notfoobar\\r\\n\\r\\n\" {\n```\n\nGREP `host.*Host`:\n```\nfasthttpadaptor/adaptor_test.go:70:\t\t\tt.Fatalf(\"unexpected host %q. Expecting %q\", r.Host, expectedHost)\nuri_test.go:62:\t\t\tt.Fatalf(\"unexpected host %q. Expecting %q\", u.Host(), host)\nuri_test.go:432:\t\tt.Fatalf(\"Unexpected host %q. Expecting %q for uri %q\", u.Host(), expectedHost, uri)\nuri_test.go:543:\t\tt.Fatalf(\"Unexpected host %q. Expected %q. host=%q, uri=%q\", u.Host(), expectedHost, host, uri)\ntcpdialer.go:480:\thost, portS, err := net.SplitHostPort(addr)\nserver_test.go:3950:\t\t\tctx.Success(\"aaa\", fmt.Appendf(nil, \"requestURI=%s, host=%s\", h.RequestURI(), h.Peek(HeaderHost)))\nserver_test.go:3993:\t\t\tctx.Success(\"aaa\", fmt.Appendf(nil, \"requestURI=%s, host=%s\", h.RequestURI(), h.Peek(HeaderHost)))\nuri.go:296:\t\thost = newHost\nuri.go:325:\tu.host = parsedHost\nuri.go:434:\t\t\thost1, err := unescape(host[:zone], encodeHost)\nuri.go:442:\t\t\thost3, err := unescape(host[i:], encodeHost)\nuri.go:466:\tif host, err = unescape(host, encodeHost); err != nil {\nfuzz_test.go:145:\t\t\tt.Fatalf(\"%q: unexpected host: %q. Expecting %q\", uri, u.Host(), nu.Host)\nclient.go:20:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:44:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:71:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:99:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:393:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:425:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:458:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:483:// scheme and host) or non-zero Host header + RequestURI.\nclient.go:505:\thost := uri.Host()\nclient.go:508:\t\treturn fmt.Errorf(\"invalid host %q. Use HostClient for multiple hosts\", host)\nclient.go:532:func (c *Client) hostClient(host []byte, isTLS bool) (*HostClient, error) {\nclient.go:1237:\treturn !isDomainOrSubdomainBytes(hostnameFromHostPortBytes(redirectHostPort), initialHost)\nclient.go:1241:\t_, host, _ := splitHostURI(nil, s2b(url))\nclient.go:1245:\treturn hostnameFromHostPortBytes(host)\nclient.go:1248:func hostnameFromHostPortBytes(hostPort []byte) []byte {\nclient.go:1249:\thost, _ := splitHostPortBytes(hostPort)\nclient.go:1351:// scheme and host) or non-zero Host header + RequestURI.\n```\n\nGREP `CRLF`:\n```\nstatus.go:170:\tneed := len(protocol) + 1 + statusCodeLen(statusCode) + 1 + len(statusText) + len(strCRLF)\nstatus.go:182:\treturn append(dst, strCRLF...)\nheaderscanner.go:23:\t\tif bytes.HasPrefix(s.b, strCRLF) {\nheaderscanner.go:28:\t\ti := bytes.Index(s.b, strCRLFCRLF)\nheader_test.go:105:\t\t\tfirstLine, _, ok := bytes.Cut(h.Header(), strCRLF)\nheader_test.go:1057:func TestResponseHeaderTrailingCRLFSuccess(t *testing.T) {\nheader_test.go:1060:\ttrailingCRLF := \"\\r\\n\\r\\n\\r\\n\"\nheader_test.go:1061:\ts := \"HTTP/1.1 200 OK\\r\\nContent-Type: aa\\r\\nContent-Length: 123\\r\\n\\r\\n\" + trailingCRLF\nheader_test.go:1069:\t// try reading the trailing CRLF. It must return EOF\nheader_test.go:1079:func TestResponseHeaderTrailingCRLFError(t *testing.T) {\nheader_test.go:1082:\ttrailingCRLF := \"\\r\\nerror\\r\\n\\r\\n\"\nheader_test.go:1083:\ts := \"HTTP/1.1 200 OK\\r\\nContent-Type: aa\\r\\nContent-Length: 123\\r\\n\\r\\n\" + trailingCRLF\nheader_test.go:1091:\t// try reading the trailing CRLF. It must return EOF\nheader_test.go:1101:func TestRequestHeaderTrailingCRLFSuccess(t *testing.T) {\nheader_test.go:1104:\ttrailingCRLF := \"\\r\\n\\r\\n\\r\\n\"\nheader_test.go:1105:\ts := \"GET / HTTP/1.1\\r\\nHost: aaa.com\\r\\n\\r\\n\" + trailingCRLF\nheader_test.go:1113:\t// try reading the trailing CRLF. It must return EOF\nheader_test.go:1123:func TestRequestHeaderTrailingCRLFError(t *testing.T) {\nheader_test.go:1126:\ttrailingCRLF := \"\\r\\nerror\\r\\n\\r\\n\"\nheader_test.go:1127:\ts := \"GET / HTTP/1.1\\r\\nHost: aaa.com\\r\\n\\r\\n\" + trailingCRLF\nheader_test.go:1135:\t// try reading the trailing CRLF. It must return EOF\nheader_test.go:2755:\t\t\tfirstLine, _, ok := bytes.Cut(h.Header(), strCRLF)\nheader.go:2257:\t// Buggy servers may leave trailing CRLFs after http body.\nheader.go:2259:\tif isOnlyCRLF(b) {\nheader.go:2366:func isOnlyCRLF(b []byte) bool {\nheader.go:2440:\th.bufV = append(h.bufV, strCRLF...)\nheader.go:2524:\treturn append(dst, strCRLF...)\nheader.go:2572:\th.bufV = append(h.bufV, strCRLF...)\nheader.go:2603:\tdst = append(dst, strCRLF...)\nheader.go:2652:\t\tdst = append(dst, strCRLF...)\n```"
},
{
"verdict": "INVALID",
"reasoning": "[ARBITER] The reported vulnerability claims that unvalidated characters in the IPv6 zone (part after '%') can lead to security issues like Host header injection if the host is later used in a request. However, the threat model for a library focuses on memory safety, correctness on malformed input, and resource bounds reachable via public APIs. The function `validateIPv6Literal` correctly validates the IPv6 address structure. It intentionally ignores the zone content for the purpose of validating the IP address format, which is standard behavior. The potential for 'Host header injection' relies on the assumption that the caller will use the unvalidated zone string directly in a security-sensitive context (like a raw HTTP request buffer) without further sanitization. In the context of `fasthttp`, the library generally handles the construction of HTTP headers safely (e.g., escaping or rejecting invalid characters when writing headers, or relying on the caller to provide valid data). More importantly, the report does not demonstrate a concrete path from the `fasthttp` public API to a memory safety violation or unbounded resource consumption caused by the zone characters. The 'ambiguity' argument is a quality-of-implementation or specific-use-case concern, not a fundamental security flaw in the library's parsing logic under the provided threat model. Furthermore, standard IPv6 zone identifiers (like 'eth0') do not contain CRLF, and if a user crafts a malicious zone, that is input validation of the user's own data, which is typically the application's responsibility unless the library explicitly claims to sanitize all output for raw HTTP transmission (which is usually handled by the header writer). Therefore, this is not a valid security vulnerability under the strict threat model.",
"round": 6,
"file": "ipv6.go",
"finding_title": "Unvalidated IPv6 zone allows arbitrary bytes",
"elapsed": 86.1,
"tokens_in": 5401,
"tokens_out": 355,
"cost_usd": 0.00333342
}
]Raw scan report for this file
This file implements Go slice/byte parsing helpers, so the main risks are not classic C memory corruption but validation logic bugs that can be triggered by attacker-controlled host bytes. I traced `host` through `validateIPv6Literal`, then into `parseIPv6Hextets` and `validIPv4`.
### Bounds / panic analysis
- `validateIPv6Literal` checks `len(host) == 0` before `host[0]`.
- `bytes.IndexByte` and `bytes.LastIndexByte` are safe for empty/nil slices.
- `addr := host[1:end]` is only reached when `end > 1`.
- `addr[lastColon-1]` is guarded by `lastColon > 0`.
- `ipv4 := addr[lastColon+1:]` is guarded by `lastColon != len(addr)-1`.
- `parseIPv6Hextets` only dereferences `s[i+1]` after ensuring `i+1 < n` or after handling the end-of-string case.
- `validIPv4` only accesses `s[start]` when `digits > 1`.
- No fixed-size buffers, no integer overflow in `validIPv4` because digit length and value are bounded, no goroutines, no shared mutable state, no `unsafe`, no command/SQL execution.
So I did not find a slice-out-of-bounds, nil dereference, panic, race condition, or unbounded allocation issue in this file.
### Security-relevant validation issues
1. **Bytes after the first `]` are ignored.**
`validateIPv6Literal` finds the first `]` and uses that as the end of the IPv6 address, but it never verifies that this `]` is the final byte of the host. Therefore inputs like `[::1]junk` or `[::1]:80]` are accepted as valid bracketed IPv6 host literals. Since host bytes are attacker-controlled, this can let an attacker append arbitrary trailing data to an otherwise valid IPv6 literal. If the caller then uses the original host in a `Host` header, request line, dial string, or any IP-literal gating logic, this can lead to protocol confusion, header/request injection, or bypass of IP-literal restrictions.
2. **IPv6 zone is not restricted.**
When `%` is present, the code only rejects an empty zone. Everything after the first `%` up to the first `]` is accepted unchanged. The zone can contain colons, extra percents, and, if raw host bytes are accepted by the caller, control characters. This is too permissive for a strict HTTP IPv6 host literal validator and can create ambiguous or unsafe host values in downstream consumers.
3. **Some valid IPv4-embedded IPv6 addresses with `::` immediately before the IPv4 tail are rejected.**
For example, `1:2:3:4:5::1.2.3.4` is a valid IPv6 text representation, but the code strips one colon for `seenDoubleAtSplit`, leaving a head ending in a single trailing colon. `parseIPv6Hextets(head, false)` then rejects the trailing colon. This is primarily a correctness/availability issue, but it can cause valid IP literals to be misclassified as hostnames or rejected, which may affect caller behavior.
```json
[
{
"severity": "high",
"title": "IPv6 host validation bypass via bytes after first closing bracket",
"function": "validateIPv6Literal",
"description": "validateIPv6Literal finds the first ']' with bytes.IndexByte and uses host[1:end] as the address, but never checks that end is the final byte. Inputs such as \"[::1]junk\" or \"[::1]:80]\" are accepted as valid bracketed IPv6 host literals. Because host bytes are attacker-controlled, this can allow trailing data to bypass validation and cause protocol confusion, Host/request-line injection, or bypass of IP-literal restrictions in callers that trust the validator."
},
{
"severity": "medium",
"title": "Unvalidated IPv6 zone allows arbitrary bytes",
"function": "validateIPv6Literal",
"description": "When a '%' is present, the code only rejects an empty zone and removes everything from the first percent onward. The zone may contain colons, additional percents, and other control characters. If the validated host is later placed in a Host header, request line, or dial string, this can produce ambiguous or unsafe host values. A strict HTTP IPv6 host validator should restrict zone characters or reject zones in this context."
},
{
"severity": "low",
"title": "Valid IPv4-embedded IPv6 addresses with '::' immediately before the IPv4 tail are rejected",
"function": "validateIPv6Literal",
"description": "For addresses like \"1:2:3:4:5::1.2.3.4\", the code sets seenDoubleAtSplit and strips one colon, leaving head with a trailing single colon. parseIPv6Hextets(head, false) then rejects the trailing colon, so syntactically valid IPv6 literals are reported as invalid. This can cause valid IP hosts to be misclassified as hostnames or rejected, leading to unexpected DNS lookups or denial-of-service in callers that use the validator for routing decisions."
}
]
```