Merge remote-tracking branch 'remotes/from/ce/main'
Some checks are pending
build / setup (push) Waiting to run
build / Check ce/* Pull Requests (push) Blocked by required conditions
build / ui (push) Blocked by required conditions
build / artifacts-ce (push) Blocked by required conditions
build / artifacts-ent (push) Blocked by required conditions
build / hcp-image (push) Blocked by required conditions
build / test (push) Blocked by required conditions
build / test-hcp-image (push) Blocked by required conditions
build / completed-successfully (push) Blocked by required conditions
CI / setup (push) Waiting to run
CI / Run Autopilot upgrade tool (push) Blocked by required conditions
CI / Run Go tests (push) Blocked by required conditions
CI / Run Go tests tagged with testonly (push) Blocked by required conditions
CI / Run Go tests with data race detection (push) Blocked by required conditions
CI / Run Go tests with FIPS configuration (push) Blocked by required conditions
CI / Test UI (push) Blocked by required conditions
CI / tests-completed (push) Blocked by required conditions
Run linters / Setup (push) Waiting to run
Run linters / Deprecated functions (push) Blocked by required conditions
Run linters / Code checks (push) Blocked by required conditions
Run linters / Protobuf generate delta (push) Blocked by required conditions
Run linters / Format (push) Blocked by required conditions
Run linters / Semgrep (push) Waiting to run
Check Copywrite Headers / copywrite (push) Waiting to run
Security Scan / scan (push) Waiting to run

This commit is contained in:
hc-github-team-secure-vault-core 2026-02-16 17:13:29 +00:00
commit bc33fac9b1
3 changed files with 104 additions and 1 deletions

View file

@ -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 {

View file

@ -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
View file

@ -0,0 +1,3 @@
```release-note:bug
secrets/pki: fix panic during acme account creation with malformed protected field.
``