diff --git a/internal/guard/wasm_payload.go b/internal/guard/wasm_payload.go index 1c448f40..50dd3555 100644 --- a/internal/guard/wasm_payload.go +++ b/internal/guard/wasm_payload.go @@ -92,8 +92,10 @@ func buildStrictLabelAgentPayload(policy interface{}) (map[string]interface{}, e reposRaw, hasRepos := allowOnly["repos"] integrityRaw, hasIntegrity := allowOnly["min-integrity"] + integrityFieldName := "min-integrity" if !hasIntegrity { integrityRaw, hasIntegrity = allowOnly["integrity"] + integrityFieldName = "integrity" } if !hasRepos || !hasIntegrity { return nil, fmt.Errorf("invalid guard policy transport shape: missing required fields repos and/or min-integrity in allow-only object") @@ -114,15 +116,8 @@ func buildStrictLabelAgentPayload(policy interface{}) (map[string]interface{}, e return nil, fmt.Errorf("invalid repos value: expected all, public, or non-empty array of scoped strings") } - integrity, ok := integrityRaw.(string) - if !ok { - return nil, fmt.Errorf("invalid integrity value: expected one of none|unapproved|approved|merged") - } - - switch strings.ToLower(strings.TrimSpace(integrity)) { - case "none", "unapproved", "approved", "merged": - default: - return nil, fmt.Errorf("invalid integrity value: expected one of none|unapproved|approved|merged") + if err := validateIntegrityField(integrityFieldName, integrityRaw); err != nil { + return nil, err } // Validate blocked-users if present: must be a non-empty array of non-empty strings. @@ -199,27 +194,15 @@ func buildStrictLabelAgentPayload(policy interface{}) (map[string]interface{}, e // Validate disapproval-integrity if present. if disIntRaw, ok := allowOnly["disapproval-integrity"]; ok { - disInt, ok := disIntRaw.(string) - if !ok { - return nil, fmt.Errorf("invalid disapproval-integrity value: expected one of none|unapproved|approved|merged") - } - switch strings.ToLower(strings.TrimSpace(disInt)) { - case "none", "unapproved", "approved", "merged": - default: - return nil, fmt.Errorf("invalid disapproval-integrity value: expected one of none|unapproved|approved|merged") + if err := validateIntegrityField("disapproval-integrity", disIntRaw); err != nil { + return nil, err } } // Validate endorser-min-integrity if present. if endMinRaw, ok := allowOnly["endorser-min-integrity"]; ok { - endMin, ok := endMinRaw.(string) - if !ok { - return nil, fmt.Errorf("invalid endorser-min-integrity value: expected one of none|unapproved|approved|merged") - } - switch strings.ToLower(strings.TrimSpace(endMin)) { - case "none", "unapproved", "approved", "merged": - default: - return nil, fmt.Errorf("invalid endorser-min-integrity value: expected one of none|unapproved|approved|merged") + if err := validateIntegrityField("endorser-min-integrity", endMinRaw); err != nil { + return nil, err } } diff --git a/internal/guard/wasm_test.go b/internal/guard/wasm_test.go index 776501fe..716e50d4 100644 --- a/internal/guard/wasm_test.go +++ b/internal/guard/wasm_test.go @@ -358,7 +358,7 @@ func TestBuildStrictLabelAgentPayloadExtended(t *testing.T) { _, err := buildStrictLabelAgentPayload(policy) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid integrity value") + assert.Contains(t, err.Error(), "invalid min-integrity value") }) t.Run("valid allow-only policy succeeds", func(t *testing.T) { diff --git a/internal/guard/wasm_validate.go b/internal/guard/wasm_validate.go new file mode 100644 index 00000000..4db27ae6 --- /dev/null +++ b/internal/guard/wasm_validate.go @@ -0,0 +1,38 @@ +package guard + +import ( + "fmt" + "strings" +) + +// allowedIntegrityLevels is the single source of truth for valid integrity-level values. +var allowedIntegrityLevels = []string{"none", "unapproved", "approved", "merged"} + +var allowedIntegrityLevelSet = map[string]struct{}{ + "none": {}, + "unapproved": {}, + "approved": {}, + "merged": {}, +} + +func invalidIntegrityFieldError(fieldName string) error { + return fmt.Errorf( + "invalid %s value: expected one of %s", + fieldName, + strings.Join(allowedIntegrityLevels, "|"), + ) +} + +// validateIntegrityField returns an error if raw is not a valid integrity-level +// string. fieldName is used in the error message (e.g. "disapproval-integrity"). +func validateIntegrityField(fieldName string, raw interface{}) error { + s, ok := raw.(string) + if !ok { + return invalidIntegrityFieldError(fieldName) + } + normalized := strings.ToLower(strings.TrimSpace(s)) + if _, ok := allowedIntegrityLevelSet[normalized]; ok { + return nil + } + return invalidIntegrityFieldError(fieldName) +}