Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions region/region.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ type Regions []Region
// which all of the other region information can be derived. The other environment
// variables are optional and can be used to override the default values.
type Info struct {
ID Region `json:"region" msg:"region" env:"REGION_INFO_ID" split_words:"true" desc:"the r8l specific region identifier code (must be valid if not empty)"`
Name string `json:"name" msg:"name" env:"REGION_INFO_NAME" split_words:"true" desc:"the human readable name of the region"`
CountryCode string `json:"country_code" msg:"country_code" env:"REGION_INFO_COUNTRY" split_words:"true" desc:"the ISO 3166-1 country code for the region"`
Zone string `json:"zone" msg:"zone" env:"REGION_INFO_ZONE" split_words:"true" desc:"the zone of the datacenter for the region"`
Cloud string `json:"cloud" msg:"cloud" env:"REGION_INFO_CLOUD" split_words:"true" desc:"the cloud provider for the region"`
Cluster string `json:"cluster" msg:"cluster" env:"REGION_INFO_CLUSTER" split_words:"true" desc:"the r8l cluster name for the region"`
country *country.Country `json:"-" msg:"-" env:"-"`
ID Region `json:"region" yaml:"region" msg:"region" env:"REGION_INFO_ID" split_words:"true" desc:"the r8l specific region identifier code (must be valid if not empty)"`
Name string `json:"name" yaml:"name" msg:"name" env:"REGION_INFO_NAME" split_words:"true" desc:"the human readable name of the region"`
CountryCode string `json:"country_code" yaml:"country_code" msg:"country_code" env:"REGION_INFO_COUNTRY" split_words:"true" desc:"the ISO 3166-1 country code for the region"`
Zone string `json:"zone" yaml:"zone" msg:"zone" env:"REGION_INFO_ZONE" split_words:"true" desc:"the zone of the datacenter for the region"`
Cloud string `json:"cloud" yaml:"cloud" msg:"cloud" env:"REGION_INFO_CLOUD" split_words:"true" desc:"the cloud provider for the region"`
Cluster string `json:"cluster" yaml:"cluster" msg:"cluster" env:"REGION_INFO_CLUSTER" split_words:"true" desc:"the r8l cluster name for the region"`
country *country.Country `json:"-" yaml:"-" msg:"-" env:"-"`
}

// Returns a list of all available regions known by the Rotational system.
Expand Down
1 change: 1 addition & 0 deletions semver/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ var (
ErrInvalidSemVer = errors.New("invalid semantic version")
ErrInvalidRange = errors.New("invalid semantic range")
ErrScanValue = errors.New("could not scan source value")
ErrDataSize = errors.New("invalid size for data")
)
104 changes: 104 additions & 0 deletions semver/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package semver

import (
"encoding/binary"
"encoding/json"
"fmt"
"regexp"
Expand Down Expand Up @@ -229,6 +230,90 @@ func isNumeric(s string) (v uint16, ok bool) {
// Serialization and Deserialization
//===========================================================================

func (v Version) MarshalBinary() (data []byte, err error) {
pr := []byte(v.PreRelease)
bm := []byte(v.BuildMeta)

size := 5*binary.MaxVarintLen16 + len(pr) + len(bm)
data = make([]byte, size)

i := binary.PutUvarint(data, uint64(v.Major))
i += binary.PutUvarint(data[i:], uint64(v.Minor))
i += binary.PutUvarint(data[i:], uint64(v.Patch))
i += binary.PutUvarint(data[i:], uint64(len(pr)))
i += copy(data[i:], pr)
i += binary.PutUvarint(data[i:], uint64(len(bm)))
i += copy(data[i:], bm)

data = data[:i:i]
return data, nil
}

func (v *Version) UnmarshalBinary(data []byte) error {
if len(data) < 5 {
return ErrDataSize
}

var (
i, j int
c uint64
)

c, j = binary.Uvarint(data[i:])
if j <= 0 {
return ErrDataSize
}
v.Major = uint16(c)

i += j
c, j = binary.Uvarint(data[i:])
if j <= 0 {
return ErrDataSize
}
v.Minor = uint16(c)

i += j
c, j = binary.Uvarint(data[i:])
if j <= 0 {
return ErrDataSize
}
v.Patch = uint16(c)

i += j
c, j = binary.Uvarint(data[i:])
if j <= 0 {
return ErrDataSize
}

i += j
if c > 0 {
if int(c) > len(data[i:]) {
return ErrDataSize
}
v.PreRelease = string(data[i : i+int(c)])
} else {
v.PreRelease = ""
}

i += int(c)
c, j = binary.Uvarint(data[i:])
if j <= 0 {
return ErrDataSize
}

i += j
if c > 0 {
if int(c) > len(data[i:]) {
return ErrDataSize
}
v.BuildMeta = string(data[i : i+int(c)])
} else {
v.BuildMeta = ""
}

return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnmarshalBinary panics on malformed input data

Medium Severity

UnmarshalBinary never checks the return value j from binary.Uvarint. When j == 0 (buffer too small) or j < 0 (overflow), the index i fails to advance correctly, leading to reading garbage. More critically, the slices data[i : i+int(c)] for PreRelease and BuildMeta have no bounds check — a crafted or corrupted input with a large length prefix c causes an out-of-bounds panic.

Fix in Cursor Fix in Web


func (v Version) MarshalText() ([]byte, error) {
return []byte(v.String()), nil
}
Expand Down Expand Up @@ -262,6 +347,25 @@ func (v *Version) UnmarshalJSON(data []byte) error {
return nil
}

func (v Version) MarshalYAML() (interface{}, error) {
return v.String(), nil
}

func (v *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
var vers string
if err := unmarshal(&vers); err != nil {
return err
}

parsed, err := Parse(vers)
if err != nil {
return err
}

*v = parsed
return nil
}

//===========================================================================
// SQL Interfaces
//===========================================================================
Expand Down
56 changes: 45 additions & 11 deletions semver/semver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,22 @@ func TestSpecifies(t *testing.T) {
}

func TestMarshal(t *testing.T) {
t.Run("Binary", func(t *testing.T) {
for i := 0; i < 128; i++ {
a := randVersion()
data, err := a.MarshalBinary()
assert.Ok(t, err)

b := Version{}
err = b.UnmarshalBinary(data)
assert.Ok(t, err)

assert.Equal(t, a, b)
}
})

t.Run("Text", func(t *testing.T) {
for i := 0; i < 100; i++ {
for i := 0; i < 128; i++ {
a := randVersion()
text, err := a.MarshalText()
assert.Ok(t, err)
Expand All @@ -159,19 +173,39 @@ func TestMarshal(t *testing.T) {
})

t.Run("JSON", func(t *testing.T) {
a := randVersion()
text, err := json.Marshal(a)
assert.Ok(t, err)
for i := 0; i < 128; i++ {
a := randVersion()
text, err := json.Marshal(a)
assert.Ok(t, err)

var s string
assert.Ok(t, json.Unmarshal(text, &s))
assert.True(t, Valid(s))
var s string
assert.Ok(t, json.Unmarshal(text, &s))
assert.True(t, Valid(s))

b := Version{}
err = json.Unmarshal(text, &b)
assert.Ok(t, err)
b := Version{}
err = json.Unmarshal(text, &b)
assert.Ok(t, err)

assert.Equal(t, a, b)
assert.Equal(t, a, b)
}
})
t.Run("YAML", func(t *testing.T) {
t.Skip("yaml is an external dependency so is not tested")
// for i := 0; i < 128; i++ {
// a := randVersion()
// text, err := yaml.Marshal(a)
// assert.Ok(t, err)

// var s string
// assert.Ok(t, yaml.Unmarshal(text, &s))
// assert.True(t, Valid(s))

// b := Version{}
// err = yaml.Unmarshal(text, &b)
// assert.Ok(t, err)

// assert.Equal(t, a, b)
// }
})
}

Expand Down
Loading