DEV Community

Christophe Colombier
Christophe Colombier

Posted on

go-safecast: Safe number conversion in Go 🪄

I worked on my first open-source package last weekend.

GitHub logo ccoVeille / go-safecast

Safe number conversion in Go: address gosec G115 and cwe-190 Integer Overflow or Wraparound

🪄 go-safecast: safe numbers conversion

Go Report Card GoDoc codecov Code Climate

go-safecast solves the type conversion issues in Go

In Go, integer type conversion can lead to a silent and unexpected behavior and errors if not handled carefully.

This package helps 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() {
  var a int

  a = 42
  b, err := safecast.ToUint8(a) // everything is fine
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(b)
  // Output: 42

  a = 255 + 1
  _, err = safecast.ToUint8(a) // 256 is greater than uint8 maximum value
  if err != nil {
    fmt.Println(err)
    // Output: conversion issue: 256
…
Enter fullscreen mode Exit fullscreen mode

About the story behind this library, you can read my first article about integer overflow in Go

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))


Enter fullscreen mode Exit fullscreen mode

Go Playground

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)


Enter fullscreen mode Exit fullscreen mode

Go Playground

I'm curious about your feedbacks

Top comments (1)

Collapse
 
ldemailly profile image
Laurent Demailly • Edited

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.