selectorvalidator.go 3.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
package selectorvalidator

import (
	"errors"

	"github.com/ipfs/go-graphsync/ipldbridge"
	ipld "github.com/ipld/go-ipld-prime"
	ipldfree "github.com/ipld/go-ipld-prime/impl/free"
	"github.com/ipld/go-ipld-prime/traversal"
	"github.com/ipld/go-ipld-prime/traversal/selector"
	"github.com/ipld/go-ipld-prime/traversal/selector/builder"
)

var (
	// ErrInvalidLimit means this type of recursive selector limit is not supported by default
	// -- to prevent DDOS attacks
	ErrInvalidLimit = errors.New("unsupported recursive selector limit")
)

// ValidateSelector applies the default selector validation policy to a selector
// on an incoming request -- which by default is to limit recursive selectors
// to a fixed depth
func ValidateSelector(bridge ipldbridge.IPLDBridge, node ipld.Node, maxAcceptedDepth int) error {
	ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder())

	// this selector is a selector for traversing selectors...
	// it traverses the various selector types looking for recursion limit fields
	// and matches them
	s, err := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
		efsb.Insert(selector.SelectorKey_ExploreRecursive, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Limit, ssb.Matcher())
			efsb.Insert(selector.SelectorKey_Sequence, ssb.ExploreRecursiveEdge())
		}))
		efsb.Insert(selector.SelectorKey_ExploreFields, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Fields, ssb.ExploreAll(ssb.ExploreRecursiveEdge()))
		}))
		efsb.Insert(selector.SelectorKey_ExploreUnion, ssb.ExploreAll(ssb.ExploreRecursiveEdge()))
		efsb.Insert(selector.SelectorKey_ExploreAll, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Next, ssb.ExploreRecursiveEdge())
		}))
		efsb.Insert(selector.SelectorKey_ExploreIndex, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Next, ssb.ExploreRecursiveEdge())
		}))
		efsb.Insert(selector.SelectorKey_ExploreRange, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Next, ssb.ExploreRecursiveEdge())
		}))
		efsb.Insert(selector.SelectorKey_ExploreConditional, ssb.ExploreFields(func(efsb builder.ExploreFieldsSpecBuilder) {
			efsb.Insert(selector.SelectorKey_Next, ssb.ExploreRecursiveEdge())
		}))
	})).Selector()

	if err != nil {
		return err
	}

	return bridge.WalkMatching(node, s, func(progress traversal.Progress, visited ipld.Node) error {
		if visited.ReprKind() != ipld.ReprKind_Map || visited.Length() != 1 {
			return ErrInvalidLimit
		}
		kn, v, _ := visited.MapIterator().Next()
		kstr, _ := kn.AsString()
		switch kstr {
		case selector.SelectorKey_LimitDepth:
			maxDepthValue, err := v.AsInt()
			if err != nil {
				return ErrInvalidLimit
			}
			if maxDepthValue > maxAcceptedDepth {
				return ErrInvalidLimit
			}
			return nil
		case selector.SelectorKey_LimitNone:
			return ErrInvalidLimit
		default:
			return ErrInvalidLimit
		}
	})
}