mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-18 18:38:08 -05:00
* fix: panic on malformed protected field in veryfyEabPayload * clear comments * missed a } * add changelog * Update changelog/12260.txt * rename 12260.txt to _12260.txt * address lint issue --------- Co-authored-by: Deniz Onur Duzgun <59659739+dduzgun-security@users.noreply.github.com> Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
parent
64fdc0b877
commit
daa03407c0
3 changed files with 104 additions and 1 deletions
|
|
@ -190,7 +190,10 @@ func verifyEabPayload(acmeState *acmeState, ac *acmeContext, outer *jwsCtx, expe
|
|||
if !ok {
|
||||
return nil, fmt.Errorf("missing required field 'protected': %w", ErrMalformed)
|
||||
}
|
||||
jwkBase64 := rawProtectedBase64.(string)
|
||||
jwkBase64, ok := rawProtectedBase64.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to parse 'protected' field: %w", ErrMalformed)
|
||||
}
|
||||
|
||||
jwkBytes, err := base64.RawURLEncoding.DecodeString(jwkBase64)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,103 @@ func TestAcmeNonces(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestVerifyEabPayloadProtectedFieldValidation verifies that the verifyEabPayload function
|
||||
// properly validates and rejects malformed 'protected' fields in ACME External Account Binding
|
||||
// (EAB) payloads. It ensures that only string values are accepted and that various non-string
|
||||
// types (object, array, number, boolean) produce appropriate error messages.
|
||||
func TestVerifyEabPayloadProtectedFieldValidation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
payload map[string]interface{}
|
||||
expectError bool
|
||||
expectMessage string
|
||||
}{
|
||||
{
|
||||
name: "valid string protected field",
|
||||
payload: map[string]interface{}{
|
||||
"protected": "eyJhbGciOiAiSFMyNTYifQ",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "missing protected field",
|
||||
payload: map[string]interface{}{
|
||||
"payload": "test",
|
||||
"signature": "test",
|
||||
},
|
||||
expectError: true,
|
||||
expectMessage: "missing required field 'protected'",
|
||||
},
|
||||
{
|
||||
name: "protected field as object",
|
||||
payload: map[string]interface{}{
|
||||
"protected": map[string]interface{}{ // should be a string, not an object
|
||||
"alg": "HS256",
|
||||
},
|
||||
"payload": "test",
|
||||
"signature": "test",
|
||||
},
|
||||
expectError: true,
|
||||
expectMessage: "failed to parse 'protected' field",
|
||||
},
|
||||
{
|
||||
name: "protected field as array",
|
||||
payload: map[string]interface{}{
|
||||
"protected": []interface{}{"test"}, // should be a string, not an array
|
||||
"payload": "test",
|
||||
"signature": "test",
|
||||
},
|
||||
expectError: true,
|
||||
expectMessage: "failed to parse 'protected' field",
|
||||
},
|
||||
{
|
||||
name: "protected field as number",
|
||||
payload: map[string]interface{}{
|
||||
"protected": 12345, // should be a string, not a number
|
||||
"payload": "test",
|
||||
"signature": "test",
|
||||
},
|
||||
expectError: true,
|
||||
expectMessage: "failed to parse 'protected' field",
|
||||
},
|
||||
{
|
||||
name: "protected field as boolean",
|
||||
payload: map[string]interface{}{
|
||||
"protected": true, // should be a string, not a boolean
|
||||
"payload": "test",
|
||||
"signature": "test",
|
||||
},
|
||||
expectError: true,
|
||||
expectMessage: "failed to parse 'protected' field",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
acmeState := NewACMEState()
|
||||
acmeCtx := &acmeContext{}
|
||||
outerJws := &jwsCtx{}
|
||||
|
||||
_, err := verifyEabPayload(acmeState, acmeCtx, outerJws, "/new-account", tc.payload)
|
||||
|
||||
if tc.expectError {
|
||||
require.Error(t, err, "expected error but got none")
|
||||
require.Contains(t, err.Error(), tc.expectMessage, "error message does not match")
|
||||
require.NotEmpty(t, err.Error())
|
||||
} else {
|
||||
// For valid protected field, we expect an error later in the flow
|
||||
// (since we don't have real base64 data), but it should NOT be from parsing the protected field
|
||||
if err != nil {
|
||||
require.NotContains(t, err.Error(), "failed to parse 'protected' field",
|
||||
"should not fail at protected field parsing stage")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestErrorResponseNoSubproblems builds the http body that exists in the header of an ACME error response and checks
|
||||
// in a simple case that "type" and "detail" two fields on the body do exist, but that "subproblems" a field which is
|
||||
// optional, is omitted because it does not exist in this case (rather than being included with a value null which can
|
||||
|
|
|
|||
3
changelog/_12260.txt
Normal file
3
changelog/_12260.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
```release-note:bug
|
||||
secrets/pki: fix panic during acme account creation with malformed protected field.
|
||||
``
|
||||
Loading…
Reference in a new issue