Skip to content

Commit 767e386

Browse files
caod123denyeart
authored andcommitted
[FAB-15845] Additional validation checks
- refactor chaincode name and version regexp - user chaincode name cannot be same as system cc - no duplicate namespace in RWSet Change-Id: Id81133244cfc62888227bd4799f520d29211a7f7 Signed-off-by: Danny Cao <dcao@us.ibm.com> Signed-off-by: Wenjian Qiao <wenjianq@gmail.com>
1 parent 73cd32c commit 767e386

File tree

11 files changed

+485
-57
lines changed

11 files changed

+485
-57
lines changed

core/committer/txvalidator/v14/validator_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import (
4343
"github.com/hyperledger/fabric/msp/mgmt"
4444
msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools"
4545
"github.com/hyperledger/fabric/protos/common"
46+
"github.com/hyperledger/fabric/protos/ledger/rwset"
47+
"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
4648
mb "github.com/hyperledger/fabric/protos/msp"
4749
"github.com/hyperledger/fabric/protos/peer"
4850
pb "github.com/hyperledger/fabric/protos/peer"
@@ -403,6 +405,64 @@ func testInvokeOK(t *testing.T, l ledger.PeerLedger, v txvalidator.Validator) {
403405
assertValid(b, t)
404406
}
405407

408+
func TestInvokeNOKDuplicateNs(t *testing.T) {
409+
t.Run("1.2Capability", func(t *testing.T) {
410+
l, v, cleanup := setupLedgerAndValidatorWithV12Capabilities(t)
411+
defer cleanup()
412+
413+
testInvokeNOKDuplicateNs(t, l, v)
414+
})
415+
416+
t.Run("1.3Capability", func(t *testing.T) {
417+
l, v, cleanup := setupLedgerAndValidatorWithV13Capabilities(t)
418+
defer cleanup()
419+
420+
testInvokeNOKDuplicateNs(t, l, v)
421+
})
422+
}
423+
424+
func testInvokeNOKDuplicateNs(t *testing.T, l ledger.PeerLedger, v txvalidator.Validator) {
425+
ccID := "mycc"
426+
427+
putCCInfo(l, ccID, signedByAnyMember([]string{"SampleOrg"}), t)
428+
429+
// note that this read-write set has two read-write sets for the same namespace and key
430+
txrws := &rwset.TxReadWriteSet{
431+
DataModel: rwset.TxReadWriteSet_KV,
432+
NsRwset: []*rwset.NsReadWriteSet{
433+
{
434+
Namespace: "mycc",
435+
Rwset: protoutil.MarshalOrPanic(&kvrwset.KVRWSet{
436+
Writes: []*kvrwset.KVWrite{
437+
{
438+
Key: "foo",
439+
Value: []byte("bar1"),
440+
},
441+
},
442+
}),
443+
},
444+
{
445+
Namespace: "mycc",
446+
Rwset: protoutil.MarshalOrPanic(&kvrwset.KVRWSet{
447+
Writes: []*kvrwset.KVWrite{
448+
{
449+
Key: "foo",
450+
Value: []byte("bar2"),
451+
},
452+
},
453+
}),
454+
},
455+
},
456+
}
457+
458+
tx := getEnv(ccID, nil, protoutil.MarshalOrPanic(txrws), t)
459+
b := &common.Block{Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(tx)}}, Header: &common.BlockHeader{Number: 2}}
460+
461+
err := v.Validate(b)
462+
assert.NoError(t, err)
463+
assertInvalid(b, t, peer.TxValidationCode_ILLEGAL_WRITESET)
464+
}
465+
406466
func TestInvokeNoRWSet(t *testing.T) {
407467
plugin := &mocks.Plugin{}
408468
plugin.On("Init", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)

core/committer/txvalidator/v14/vscc_validator.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,15 @@ func (v *VsccValidatorImpl) VSCCValidateTx(seq int, payload *common.Payload, env
118118
}
119119
}
120120

121+
namespaces := make(map[string]struct{})
121122
for _, ns := range txRWSet.NsRwSets {
123+
// check to make sure there is no duplicate namespace in txRWSet
124+
if _, ok := namespaces[ns.NameSpace]; ok {
125+
return errors.Errorf("duplicate namespace '%s' in txRWSet", ns.NameSpace),
126+
peer.TxValidationCode_ILLEGAL_WRITESET
127+
}
128+
namespaces[ns.NameSpace] = struct{}{}
129+
122130
if !v.txWritesToNamespace(ns) {
123131
continue
124132
}

core/committer/txvalidator/v20/plugindispatcher/dispatcher.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,16 @@ func (v *dispatcherImpl) Dispatch(seq int, payload *common.Payload, envBytes []b
168168
}
169169
}
170170

171+
namespaces := make(map[string]struct{})
171172
for _, ns := range txRWSet.NsRwSets {
173+
// check to make sure there is no duplicate namespace in txRWSet
174+
if _, ok := namespaces[ns.NameSpace]; ok {
175+
logger.Errorf("duplicate namespace '%s' in txRWSet", ns.NameSpace)
176+
return errors.Errorf("duplicate namespace '%s' in txRWSet", ns.NameSpace),
177+
peer.TxValidationCode_ILLEGAL_WRITESET
178+
}
179+
namespaces[ns.NameSpace] = struct{}{}
180+
172181
if v.txWritesToNamespace(ns) {
173182
wrNamespace[ns.NameSpace] = true
174183
}

core/committer/txvalidator/v20/validator_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import (
3939
"github.com/hyperledger/fabric/msp/mgmt"
4040
msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools"
4141
"github.com/hyperledger/fabric/protos/common"
42+
"github.com/hyperledger/fabric/protos/ledger/rwset"
43+
"github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset"
4244
protosmsp "github.com/hyperledger/fabric/protos/msp"
4345
"github.com/hyperledger/fabric/protos/peer"
4446
protospeer "github.com/hyperledger/fabric/protos/peer"
@@ -310,6 +312,56 @@ func TestInvokeOK(t *testing.T) {
310312
assertValid(b, t)
311313
}
312314

315+
func TestInvokeNOKDuplicateNs(t *testing.T) {
316+
ccID := "mycc"
317+
318+
v, mockQE, _, _ := setupValidator()
319+
320+
mockQE.On("GetState", "lscc", ccID).Return(protoutil.MarshalOrPanic(&ccp.ChaincodeData{
321+
Name: ccID,
322+
Version: ccVersion,
323+
Vscc: "vscc",
324+
Policy: signedByAnyMember([]string{"SampleOrg"}),
325+
}), nil)
326+
mockQE.On("GetStateMetadata", ccID, "key").Return(nil, nil)
327+
328+
// note that this read-write set has two read-write sets for the same namespace and key
329+
txrws := &rwset.TxReadWriteSet{
330+
DataModel: rwset.TxReadWriteSet_KV,
331+
NsRwset: []*rwset.NsReadWriteSet{
332+
{
333+
Namespace: "mycc",
334+
Rwset: protoutil.MarshalOrPanic(&kvrwset.KVRWSet{
335+
Writes: []*kvrwset.KVWrite{
336+
{
337+
Key: "foo",
338+
Value: []byte("bar1"),
339+
},
340+
},
341+
}),
342+
},
343+
{
344+
Namespace: "mycc",
345+
Rwset: protoutil.MarshalOrPanic(&kvrwset.KVRWSet{
346+
Writes: []*kvrwset.KVWrite{
347+
{
348+
Key: "foo",
349+
Value: []byte("bar2"),
350+
},
351+
},
352+
}),
353+
},
354+
},
355+
}
356+
357+
tx := getEnv(ccID, nil, protoutil.MarshalOrPanic(txrws), t)
358+
b := &common.Block{Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(tx)}}, Header: &common.BlockHeader{Number: 2}}
359+
360+
err := v.Validate(b)
361+
assert.NoError(t, err)
362+
assertInvalid(b, t, peer.TxValidationCode_ILLEGAL_WRITESET)
363+
}
364+
313365
func TestInvokeNoRWSet(t *testing.T) {
314366
ccID := "mycc"
315367

core/handlers/validation/builtin/v12/validation_logic.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,19 @@ import (
3535
"github.com/pkg/errors"
3636
)
3737

38-
var logger = flogging.MustGetLogger("vscc")
38+
var (
39+
logger = flogging.MustGetLogger("vscc")
40+
41+
// currently defined system chaincode names that shouldn't
42+
// be allowed as user-defined chaincode names
43+
systemChaincodeNames = map[string]struct{}{
44+
"cscc": {},
45+
"escc": {},
46+
"lscc": {},
47+
"qscc": {},
48+
"vscc": {},
49+
}
50+
)
3951

4052
const (
4153
DUPLICATED_IDENTITY_ERROR = "Endorsement policy evaluation failure might be caused by duplicated identities"
@@ -524,6 +536,11 @@ func (vscc *Validator) ValidateLSCCInvocation(
524536
return policyErr(fmt.Errorf("GetChaincodeDeploymentSpec error %s", err))
525537
}
526538

539+
if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil ||
540+
cap.Action == nil || cap.Action.ProposalResponsePayload == nil {
541+
return policyErr(fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc))
542+
}
543+
527544
err = packaging.NewRegistry(
528545
// XXX We should definitely _not_ have this external dependency in VSCC
529546
// as adding a platform could cause non-determinism. This is yet another
@@ -538,9 +555,22 @@ func (vscc *Validator) ValidateLSCCInvocation(
538555
return policyErr(fmt.Errorf("failed to validate deployment spec: %s", err))
539556
}
540557

541-
if cdsArgs == nil || cdsArgs.ChaincodeSpec == nil || cdsArgs.ChaincodeSpec.ChaincodeId == nil ||
542-
cap.Action == nil || cap.Action.ProposalResponsePayload == nil {
543-
return policyErr(fmt.Errorf("VSCC error: invocation of lscc(%s) does not have appropriate arguments", lsccFunc))
558+
// validate chaincode name
559+
ccName := cdsArgs.ChaincodeSpec.ChaincodeId.Name
560+
// it must comply with the lscc.ChaincodeNameRegExp
561+
if !lscc.ChaincodeNameRegExp.MatchString(ccName) {
562+
return policyErr(errors.Errorf("invalid chaincode name '%s'", ccName))
563+
}
564+
// it can't match the name of one of the system chaincodes
565+
if _, in := systemChaincodeNames[ccName]; in {
566+
return policyErr(errors.Errorf("chaincode name '%s' is reserved for system chaincodes", ccName))
567+
}
568+
569+
// validate chaincode version
570+
ccVersion := cdsArgs.ChaincodeSpec.ChaincodeId.Version
571+
// it must comply with the lscc.ChaincodeVersionRegExp
572+
if !lscc.ChaincodeVersionRegExp.MatchString(ccVersion) {
573+
return policyErr(errors.Errorf("invalid chaincode version '%s'", ccVersion))
544574
}
545575

546576
// get the rwset

core/handlers/validation/builtin/v12/validation_logic_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,66 @@ func TestValidateDeployNoLedger(t *testing.T) {
889889
assert.Error(t, err)
890890
}
891891

892+
func TestValidateDeployNOKNilChaincodeSpec(t *testing.T) {
893+
state := make(map[string]map[string][]byte)
894+
mp := (&scc.MocksccProviderFactory{
895+
Qe: lm.NewMockQueryExecutor(state),
896+
ApplicationConfigBool: true,
897+
ApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
898+
}).NewSystemChaincodeProvider().(*scc.MocksccProviderImpl)
899+
900+
v := newValidationInstance(state)
901+
902+
mockAclProvider := &aclmocks.MockACLProvider{}
903+
lccc := lscc.New(mp, mockAclProvider, mockMSPIDGetter, &mockPolicyChecker{})
904+
stublccc := shimtest.NewMockStub("lscc", lccc)
905+
state["lscc"] = stublccc.State
906+
907+
ccname := "mycc"
908+
ccver := "1"
909+
910+
defaultPolicy, err := getSignedByMSPAdminPolicy(mspid)
911+
assert.NoError(t, err)
912+
res, err := createCCDataRWset(ccname, ccname, ccver, defaultPolicy)
913+
assert.NoError(t, err)
914+
915+
// Create a ChaincodeDeploymentSpec with nil ChaincodeSpec for negative test
916+
cdsBytes, err := proto.Marshal(&peer.ChaincodeDeploymentSpec{})
917+
assert.NoError(t, err)
918+
919+
// ChaincodeDeploymentSpec/ChaincodeSpec are derived from cdsBytes (i.e., cis.ChaincodeSpec.Input.Args[2])
920+
cis := &peer.ChaincodeInvocationSpec{
921+
ChaincodeSpec: &peer.ChaincodeSpec{
922+
ChaincodeId: &peer.ChaincodeID{Name: "lscc"},
923+
Input: &peer.ChaincodeInput{
924+
Args: [][]byte{[]byte(lscc.DEPLOY), []byte("barf"), cdsBytes},
925+
},
926+
Type: peer.ChaincodeSpec_GOLANG,
927+
},
928+
}
929+
930+
prop, _, err := protoutil.CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, util.GetTestChainID(), cis, sid)
931+
assert.NoError(t, err)
932+
933+
ccid := &peer.ChaincodeID{Name: ccname, Version: ccver}
934+
935+
presp, err := protoutil.CreateProposalResponse(prop.Header, prop.Payload, &peer.Response{Status: 200}, res, nil, ccid, nil, id)
936+
assert.NoError(t, err)
937+
938+
env, err := protoutil.CreateSignedTx(prop, id, presp)
939+
assert.NoError(t, err)
940+
941+
// good path: signed by the right MSP
942+
policy, err := getSignedByMSPMemberPolicy(mspid)
943+
if err != nil {
944+
t.Fatalf("failed getting policy, err %s", err)
945+
}
946+
947+
b := &common.Block{Data: &common.BlockData{Data: [][]byte{protoutil.MarshalOrPanic(env)}}, Header: &common.BlockHeader{}}
948+
err = v.Validate(b, "lscc", 0, 0, policy)
949+
assert.EqualError(t, err, "VSCC error: invocation of lscc(deploy) does not have appropriate arguments")
950+
}
951+
892952
func TestValidateDeployOK(t *testing.T) {
893953
state := make(map[string]map[string][]byte)
894954
mp := (&scc.MocksccProviderFactory{
@@ -933,6 +993,65 @@ func TestValidateDeployOK(t *testing.T) {
933993
assert.NoError(t, err)
934994
}
935995

996+
func TestValidateDeployNOK(t *testing.T) {
997+
var testCases = []struct {
998+
description string
999+
ccName string
1000+
ccVersion string
1001+
errMsg string
1002+
}{
1003+
{description: "empty cc name", ccName: "", ccVersion: "1", errMsg: "invalid chaincode name ''"},
1004+
{description: "bad first character in cc name", ccName: "_badname", ccVersion: "1.2", errMsg: "invalid chaincode name '_badname'"},
1005+
{description: "bad character in cc name", ccName: "bad.name", ccVersion: "1-5", errMsg: "invalid chaincode name 'bad.name'"},
1006+
{description: "empty cc version", ccName: "1good_name", ccVersion: "", errMsg: "invalid chaincode version ''"},
1007+
{description: "bad cc version", ccName: "good-name", ccVersion: "$badversion", errMsg: "invalid chaincode version '$badversion'"},
1008+
{description: "use system cc name", ccName: "qscc", ccVersion: "2.1", errMsg: "chaincode name 'qscc' is reserved for system chaincodes"},
1009+
}
1010+
1011+
// create validator and policy
1012+
state := make(map[string]map[string][]byte)
1013+
mp := (&scc.MocksccProviderFactory{
1014+
Qe: lm.NewMockQueryExecutor(state),
1015+
ApplicationConfigBool: true,
1016+
ApplicationConfigRv: &mc.MockApplication{CapabilitiesRv: &mc.MockApplicationCapabilities{}},
1017+
}).NewSystemChaincodeProvider().(*scc.MocksccProviderImpl)
1018+
1019+
v := newValidationInstance(state)
1020+
1021+
mockAclProvider := &aclmocks.MockACLProvider{}
1022+
lccc := lscc.New(mp, mockAclProvider, mockMSPIDGetter, &mockPolicyChecker{})
1023+
stublccc := shimtest.NewMockStub("lscc", lccc)
1024+
state["lscc"] = stublccc.State
1025+
1026+
policy, err := getSignedByMSPAdminPolicy(mspid)
1027+
assert.NoError(t, err)
1028+
1029+
for _, tc := range testCases {
1030+
t.Run(tc.description, func(t *testing.T) {
1031+
testChaincodeDeployNOK(t, tc.ccName, tc.ccVersion, tc.errMsg, v, policy)
1032+
})
1033+
}
1034+
}
1035+
1036+
func testChaincodeDeployNOK(t *testing.T, ccName, ccVersion, errMsg string, v *Validator, policy []byte) {
1037+
res, err := createCCDataRWset(ccName, ccName, ccVersion, policy)
1038+
assert.NoError(t, err)
1039+
1040+
tx, err := createLSCCTx(ccName, ccVersion, lscc.DEPLOY, res)
1041+
if err != nil {
1042+
t.Fatalf("createTx returned err %s", err)
1043+
}
1044+
1045+
envBytes, err := protoutil.GetBytesEnvelope(tx)
1046+
if err != nil {
1047+
t.Fatalf("GetBytesEnvelope returned err %s", err)
1048+
}
1049+
1050+
b := &common.Block{Data: &common.BlockData{Data: [][]byte{envBytes}}, Header: &common.BlockHeader{}}
1051+
err = v.Validate(b, "lscc", 0, 0, policy)
1052+
assert.EqualError(t, err, errMsg)
1053+
}
1054+
9361055
func TestValidateDeployWithCollection(t *testing.T) {
9371056
state := make(map[string]map[string][]byte)
9381057
mp := (&scc.MocksccProviderFactory{

0 commit comments

Comments
 (0)