fix: check order identifiers difference between client and server (#2520)

This commit is contained in:
Ludovic Fernandez 2025-04-29 13:56:59 +02:00 committed by GitHub
commit 1cee2efbdc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 154 additions and 0 deletions

27
acme/api/identifier.go Normal file
View file

@ -0,0 +1,27 @@
package api
import (
"cmp"
"slices"
"github.com/go-acme/lego/v4/acme"
)
// compareIdentifiers compares 2 slices of [acme.Identifier].
func compareIdentifiers(a, b []acme.Identifier) int {
// Clones slices to avoid modifying original slices.
right := slices.Clone(a)
left := slices.Clone(b)
slices.SortStableFunc(right, compareIdentifier)
slices.SortStableFunc(left, compareIdentifier)
return slices.CompareFunc(right, left, compareIdentifier)
}
func compareIdentifier(right, left acme.Identifier) int {
return cmp.Or(
cmp.Compare(right.Type, left.Type),
cmp.Compare(right.Value, left.Value),
)
}

111
acme/api/identifier_test.go Normal file
View file

@ -0,0 +1,111 @@
package api
import (
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/stretchr/testify/assert"
)
func Test_compareIdentifiers(t *testing.T) {
testCases := []struct {
desc string
a, b []acme.Identifier
expected int
}{
{
desc: "identical identifiers",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
expected: 0,
},
{
desc: "identical identifiers but different order",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "*.example.com"},
{Type: "dns", Value: "example.com"},
},
expected: 0,
},
{
desc: "duplicate identifiers",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "example.com"},
},
expected: -1,
},
{
desc: "different identifier values",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.org"},
},
expected: -1,
},
{
desc: "different identifier types",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "ip", Value: "*.example.com"},
},
expected: -1,
},
{
desc: "different number of identifiers a>b",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
{Type: "dns", Value: "example.org"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
expected: 1,
},
{
desc: "different number of identifiers b>a",
a: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
},
b: []acme.Identifier{
{Type: "dns", Value: "example.com"},
{Type: "dns", Value: "*.example.com"},
{Type: "dns", Value: "example.org"},
},
expected: -1,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.expected, compareIdentifiers(test.a, test.b))
})
}
}

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"slices"
"time"
"github.com/go-acme/lego/v4/acme"
@ -85,6 +86,21 @@ func (o *OrderService) NewWithOptions(domains []string, opts *OrderOptions) (acm
}
}
// The elements of the "authorizations" and "identifiers" arrays are immutable once set.
// The server MUST NOT change the contents of either array after they are created.
// If a client observes a change in the contents of either array,
// then it SHOULD consider the order invalid.
// https://www.rfc-editor.org/rfc/rfc8555#section-7.1.3
if compareIdentifiers(orderReq.Identifiers, order.Identifiers) != 0 {
// Sorts identifiers to avoid error message ambiguities about the order of the identifiers.
slices.SortStableFunc(orderReq.Identifiers, compareIdentifier)
slices.SortStableFunc(order.Identifiers, compareIdentifier)
return acme.ExtendedOrder{},
fmt.Errorf("order identifiers have been by the ACME server (RFC8555 §7.1.3): %+v != %+v",
orderReq.Identifiers, order.Identifiers)
}
return acme.ExtendedOrder{
Order: order,
Location: resp.Header.Get("Location"),