move int validation to where a error can be returned
This commit is contained in:
@@ -9,10 +9,15 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/must"
|
"github.com/ipld/go-ipld-prime/must"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
|
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromIPLD(node datamodel.Node) (Policy, error) {
|
func FromIPLD(node datamodel.Node) (Policy, error) {
|
||||||
|
if err := limits.ValidateIntegerBoundsIPLD(node); err != nil {
|
||||||
|
return nil, fmt.Errorf("policy contains integer values outside safe bounds: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return statementsFromIPLD("/", node)
|
return statementsFromIPLD("/", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package limits
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxInt53 represents the maximum safe integer in JavaScript (2^53 - 1)
|
|
||||||
MaxInt53 = 9007199254740991
|
|
||||||
// MinInt53 represents the minimum safe integer in JavaScript (-2^53 + 1)
|
|
||||||
MinInt53 = -9007199254740991
|
|
||||||
)
|
|
||||||
49
pkg/policy/limits/int.go
Normal file
49
pkg/policy/limits/int.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package limits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ipld/go-ipld-prime"
|
||||||
|
"github.com/ipld/go-ipld-prime/must"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxInt53 represents the maximum safe integer in JavaScript (2^53 - 1)
|
||||||
|
MaxInt53 = 9007199254740991
|
||||||
|
// MinInt53 represents the minimum safe integer in JavaScript (-2^53 + 1)
|
||||||
|
MinInt53 = -9007199254740991
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidateIntegerBoundsIPLD(node ipld.Node) error {
|
||||||
|
switch node.Kind() {
|
||||||
|
case ipld.Kind_Int:
|
||||||
|
val := must.Int(node)
|
||||||
|
if val > MaxInt53 || val < MinInt53 {
|
||||||
|
return fmt.Errorf("integer value %d exceeds safe bounds", val)
|
||||||
|
}
|
||||||
|
case ipld.Kind_List:
|
||||||
|
it := node.ListIterator()
|
||||||
|
for !it.Done() {
|
||||||
|
_, v, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ValidateIntegerBoundsIPLD(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ipld.Kind_Map:
|
||||||
|
it := node.MapIterator()
|
||||||
|
for !it.Done() {
|
||||||
|
_, v, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ValidateIntegerBoundsIPLD(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
82
pkg/policy/limits/int_test.go
Normal file
82
pkg/policy/limits/int_test.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package limits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||||
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateIntegerBoundsIPLD(t *testing.T) {
|
||||||
|
buildMap := func() datamodel.Node {
|
||||||
|
nb := basicnode.Prototype.Any.NewBuilder()
|
||||||
|
qp.Map(1, func(ma datamodel.MapAssembler) {
|
||||||
|
qp.MapEntry(ma, "foo", qp.Int(MaxInt53+1))
|
||||||
|
})(nb)
|
||||||
|
return nb.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
buildList := func() datamodel.Node {
|
||||||
|
nb := basicnode.Prototype.Any.NewBuilder()
|
||||||
|
qp.List(1, func(la datamodel.ListAssembler) {
|
||||||
|
qp.ListEntry(la, qp.Int(MinInt53-1))
|
||||||
|
})(nb)
|
||||||
|
return nb.Build()
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input datamodel.Node
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid int",
|
||||||
|
input: basicnode.NewInt(42),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max safe int",
|
||||||
|
input: basicnode.NewInt(MaxInt53),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "min safe int",
|
||||||
|
input: basicnode.NewInt(MinInt53),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "above MaxInt53",
|
||||||
|
input: basicnode.NewInt(MaxInt53 + 1),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "below MinInt53",
|
||||||
|
input: basicnode.NewInt(MinInt53 - 1),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested map with invalid int",
|
||||||
|
input: buildMap(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested list with invalid int",
|
||||||
|
input: buildList(),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
err := ValidateIntegerBoundsIPLD(tt.input)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "exceeds safe bounds")
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,7 +69,11 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case string:
|
case string:
|
||||||
return basicnode.NewString(val), nil
|
return basicnode.NewString(val), nil
|
||||||
case int:
|
case int:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
i := int64(val)
|
||||||
|
if i > limits.MaxInt53 || i < limits.MinInt53 {
|
||||||
|
return nil, fmt.Errorf("integer value %d exceeds safe integer bounds", i)
|
||||||
|
}
|
||||||
|
return basicnode.NewInt(i), nil
|
||||||
case int8:
|
case int8:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case int16:
|
case int16:
|
||||||
@@ -77,6 +81,9 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case int32:
|
case int32:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case int64:
|
case int64:
|
||||||
|
if val > limits.MaxInt53 || val < limits.MinInt53 {
|
||||||
|
return nil, fmt.Errorf("integer value %d exceeds safe integer bounds", val)
|
||||||
|
}
|
||||||
return basicnode.NewInt(val), nil
|
return basicnode.NewInt(val), nil
|
||||||
case uint:
|
case uint:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
@@ -87,6 +94,9 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case uint32:
|
case uint32:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case uint64:
|
case uint64:
|
||||||
|
if val > uint64(limits.MaxInt53) {
|
||||||
|
return nil, fmt.Errorf("unsigned integer value %d exceeds safe integer bounds", val)
|
||||||
|
}
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case float32:
|
case float32:
|
||||||
return basicnode.NewFloat(float64(val)), nil
|
return basicnode.NewFloat(float64(val)), nil
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
"github.com/ipld/go-ipld-prime/printer"
|
"github.com/ipld/go-ipld-prime/printer"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -295,14 +296,11 @@ func TestAnyAssembleIntegerOverflow(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := Any(tt.input)
|
||||||
if tt.shouldErr {
|
if tt.shouldErr {
|
||||||
require.Panics(t, func() {
|
require.Error(t, err)
|
||||||
anyAssemble(tt.input)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
require.NotPanics(t, func() {
|
require.NoError(t, err)
|
||||||
anyAssemble(tt.input)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
"github.com/ipld/go-ipld-prime/must"
|
"github.com/ipld/go-ipld-prime/must"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Match determines if the IPLD node satisfies the policy.
|
// Match determines if the IPLD node satisfies the policy.
|
||||||
@@ -268,10 +266,6 @@ func isOrdered(expected ipld.Node, actual ipld.Node, satisfies func(order int) b
|
|||||||
a := must.Int(actual)
|
a := must.Int(actual)
|
||||||
b := must.Int(expected)
|
b := must.Int(expected)
|
||||||
|
|
||||||
if a > limits.MaxInt53 || a < limits.MinInt53 || b > limits.MaxInt53 || b < limits.MinInt53 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return satisfies(cmp.Compare(a, b))
|
return satisfies(cmp.Compare(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -902,38 +901,3 @@ func TestPartialMatch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntegerOverflow(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
expected ipld.Node
|
|
||||||
actual ipld.Node
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid integers",
|
|
||||||
expected: literal.Int(42),
|
|
||||||
actual: literal.Int(43),
|
|
||||||
want: true, // for gt comparison
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "exceeds MaxInt53",
|
|
||||||
expected: literal.Int(limits.MaxInt53 + 1),
|
|
||||||
actual: literal.Int(42),
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "below MinInt53",
|
|
||||||
expected: literal.Int(limits.MinInt53 - 1),
|
|
||||||
actual: literal.Int(42),
|
|
||||||
want: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result := isOrdered(tt.expected, tt.actual, gt)
|
|
||||||
require.Equal(t, tt.want, result)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Selector describes a UCAN policy selector, as specified here:
|
// Selector describes a UCAN policy selector, as specified here:
|
||||||
@@ -22,6 +23,10 @@ type Selector []segment
|
|||||||
// - a resolutionerr error if not being able to resolve to a node
|
// - a resolutionerr error if not being able to resolve to a node
|
||||||
// - nil and no errors, if the selector couldn't match on an optional segment (with ?).
|
// - nil and no errors, if the selector couldn't match on an optional segment (with ?).
|
||||||
func (s Selector) Select(subject ipld.Node) (ipld.Node, error) {
|
func (s Selector) Select(subject ipld.Node) (ipld.Node, error) {
|
||||||
|
if err := limits.ValidateIntegerBoundsIPLD(subject); err != nil {
|
||||||
|
return nil, fmt.Errorf("node contains integer values outside safe bounds: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return resolve(s, subject, nil)
|
return resolve(s, subject, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user