Skip to content
Open
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
32 changes: 32 additions & 0 deletions common/policydsl/policyparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const (
GateOutOf = "OutOf"
)

const (
maxPolicyStringLength = 100000
maxPolicyNestingDepth = 1024
)

// Role values for principals
const (
RoleAdmin = "admin"
Expand Down Expand Up @@ -233,6 +238,29 @@ func newContext() *context {
return &context{IDNum: 0, principals: make([]*mb.MSPPrincipal, 0)}
}

func validatePolicyString(policy string) error {
if len(policy) > maxPolicyStringLength {
return fmt.Errorf("policy string exceeds maximum length of %d characters", maxPolicyStringLength)
}

depth := 0
for _, r := range policy {
switch r {
case '(':
depth++
if depth > maxPolicyNestingDepth {
return fmt.Errorf("policy string exceeds maximum nesting depth of %d", maxPolicyNestingDepth)
}
case ')':
if depth > 0 {
depth--
}
}
}

return nil
}

// FromString takes a string representation of the policy,
// parses it and returns a SignaturePolicyEnvelope that
// implements that policy. The supported language is as follows:
Expand All @@ -252,6 +280,10 @@ func newContext() *context {
// - ROLE takes the value of any of the RoleXXX constants representing
// the required role
func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
if err := validatePolicyString(policy); err != nil {
return nil, err
}

// first we translate the and/or business into outof gates
intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(
policy, map[string]govaluate.ExpressionFunction{
Expand Down
20 changes: 20 additions & 0 deletions common/policydsl/policyparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ SPDX-License-Identifier: Apache-2.0
package policydsl

import (
"fmt"
"strings"
"testing"

"github.com/hyperledger/fabric-protos-go-apiv2/common"
Expand Down Expand Up @@ -406,3 +408,21 @@ func TestSecondPassBoundaryCheck(t *testing.T) {
require.Nil(t, p3)
require.EqualError(t, err3, "invalid t-out-of-n predicate, t 4, n 2")
}

func TestPolicyStringGuardrails(t *testing.T) {
t.Run("rejects excessive nesting", func(t *testing.T) {
policy := strings.Repeat("OR(", maxPolicyNestingDepth+1) + "'A.member'" + strings.Repeat(")", maxPolicyNestingDepth+1)

p, err := FromString(policy)
require.Nil(t, p)
require.EqualError(t, err, fmt.Sprintf("policy string exceeds maximum nesting depth of %d", maxPolicyNestingDepth))
})

t.Run("rejects oversized policy strings", func(t *testing.T) {
policy := "OR('A.member', '" + strings.Repeat("B", maxPolicyStringLength) + ".member')"

p, err := FromString(policy)
require.Nil(t, p)
require.EqualError(t, err, fmt.Sprintf("policy string exceeds maximum length of %d characters", maxPolicyStringLength))
})
}