fix: don't panic on managed policy json unmarshal failure

This commit is contained in:
Julian Sommer
2026-08-19 14:39:39 +10:00
parent 28330cb318
commit 6e1e2f3fbb
3 changed files with 59 additions and 7 deletions
+9 -7
View File
@@ -282,18 +282,20 @@ var awsManagedPolicyDocs = map[string]string{
}
// builtinManagedPolicyParsed is the parsed form of awsManagedPolicyDocs, built
// once at init. A malformed builtin document is a programming error and panics.
var builtinManagedPolicyParsed = func() map[string]PolicyDocument {
parsed := make(map[string]PolicyDocument, len(awsManagedPolicyDocs))
for arn, raw := range awsManagedPolicyDocs {
// once at init. NewIAMServiceImpl propagates any parsing failure.
var builtinManagedPolicyParsed, builtinManagedPolicyParseErr = parseBuiltinManagedPolicies(awsManagedPolicyDocs)
func parseBuiltinManagedPolicies(rawPolicies map[string]string) (map[string]PolicyDocument, error) {
parsed := make(map[string]PolicyDocument, len(rawPolicies))
for arn, raw := range rawPolicies {
var doc PolicyDocument
if err := json.Unmarshal([]byte(raw), &doc); err != nil {
panic(fmt.Sprintf("iam: malformed builtin managed policy %s: %v", arn, err))
return nil, fmt.Errorf("parse managed policy %s: %w", arn, err)
}
parsed[arn] = doc
}
return parsed
}()
return parsed, nil
}
// builtinManagedPolicyDoc returns the modeled grant document for an AWS-managed
// policy ARN. ok is false for managed ARNs Spinifex does not model.
@@ -1,6 +1,7 @@
package handlers_iam
import (
"errors"
"testing"
"github.com/mulgadc/bluebottle/pkg/iampolicy"
@@ -22,6 +23,52 @@ var rdsInternalActions = []string{
"rds:GetDBBootstrapConfig",
}
func TestParseBuiltinManagedPolicies(t *testing.T) {
tests := []struct {
name string
raw map[string]string
wantErr string
}{
{
name: "valid document",
raw: map[string]string{
"arn:valid": `{"Version":"2012-10-17","Statement":[]}`,
},
},
{
name: "malformed document",
raw: map[string]string{
"arn:malformed": `{`,
},
wantErr: "parse managed policy arn:malformed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parsed, err := parseBuiltinManagedPolicies(tt.raw)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
assert.Nil(t, parsed)
return
}
require.NoError(t, err)
assert.Len(t, parsed, len(tt.raw))
})
}
}
func TestNewIAMServiceImpl_PropagatesBuiltinManagedPolicyParseError(t *testing.T) {
parseErr := errors.New("malformed builtin policy")
previousErr := builtinManagedPolicyParseErr
builtinManagedPolicyParseErr = parseErr
t.Cleanup(func() { builtinManagedPolicyParseErr = previousErr })
_, err := NewIAMServiceImpl(t.Context(), nil, make([]byte, 32), 1)
require.ErrorIs(t, err, parseErr)
assert.ErrorContains(t, err, "init builtin managed policies")
}
// A role whose permissions come entirely from an attached AWS-managed ARN has to
// resolve to a grant, the way the stock EKS roles already do, rather than fail
// closed on a document Spinifex never stores.
+3
View File
@@ -112,6 +112,9 @@ var _ IAMService = (*IAMServiceImpl)(nil)
// clusterSize sets the replication factor; pass 1 for single-node or test setups.
// The context bounds bucket creation and the schema migrations only.
func NewIAMServiceImpl(ctx context.Context, natsConn *nats.Conn, masterKey []byte, clusterSize int) (*IAMServiceImpl, error) {
if builtinManagedPolicyParseErr != nil {
return nil, fmt.Errorf("init builtin managed policies: %w", builtinManagedPolicyParseErr)
}
if len(masterKey) != 32 {
return nil, fmt.Errorf("master key must be 32 bytes, got %d", len(masterKey))
}