Earlier this week, I thought I stumbled across a bug in the go standard library.
Earlier this week, I was wrong.
While browsing the net/http sources, I spotted what appears to be an index out of range in calls to http.Request.BasicAuth()
.
Decoding func parseBasicAuth()
:
- If the value string has prefix “Basic “
- base64 decode the remainder of the string
- find the index of the first colon (:) in the decoded result
- return username and password as the substrings before and after the colon, respectively
But wait - what if there’s nothing after the colon?
I posited that crafting an HTTP request with an Authorization header with nothing after the colon should result in a runtime error, so quickly threw together a simple http server that calls to BasicAuth()
, started it, then tested with the following client code:
enc := base64.StdEncoding.EncodeToString([]byte("username:"))
auth := fmt.Sprintf("Basic %s", enc)
r.Header.Set("Authorization", auth)
http.DefaultClient.Do(r)
… and nothing happened.
Today I discovered that it’s legal to slice an array
or string
from its length to its end.
For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range.
– The Go Programming Language Specification
Here is some code to demonstrate, runnable in The Playground:
package main
import (
"fmt"
)
func main() {
// Start with a string
s := "index"
fmt.Println("The string is:", s)
// What's at index 0?
fmt.Printf("The letter at index %d is: %s\n", 0, string(s[0])) // output: "i"
// Now as a slice 0:0
fmt.Printf("The letter at slice [%d:%d] is: %s\n", 0, 0, string(s[0:0])) // output: ""
// Now as a slice 1:1
fmt.Printf("The letter at slice [%d:%d] is: %s\n... and so on\n\n", 1, 1, string(s[1:1])) // output: ""
// Now as a slice 0:1
fmt.Printf("The letter at slice [%d:%d] is: %s\n", 0, 1, string(s[0:1])) // output: "i"
// Now as a slice 0:1
fmt.Printf("The letter at slice [%d:%d] is: %s\n... and so on\n\n", 1, 2, string(s[1:2])) // output: "n"
// last index == len(s) - 1
fmt.Printf("The letter at final index %d [len(s) - 1] is: %s\n", len(s)-1, string(s[len(s)-1])) // output: "x"
// When we go beyond that, we panic
printPanic(s)
// But for some reason, this is OK when slicing
fmt.Printf("\nThe letter at slice [%d:] is: %s\n", len(s), string(s[len(s):])) // output: ""
}
func printPanic(s string) {
defer func() {
if r := recover(); r != nil {
fmt.Println("panic recovered: ", r)
}
}()
fmt.Printf("The letter at index %d is: ", len(s))
fmt.Printf(" %s\n", string(s[len(s)]))
}
comments powered by Disqus