I worked on my first open-source package last weekend.
ccoVeille / go-safecast
Safe number conversion in Go: address gosec G115 and cwe-190 Integer Overflow or Wraparound
🪄 go-safecast: safe numbers conversion
Project purpose
In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully.
This package is made to help to convert any number to another, and report an error when if there would be a loss or overflow in the conversion
Usage
package main
import (
"fmt"
"math"
"github.com/ccoveille/go-safecast"
)
func main() {
// when there is no overflow
//
fmt.Println(safecast.ToInt8(float64(42)))
// Output: 42, nil
fmt.Println(safecast.ToInt8(int64(-1)))
// Output: -1, nil
// when there is an overflow
//
fmt.Println(safecast.ToInt8(float64(20000)))
// Output: 0 conversion issue: 20000 is greater than 127
fmt.Println(safecast.ToUint8(int64(-1)))
//
…About the story behind this library, you can read my first article about integer overflow in Go
About the gosec G115 drama, or how I faced back integer conversion overflow in Go 🤯
Christophe Colombier ・ Sep 9
As I found nothing to cope with this kind of error, except adding a lot of boilerplate for each cast to do, so I decided to make my own Go package.
@ldemailly helped me to review the code and the idea. Thank you guy.
The package is now mature enough for me to talk about it.
So instead of this
var a int
var b uint8
a = 255 + 1
b = uint8(a)
if a < 0 || a > math.MaxUint8 {
log.Println("overflow")
}
fmt.Println(b)
a = -1
b = uint8(a)
if a < 0 || a > math.MaxUint8 {
log.Println("overflow")
}
fmt.Println(b)
c, d := 255, 300
res := max(c, d)
if res < 0 || res > math.MaxInt8 {
log.Println("overflow")
}
fmt.Println(int8(res))
str := "\x99" // hexadecimal representation of Trademark ASCII character: ™
e := str[0]
if e < 0 || e > math.MaxInt8 {
log.Println("overflow")
}
fmt.Println(int8(e))
You can now do this
var a int
var b uint8
a = 255 + 1
b, err := safecast.ToUint8(a)
if err != nil {
log.Println(err)
}
fmt.Println(b)
a = -1
b, err = safecast.ToUint8(a)
if err != nil {
log.Println(err)
}
fmt.Println(b)
c, d := 255, 300
res := max(c, d)
g, err := safecast.ToInt8(res)
if err != nil {
log.Println(err)
}
fmt.Println(g)
str := "\x99" // hexadecimal representation of Trademark ASCII character: ™
e := str[0]
f, err := safecast.ToUint8(e)
if err != nil {
log.Println(err)
}
fmt.Println(f)
I'm curious about your feedbacks
Top comments (1)
Great series on a serious problem, and thanks for the mention!
If you don’t mind I’d like to offer my smaller and simpler (I think) generic version: pkg.go.dev/fortio.org/safecast for people to consider. It also has Must* variant of the conversions.