Skip to content

Commit f7873db

Browse files
authored
fix: Rejecting duplicate component identifier as required by the RFC (#119)
1 parent aeed528 commit f7873db

2 files changed

Lines changed: 103 additions & 13 deletions

File tree

signature_parameters.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,20 @@ func (p *signatureParameters) toSignatureBase(msg *Message) ([]byte, error) {
145145
}
146146

147147
sigBase := make(componentList, 0, len(p.identifiers))
148+
seen := make(map[string]struct{}, len(p.identifiers))
148149

149150
for _, ci := range p.identifiers {
151+
identifier, err := httpsfv.Marshal(ci.Item)
152+
if err != nil {
153+
return nil, err
154+
}
155+
156+
if _, ok := seen[identifier]; ok {
157+
return nil, fmt.Errorf("%w: duplicate component identifier %s", ErrMalformedData, identifier)
158+
}
159+
160+
seen[identifier] = struct{}{}
161+
150162
comp, err := ci.createComponent(msg)
151163
if err != nil {
152164
return nil, err

signature_parameters_test.go

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ func TestSignatureParametersToSignatureBase(t *testing.T) {
488488
// test is based on the non-normative example from
489489
// https://www.rfc-editor.org/rfc/rfc9421.html#name-creating-the-signature-base
490490

491-
reqURL, err := url.Parse("http://example.com/foo?param=Value&Pet=dog")
491+
reqURL, err := url.Parse("http://example.com/foo?param=Value&Pet=dog&Cat=meow")
492492
require.NoError(t, err)
493493

494494
msg := &Message{
@@ -506,27 +506,105 @@ func TestSignatureParametersToSignatureBase(t *testing.T) {
506506
IsRequest: true,
507507
}
508508

509-
expectedSigBase := `"@method": POST
509+
keyID := "test-key-rsa-pss"
510+
created, err := time.Parse(time.RFC1123, "Tue, 20 Apr 2021 02:07:53 GMT")
511+
require.NoError(t, err)
512+
513+
for _, tc := range []struct {
514+
uc string
515+
components []string
516+
assert func(t *testing.T, err error, sigBase []byte)
517+
}{
518+
{
519+
uc: "successful signature base creation",
520+
components: []string{
521+
"@method", "@authority", "@path", "content-digest", "content-length", "content-type",
522+
},
523+
assert: func(t *testing.T, err error, sigBase []byte) {
524+
t.Helper()
525+
526+
require.NoError(t, err)
527+
528+
expected := `"@method": POST
510529
"@authority": example.com
511530
"@path": /foo
512531
"content-digest": sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:
513532
"content-length": 18
514533
"content-type": application/json
515534
"@signature-params": ("@method" "@authority" "@path" "content-digest" "content-length" "content-type");created=1618884473;keyid="test-key-rsa-pss"`
516535

517-
identifiers, err := toComponentIdentifiers([]string{
518-
"@method", "@authority", "@path", "content-digest", "content-length", "content-type",
519-
})
520-
require.NoError(t, err)
536+
assert.Equal(t, expected, string(sigBase))
537+
},
538+
},
539+
{
540+
uc: "failing for duplicate component identifier",
541+
components: []string{
542+
"@method", "@method",
543+
},
544+
assert: func(t *testing.T, err error, sigBase []byte) {
545+
t.Helper()
521546

522-
keyID := "test-key-rsa-pss"
523-
created, err := time.Parse(time.RFC1123, "Tue, 20 Apr 2021 02:07:53 GMT")
524-
require.NoError(t, err)
547+
require.Error(t, err)
548+
require.ErrorIs(t, err, ErrMalformedData)
549+
require.ErrorContains(t, err, "duplicate component identifier")
550+
assert.Nil(t, sigBase)
551+
},
552+
},
553+
{
554+
uc: "failing for duplicate parameterized component identifier",
555+
components: []string{
556+
`@query-param;name="Pet"`,
557+
`@query-param;name="Pet"`,
558+
},
559+
assert: func(t *testing.T, err error, sigBase []byte) {
560+
t.Helper()
525561

526-
params := newSignatureParameters(created, time.Time{}, "", keyID, "", "", identifiers)
562+
require.Error(t, err)
563+
require.ErrorIs(t, err, ErrMalformedData)
564+
require.ErrorContains(t, err, "duplicate component identifier")
565+
assert.Nil(t, sigBase)
566+
},
567+
},
568+
{
569+
uc: "failing for unknown component identifier",
570+
components: []string{"@status"},
571+
assert: func(t *testing.T, err error, sigBase []byte) {
572+
t.Helper()
527573

528-
sigBase, err := params.toSignatureBase(msg)
529-
require.NoError(t, err)
574+
require.Error(t, err)
575+
require.ErrorIs(t, err, ErrCanonicalization)
576+
require.ErrorContains(t, err, "@status")
577+
assert.Nil(t, sigBase)
578+
},
579+
},
580+
{
581+
uc: "allowing same component identifier with different parameters",
582+
components: []string{
583+
`@query-param;name="Pet"`,
584+
`@query-param;name="Cat"`,
585+
},
586+
assert: func(t *testing.T, err error, sigBase []byte) {
587+
t.Helper()
530588

531-
assert.Equal(t, expectedSigBase, string(sigBase))
589+
require.NoError(t, err)
590+
591+
expected := `"@query-param";name="Pet": dog
592+
"@query-param";name="Cat": meow
593+
"@signature-params": ("@query-param";name="Pet" "@query-param";name="Cat");created=1618884473;keyid="test-key-rsa-pss"`
594+
595+
assert.Equal(t, expected, string(sigBase))
596+
},
597+
},
598+
} {
599+
t.Run(tc.uc, func(t *testing.T) {
600+
identifiers, err := toComponentIdentifiers(tc.components)
601+
require.NoError(t, err)
602+
603+
params := newSignatureParameters(created, time.Time{}, "", keyID, "", "", identifiers)
604+
605+
sigBase, err := params.toSignatureBase(msg)
606+
607+
tc.assert(t, err, sigBase)
608+
})
609+
}
532610
}

0 commit comments

Comments
 (0)