diff --git a/modules/core/02-client/types/keys.go b/modules/core/02-client/types/keys.go index 74597232d7c..919f91f9ebe 100644 --- a/modules/core/02-client/types/keys.go +++ b/modules/core/02-client/types/keys.go @@ -34,7 +34,9 @@ func FormatClientIdentifier(clientType string, sequence uint64) string { // IsClientIDFormat checks if a clientID is in the format required on the SDK for // parsing client identifiers. The client identifier must be in the form: `{client-type}-{N} -var IsClientIDFormat = regexp.MustCompile(`^.*[^\n-]-[0-9]{1,20}$`).MatchString +// which per the specification only permits ASCII for the {client-type} segment and +// 1 to 20 digits for the {N} segment. +var IsClientIDFormat = regexp.MustCompile(`^\w+([\w-]+\w)?-[0-9]{1,20}$`).MatchString // IsValidClientID checks if the clientID is valid and can be parsed into the client // identifier format. diff --git a/modules/core/02-client/types/keys_test.go b/modules/core/02-client/types/keys_test.go index f0e5fcd02b7..0da1b4c3e4b 100644 --- a/modules/core/02-client/types/keys_test.go +++ b/modules/core/02-client/types/keys_test.go @@ -35,22 +35,31 @@ func TestParseClientIdentifier(t *testing.T) { {"negative sequence", "tendermint--1", "tendermint", 0, false}, {"invalid format", "tendermint-tm", "tendermint", 0, false}, {"empty clientype", " -100", "tendermint", 0, false}, + {"with in the middle tabs", "a\t\t\t-100", "tendermint", 0, false}, + {"leading tabs", "\t\t\ta-100", "tendermint", 0, false}, + {"with whitespace", " a-100", "tendermint", 0, false}, + {"leading hyphens", "-----a-100", "tendermint", 0, false}, + {"with slash", "tendermint/-100", "tendermint", 0, false}, + {"non-ASCII:: emoji", "🚨😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎😎-100", "tendermint", 0, false}, + {"non-ASCII:: others", "δΈ–η•Œ-100", "tendermint", 0, false}, } for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + clientType, seq, err := types.ParseClientIdentifier(tc.clientID) + valid := types.IsValidClientID(tc.clientID) + require.Equal(t, tc.expSeq, seq, tc.clientID) - clientType, seq, err := types.ParseClientIdentifier(tc.clientID) - valid := types.IsValidClientID(tc.clientID) - require.Equal(t, tc.expSeq, seq, tc.clientID) - - if tc.expPass { - require.NoError(t, err, tc.name) - require.True(t, valid) - require.Equal(t, tc.clientType, clientType) - } else { - require.Error(t, err, tc.name, tc.clientID) - require.False(t, valid) - require.Equal(t, "", clientType) - } + if tc.expPass { + require.NoError(t, err, tc.name) + require.True(t, valid) + require.Equal(t, tc.clientType, clientType) + } else { + require.Error(t, err, tc.name, tc.clientID) + require.False(t, valid) + require.Equal(t, "", clientType) + } + }) } }