mirror of
https://github.com/go-acme/lego
synced 2026-03-14 14:35:48 +01:00
fix: check order identifiers difference between client and server (#2520)
This commit is contained in:
parent
c56d45486b
commit
1cee2efbdc
3 changed files with 154 additions and 0 deletions
27
acme/api/identifier.go
Normal file
27
acme/api/identifier.go
Normal 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
111
acme/api/identifier_test.go
Normal 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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue