IPv6 host validation bypass via bytes after first closing bracket
high swesweep-fasthttp · ipv6.go · validateIPv6Literal · run #4
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.
Triage
- Verdict
- INVALID confidence 0.0
- Model
- qwen/qwen3.8-27b
- Rounds
- 5
- Cost
- $0.0154 · 484.9 s
The reported bug pattern is real: `validateIPv6Literal` in `ipv6.go` extracts the address up to the first `]` but does not verify that `]` is the final character, allowing inputs like `[::1]junk` to pass validation. However, under the provided threat model for a reusable library, a vulnerability must cause meaningful harm such as memory corruption, code execution, data corruption, or authentication bypass via untrusted input. The function is a validation helper that returns an error; it does not perform memory allocation, pointer arithmetic on the host buffer, or state modification that would lead to memory safety issues if it erroneously returns `nil`. The primary consequence of a validation bypass in an HTTP library is that a malformed host string might proceed to subsequent parsing stages (e.g., port extraction or dialing). Standard Go network libraries and fasthttp will treat `[::1]junk` as an invalid host or fail to dial, resulting in a functional error (connection failure) rather than a security compromise. Since the bypass does not lead to a crash, RCE, or bypass of security controls (like authentication), but only results in a failed request or connection error, it is classified as a code quality/functional bug rather than a security vulnerability in this context. CRUX: The validation bypass for trailing garbage after the IPv6 closing bracket results only in functional errors (e.g., failed connections) and does not lead to memory corruption, code execution, or authentication bypass. GREP: validateIPv6Literal
All rounds
[
{
"finding_title": "IPv6 host validation bypass via bytes after first closing bracket",
"verdict": "INVALID",
"reasoning": "The code in `validateIPv6Literal` extracts the IPv6 address from the input `host` by finding the first `]` (`end := bytes.IndexByte(host, ']')`) and using `addr := host[1:end]`. It then validates `addr` strictly. However, it returns `nil` (indicating success/validity) without checking if `end` is the last byte of `host`. This means an input like `[::1]junk` will be accepted as a valid IPv6 host literal because the substring `::1` is valid. \n\nWhile this is a bug in validation logic (it should reject any characters after `]`), the security impact is minimal to nonexistent in the context of an HTTP library. \n1. **Protocol Confusion/Injection**: If a caller uses this to validate a Host header or URI, they will likely strip the brackets or use the host string for further processing. Standard practice in Go HTTP (and fasthttp) is to parse the host into components (host, port). If `host` is `[::1]junk`, the port parsing logic (which looks for `:port` after `]`) would likely fail or result in an error, or if it blindly takes the rest, it might create a malformed connection target. However, this results in a connection error (Refused/Unknown Host) for the caller, not a security bypass. \n2. **No Bypass of Restrictions**: The report claims this can bypass IP-literal restrictions. However, the validator's job is to say \"this is a valid IPv6 literal\". If it says yes for `[::1]junk`, it is factually wrong about the syntax, but it doesn't allow an attacker to execute code or crash the server. It just allows a syntactically invalid string to pass a check that should have failed. Subsequent steps (like dialing the connection) will fail. \n3. **Denial of Service**: An attacker cannot use this to crash the service or consume unbounded resources. \n\nUnder the threat model, a vulnerability must cause meaningful harm (crash, code execution, data corruption, auth bypass). A validation bypass that only results in the client/server failing to connect to a malformed host is a functional bug, not a security vulnerability. The library is used to *consume* or *send* requests; if the host is malformed, the request fails. It does not lead to the host process being compromised. Therefore, it is not security-relevant under the provided threat model.\n\nCRUX: Does the acceptance of trailing garbage after ']' in validateIPv6Literal lead to memory corruption, code execution, or authentication bypass, or does it merely result in a failed connection/logic error?\nGREP: validateIPv6Literal",
"elapsed": 154.2,
"tokens": 3820,
"tokens_in": 3228,
"tokens_out": 592,
"cost_usd": 0.00313176,
"file": "ipv6.go",
"round": 1,
"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\",\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
},
{
"finding_title": "IPv6 host validation bypass via bytes after first closing bracket",
"verdict": "INVALID",
"reasoning": "The reported bug is a logic error in `validateIPv6Literal` where it fails to check if the closing bracket `]` is the final character, allowing trailing junk (e.g., `[::1]junk`) to pass validation. To determine if this is a security vulnerability under the provided threat model, I must verify if this validation bypass leads to meaningful harm (crash, code execution, data corruption, or auth bypass) or if it is merely a functional bug. \n\n1. **Code Verification**: The code in `ipv6.go` indeed uses `bytes.IndexByte(host, ']')` to find the end of the address and extracts `host[1:end]` for validation. It does not check `end == len(host)-1`. Thus, the bug pattern exists.\n2. **Impact Analysis**: The function is called in `uri.go` during URL parsing. If `validateIPv6Literal` returns `nil` (valid) for a malformed host like `[::1]junk`, the parsing logic proceeds. \n3. **Downstream Consequences**: In HTTP/1.1 and standard Go HTTP libraries, after the host is extracted, the port is typically parsed from the remainder. If the host is `[::1]junk`, the remainder is `junk`. If the code attempts to parse `junk` as a port, it will fail. If the code treats the entire string as the host, the subsequent `net.Dial` or connection logic will attempt to resolve `[::1]junk` or similar, which will result in a \"Name does not resolve\" or \"Invalid port\" error. \n4. **Security Relevance**: The threat model defines assets as \"Memory safety and integrity... correct behaviour on malformed input; resource bounds\". A validation bypass that results in a connection failure (functional error) does not compromise memory safety (no OOB access, no UAF), does not allow code execution, and does not bypass authentication. It prevents a valid request from being sent (a minor availability issue for legitimate users if they made a typo, but not a vulnerability for an attacker who wants to exploit the system). An attacker cannot use this to crash the server, corrupt memory, or gain unauthorized access. Therefore, it is a code quality/functional bug, not a security vulnerability.\n\nCRUX: Does accepting trailing garbage after the IPv6 closing bracket lead to memory corruption, code execution, or authentication bypass, or does it merely result in a failed connection/logic error?\nGREP: validateIPv6Literal",
"elapsed": 40.5,
"tokens": 4587,
"tokens_in": 4051,
"tokens_out": 536,
"cost_usd": 0.00330942,
"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\",\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
},
{
"finding_title": "IPv6 host validation bypass via bytes after first closing bracket",
"verdict": "INVALID",
"reasoning": "The code in `validateIPv6Literal` indeed fails to check that the closing bracket `]` is the last byte of the `host` string, allowing inputs like `[::1]junk` to pass validation as valid IPv6 literals. However, under the provided threat model, a security vulnerability must result in meaningful harm such as memory corruption, code execution, data corruption, or authentication bypass. This validation bypass merely allows a syntactically invalid host string to be accepted. In the context of an HTTP library, this leads to a functional error where subsequent operations (like dialing a connection or parsing a port) will fail gracefully because the resulting string is malformed for standard network operations. It does not lead to out-of-bounds memory access, integer overflow, or infinite loops (DoS). Therefore, it is a code quality/functional bug rather than a security vulnerability.\n\nCRUX: The validation bypass for trailing garbage after the IPv6 closing bracket does not lead to memory corruption, code execution, or authentication bypass, only to functional connection failures.\nGREP: validateIPv6Literal",
"elapsed": 139.1,
"tokens": 4899,
"tokens_in": 4664,
"tokens_out": 235,
"cost_usd": 0.00266388,
"file": "ipv6.go",
"round": 3,
"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\",\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
},
{
"finding_title": "IPv6 host validation bypass via bytes after first closing bracket",
"verdict": "INVALID",
"reasoning": "The bug pattern is real: `validateIPv6Literal` uses `bytes.IndexByte` to find the first `]` and validates the substring `host[1:end]`, but does not verify that `end` is the final byte of `host`. Thus, inputs like `[::1]junk` pass validation. However, under the provided threat model for a library, a vulnerability requires meaningful harm such as memory safety violations, code execution, data corruption, or resource exhaustion (DoS). The bypass allows a syntactically invalid host string to be considered 'valid'. In an HTTP library context, this results in a functional failure (e.g., the host string is malformed for subsequent dialing or header construction, leading to a connection error or protocol error), but it does not lead to out-of-bounds memory access, type confusion, or unbounded resource consumption. The Go runtime's slice operations are safe, and the parser logic in `parseIPv6Hextets` is bounded. Therefore, this is a logic error (incorrect validation) but not a security vulnerability that compromises the integrity or availability of the host process in a meaningful way relative to the threat model's definition of 'valid' vulnerabilities (crash, RCE, auth bypass). It falls under code quality/functional bug.\n\nCRUX: The validation bypass allows invalid strings to be accepted but does not lead to memory corruption, code execution, or DoS; it only causes functional errors.\nGREP: validateIPv6Literal",
"elapsed": 81.5,
"tokens": 5300,
"tokens_in": 4981,
"tokens_out": 319,
"cost_usd": 0.00304902,
"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\",\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
},
{
"finding_title": "IPv6 host validation bypass via bytes after first closing bracket",
"verdict": "INVALID",
"reasoning": "The reported bug pattern is real: `validateIPv6Literal` in `ipv6.go` extracts the address up to the first `]` but does not verify that `]` is the final character, allowing inputs like `[::1]junk` to pass validation. However, under the provided threat model for a reusable library, a vulnerability must cause meaningful harm such as memory corruption, code execution, data corruption, or authentication bypass via untrusted input. The function is a validation helper that returns an error; it does not perform memory allocation, pointer arithmetic on the host buffer, or state modification that would lead to memory safety issues if it erroneously returns `nil`. The primary consequence of a validation bypass in an HTTP library is that a malformed host string might proceed to subsequent parsing stages (e.g., port extraction or dialing). Standard Go network libraries and fasthttp will treat `[::1]junk` as an invalid host or fail to dial, resulting in a functional error (connection failure) rather than a security compromise. Since the bypass does not lead to a crash, RCE, or bypass of security controls (like authentication), but only results in a failed request or connection error, it is classified as a code quality/functional bug rather than a security vulnerability in this context.\n\nCRUX: The validation bypass for trailing garbage after the IPv6 closing bracket results only in functional errors (e.g., failed connections) and does not lead to memory corruption, code execution, or authentication bypass.\nGREP: validateIPv6Literal",
"elapsed": 69.6,
"tokens": 5707,
"tokens_in": 5382,
"tokens_out": 325,
"cost_usd": 0.00323544,
"file": "ipv6.go",
"round": 5,
"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\",\nipv6.go:14:func validateIPv6Literal(host []byte) error {\nuri.go:469:\tif err = validateIPv6Literal(host); err != nil {\n```"
}
]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."
}
]
```