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
59 changes: 29 additions & 30 deletions common/policydsl/policyparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"strconv"
"strings"

"github.com/Knetic/govaluate"
"github.com/expr-lang/expr"
cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
mb "github.com/hyperledger/fabric-protos-go-apiv2/msp"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -111,6 +111,8 @@ func firstPass(args ...any) (any, error) {
case float32:
case float64:
toret.WriteString(strconv.Itoa(int(t)))
case int:
toret.WriteString(strconv.Itoa(t))
default:
return nil, fmt.Errorf("unexpected type %s", reflect.TypeOf(arg))
}
Expand Down Expand Up @@ -143,6 +145,8 @@ func secondPass(args ...any) (any, error) {
switch arg := args[1].(type) {
case float64:
t = int(arg)
case int:
t = arg
default:
return nil, fmt.Errorf("unrecognized type, expected a number, got %s", reflect.TypeOf(args[1]))
}
Expand Down Expand Up @@ -253,24 +257,23 @@ func newContext() *context {
// the required role
func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
// first we translate the and/or business into outof gates
intermediate, err := govaluate.NewEvaluableExpressionWithFunctions(
policy, map[string]govaluate.ExpressionFunction{
GateAnd: and,
strings.ToLower(GateAnd): and,
strings.ToUpper(GateAnd): and,
GateOr: or,
strings.ToLower(GateOr): or,
strings.ToUpper(GateOr): or,
GateOutOf: outof,
strings.ToLower(GateOutOf): outof,
strings.ToUpper(GateOutOf): outof,
},
)
env := map[string]interface{}{
GateAnd: and,
strings.ToLower(GateAnd): and,
strings.ToUpper(GateAnd): and,
GateOr: or,
strings.ToLower(GateOr): or,
strings.ToUpper(GateOr): or,
GateOutOf: outof,
strings.ToLower(GateOutOf): outof,
strings.ToUpper(GateOutOf): outof,
}
intermediate, err := expr.Compile(policy, expr.Env(env))
if err != nil {
return nil, err
}

intermediateRes, err := intermediate.Evaluate(map[string]any{})
intermediateRes, err := expr.Run(intermediate, env)
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
Expand All @@ -282,7 +285,6 @@ func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {

return nil, err
}

resStr, ok := intermediateRes.(string)
if !ok {
return nil, fmt.Errorf("invalid policy string '%s'", policy)
Expand All @@ -294,15 +296,14 @@ func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
// to user-implemented functions other than via arguments.
// We need this argument because we need a global place where
// we put the identities that the policy requires
exp, err := govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": firstPass},
)
env = map[string]interface{}{
"outof": firstPass,
}
exp, err := expr.Compile(resStr, expr.Env(env))
if err != nil {
return nil, err
}

res, err := exp.Evaluate(map[string]any{})
res, err := expr.Run(exp, env)
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
Expand All @@ -321,18 +322,16 @@ func FromString(policy string) (*cb.SignaturePolicyEnvelope, error) {
}

ctx := newContext()
parameters := make(map[string]any, 1)
parameters["ID"] = ctx

exp, err = govaluate.NewEvaluableExpressionWithFunctions(
resStr,
map[string]govaluate.ExpressionFunction{"outof": secondPass},
)
env = map[string]interface{}{
"outof": secondPass,
"ID": ctx,
}
exp, err = expr.Compile(resStr, expr.Env(env))
if err != nil {
return nil, err
}

res, err = exp.Evaluate(parameters)
res, err = expr.Run(exp, env)
if err != nil {
// attempt to produce a meaningful error
if regexErr.MatchString(err.Error()) {
Expand Down
28 changes: 14 additions & 14 deletions common/policydsl/policyparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,13 @@ func TestMSPIDWIthSpecialChars(t *testing.T) {

func TestBadStringsNoPanic(t *testing.T) {
_, err := FromString("OR('A.member', Bmember)") // error after 1st Evaluate()
require.EqualError(t, err, "unrecognized token 'Bmember' in policy string")
require.ErrorContains(t, err, "unknown name Bmember")

_, err = FromString("OR('A.member', 'Bmember')") // error after 2nd Evalute()
require.EqualError(t, err, "unrecognized token 'Bmember' in policy string")
require.ErrorContains(t, err, "unknown name Bmember")

_, err = FromString(`OR('A.member', '\'Bmember\'')`) // error after 3rd Evalute()
require.EqualError(t, err, "unrecognized token 'Bmember' in policy string")
require.ErrorContains(t, err, "unknown name Bmember")
}

func TestNodeOUs(t *testing.T) {
Expand Down Expand Up @@ -310,39 +310,39 @@ func TestOutOfNumIsString(t *testing.T) {
func TestOutOfErrorCase(t *testing.T) {
p1, err1 := FromString("") // 1st NewEvaluableExpressionWithFunctions() returns an error
require.Nil(t, p1)
require.EqualError(t, err1, "Unexpected end of expression")
require.EqualError(t, err1, "unexpected token EOF")

p2, err2 := FromString("OutOf(1)") // outof() if len(args)<2
require.Nil(t, p2)
require.EqualError(t, err2, "expected at least two arguments to NOutOf. Given 1")
require.ErrorContains(t, err2, "expected at least two arguments to NOutOf. Given 1")

p3, err3 := FromString("OutOf(true, 'A.member')") // outof() }else{. 1st arg is non of float, int, string
require.Nil(t, p3)
require.EqualError(t, err3, "unexpected type bool")
require.ErrorContains(t, err3, "unexpected type bool")

p4, err4 := FromString("OutOf(1, 2)") // oufof() switch default. 2nd arg is not string.
require.Nil(t, p4)
require.EqualError(t, err4, "unexpected type float64")
require.ErrorContains(t, err4, "unexpected type int")

p5, err5 := FromString("OutOf(1, 'true')") // firstPass() switch default
require.Nil(t, p5)
require.EqualError(t, err5, "unexpected type bool")
require.ErrorContains(t, err5, "unexpected type bool")

p6, err6 := FromString(`OutOf('\'\\\'A\\\'\'', 'B.member')`) // secondPass() switch args[1].(type) default
require.Nil(t, p6)
require.EqualError(t, err6, "unrecognized type, expected a number, got string")
require.ErrorContains(t, err6, "unrecognized type, expected a number, got string")

p7, err7 := FromString(`OutOf(1, '\'1\'')`) // secondPass() switch args[1].(type) default
require.Nil(t, p7)
require.EqualError(t, err7, "unrecognized type, expected a principal or a policy, got float64")
require.ErrorContains(t, err7, "unrecognized type, expected a principal or a policy, got int")

p8, err8 := FromString(`''`) // 2nd NewEvaluateExpressionWithFunction() returns an error
require.Nil(t, p8)
require.EqualError(t, err8, "Unexpected end of expression")
require.EqualError(t, err8, "unexpected token EOF")

p9, err9 := FromString(`'\'\''`) // 3rd NewEvaluateExpressionWithFunction() returns an error
require.Nil(t, p9)
require.EqualError(t, err9, "Unexpected end of expression")
require.EqualError(t, err9, "unexpected token EOF")
}

func TestBadStringBeforeFAB11404_ThisCanDeleteAfterFAB11404HasMerged(t *testing.T) {
Expand All @@ -367,7 +367,7 @@ func TestSecondPassBoundaryCheck(t *testing.T) {
// Prohibit t<0
p0, err0 := FromString("OutOf(-1, 'A.member', 'B.member')")
require.Nil(t, p0)
require.EqualError(t, err0, "invalid t-out-of-n predicate, t -1, n 2")
require.ErrorContains(t, err0, "invalid t-out-of-n predicate, t -1, n 2")

// Permit t==0 : always satisfied policy
// There is no clear usecase of t=0, but somebody may already use it, so we don't treat as an error.
Expand Down Expand Up @@ -404,5 +404,5 @@ func TestSecondPassBoundaryCheck(t *testing.T) {
// Prohibit t>n + 1
p3, err3 := FromString("OutOf(4, 'A.member', 'B.member')")
require.Nil(t, p3)
require.EqualError(t, err3, "invalid t-out-of-n predicate, t 4, n 2")
require.ErrorContains(t, err3, "invalid t-out-of-n predicate, t 4, n 2")
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ go 1.26.1
require (
code.cloudfoundry.org/clock v1.15.0
github.com/IBM/idemix v0.0.2-0.20240913182345-72941a5f41cd
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
github.com/VictoriaMetrics/fastcache v1.12.2
github.com/bits-and-blooms/bitset v1.20.0
github.com/cheggaaa/pb/v3 v3.1.5
github.com/davecgh/go-spew v1.1.1
github.com/expr-lang/expr v1.17.8
github.com/go-kit/kit v0.13.0
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/gorilla/handlers v1.5.2
Expand Down Expand Up @@ -48,6 +48,7 @@ require (
github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20240913182345-72941a5f41cd // indirect
github.com/IBM/idemix/bccsp/types v0.0.0-20240913182345-72941a5f41cd // indirect
github.com/IBM/mathlib v0.0.3-0.20250709075152-a138079496c3 // indirect
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM=
github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
Expand Down
9 changes: 4 additions & 5 deletions internal/configtxgen/encoder/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ import (
"fmt"
"time"

. "github.com/hyperledger/fabric/internal/test"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

cb "github.com/hyperledger/fabric-protos-go-apiv2/common"
ab "github.com/hyperledger/fabric-protos-go-apiv2/orderer"
"github.com/hyperledger/fabric-protos-go-apiv2/orderer/etcdraft"
Expand All @@ -23,7 +19,10 @@ import (
"github.com/hyperledger/fabric/internal/configtxgen/encoder/fakes"
"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
"github.com/hyperledger/fabric/internal/pkg/identity"
. "github.com/hyperledger/fabric/internal/test"
"github.com/hyperledger/fabric/protoutil"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"google.golang.org/protobuf/proto"
)

Expand Down Expand Up @@ -202,7 +201,7 @@ var _ = Describe("Encoder", func() {

It("wraps and returns the error", func() {
err := encoder.AddPolicies(cg, policies, "Readers")
Expect(err).To(MatchError("invalid signature policy rule 'garbage': unrecognized token 'garbage' in policy string"))
Expect(err).To(MatchError("invalid signature policy rule 'garbage': unknown name garbage (1:1)\n | garbage\n | ^"))
})
})

Expand Down
2 changes: 1 addition & 1 deletion internal/peer/chaincode/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func TestCollectionParsing(t *testing.T) {
{
name: "Invalid member orgs policy",
collectionConfig: sampleCollectionConfigBad,
expectedErr: "invalid policy barf: unrecognized token 'barf' in policy string",
expectedErr: "invalid policy barf: unknown name barf (1:1)\n | barf\n | ^",
},
{
name: "Invalid collection config",
Expand Down
1 change: 1 addition & 0 deletions vendor/github.com/expr-lang/expr/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions vendor/github.com/expr-lang/expr/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions vendor/github.com/expr-lang/expr/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading