Skip to content

Commit d1d6b27

Browse files
committed
Reject nil constructor union branch values
1 parent e68fa55 commit d1d6b27

11 files changed

Lines changed: 108 additions & 3 deletions

codegen/templates/validation/union_sum.go.tpl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ switch string({{ .target }}.Kind()) {
22
{{- range .cases }}
33
case {{ printf "%q" .typeTag }}:
44
actual, _ := {{ $.target }}.As{{ .fieldName }}()
5+
{{- if .requiresValue }}
6+
if actual == nil {
7+
err = goa.MergeErrors(err, goa.MissingFieldError("value", {{ printf "%q" .context }}))
8+
break
9+
}
10+
{{- end }}
511
{{ .validation }}
612
{{- end }}
713
}

codegen/testdata/golden/validation_union-with-view.go.golden

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,32 @@ func Validate() (err error) {
55
switch string(target.RequiredUnion.Kind()) {
66
case "int":
77
actual, _ := target.RequiredUnion.AsInt()
8+
if actual == nil {
9+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
10+
break
11+
}
812
if actual != nil {
913
if err2 := ValidateInteger(actual); err2 != nil {
1014
err = goa.MergeErrors(err, err2)
1115
}
1216
}
1317
case "float":
1418
actual, _ := target.RequiredUnion.AsFloat()
19+
if actual == nil {
20+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
21+
break
22+
}
1523
if actual != nil {
1624
if err2 := ValidateFloat(actual); err2 != nil {
1725
err = goa.MergeErrors(err, err2)
1826
}
1927
}
2028
case "string":
2129
actual, _ := target.RequiredUnion.AsString()
30+
if actual == nil {
31+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
32+
break
33+
}
2234
if actual != nil {
2335
if err2 := ValidateString(actual); err2 != nil {
2436
err = goa.MergeErrors(err, err2)
@@ -29,20 +41,32 @@ func Validate() (err error) {
2941
switch string(target.Union.Kind()) {
3042
case "int":
3143
actual, _ := target.Union.AsInt()
44+
if actual == nil {
45+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
46+
break
47+
}
3248
if actual != nil {
3349
if err2 := ValidateInteger(actual); err2 != nil {
3450
err = goa.MergeErrors(err, err2)
3551
}
3652
}
3753
case "float":
3854
actual, _ := target.Union.AsFloat()
55+
if actual == nil {
56+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
57+
break
58+
}
3959
if actual != nil {
4060
if err2 := ValidateFloat(actual); err2 != nil {
4161
err = goa.MergeErrors(err, err2)
4262
}
4363
}
4464
case "string":
4565
actual, _ := target.Union.AsString()
66+
if actual == nil {
67+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
68+
break
69+
}
4670
if actual != nil {
4771
if err2 := ValidateString(actual); err2 != nil {
4872
err = goa.MergeErrors(err, err2)

codegen/testdata/golden/validation_union.go.golden

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,32 @@ func Validate() (err error) {
55
switch string(target.RequiredUnion.Kind()) {
66
case "int":
77
actual, _ := target.RequiredUnion.AsInt()
8+
if actual == nil {
9+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
10+
break
11+
}
812
if actual != nil {
913
if err2 := ValidateInteger(actual); err2 != nil {
1014
err = goa.MergeErrors(err, err2)
1115
}
1216
}
1317
case "float":
1418
actual, _ := target.RequiredUnion.AsFloat()
19+
if actual == nil {
20+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
21+
break
22+
}
1523
if actual != nil {
1624
if err2 := ValidateFloat(actual); err2 != nil {
1725
err = goa.MergeErrors(err, err2)
1826
}
1927
}
2028
case "string":
2129
actual, _ := target.RequiredUnion.AsString()
30+
if actual == nil {
31+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.required_union.value"))
32+
break
33+
}
2234
if actual != nil {
2335
if err2 := ValidateString(actual); err2 != nil {
2436
err = goa.MergeErrors(err, err2)
@@ -29,20 +41,32 @@ func Validate() (err error) {
2941
switch string(target.Union.Kind()) {
3042
case "int":
3143
actual, _ := target.Union.AsInt()
44+
if actual == nil {
45+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
46+
break
47+
}
3248
if actual != nil {
3349
if err2 := ValidateInteger(actual); err2 != nil {
3450
err = goa.MergeErrors(err, err2)
3551
}
3652
}
3753
case "float":
3854
actual, _ := target.Union.AsFloat()
55+
if actual == nil {
56+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
57+
break
58+
}
3959
if actual != nil {
4060
if err2 := ValidateFloat(actual); err2 != nil {
4161
err = goa.MergeErrors(err, err2)
4262
}
4363
}
4464
case "string":
4565
actual, _ := target.Union.AsString()
66+
if actual == nil {
67+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "target.union.value"))
68+
break
69+
}
4670
if actual != nil {
4771
if err2 := ValidateString(actual); err2 != nil {
4872
err = goa.MergeErrors(err, err2)

codegen/validation.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ func recurseValidationCode(att *expr.AttributeExpr, put expr.UserType, attCtx *A
202202
continue
203203
}
204204
cases = append(cases, map[string]any{
205-
"typeTag": expr.UnionVariantTag(v),
206-
"fieldName": Goify(v.Name, true),
207-
"validation": val,
205+
"typeTag": expr.UnionVariantTag(v),
206+
"fieldName": Goify(v.Name, true),
207+
"requiresValue": strings.HasPrefix(strings.TrimSpace(val), "if actual != nil {"),
208+
"context": context + ".value",
209+
"validation": val,
208210
})
209211
}
210212
if len(cases) > 0 {

codegen/validation_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ func TestRecursiveValidationCode(t *testing.T) {
143143
require.Contains(t, code, "switch string(target.Choice.Kind())")
144144
require.Contains(t, code, "case \"text\":")
145145
require.Contains(t, code, "case \"json\":")
146+
require.Contains(t, code, "goa.MissingFieldError(\"value\", \"target.Choice.value\")")
146147
})
147148

148149
t.Run("invalid-constructor-union-fails-before-validation", func(t *testing.T) {

http/codegen/testdata/golden/client_types_client-nested-top-level-constructor-union-custom-keys.go.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,13 +357,21 @@ func ValidateOuterA(body *OuterA) (err error) {
357357
switch string(body.Choice.Kind()) {
358358
case "InnerA":
359359
actual, _ := body.Choice.AsInnerA()
360+
if actual == nil {
361+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.choice.value"))
362+
break
363+
}
360364
if actual != nil {
361365
if err2 := ValidateInnerA(actual); err2 != nil {
362366
err = goa.MergeErrors(err, err2)
363367
}
364368
}
365369
case "InnerB":
366370
actual, _ := body.Choice.AsInnerB()
371+
if actual == nil {
372+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.choice.value"))
373+
break
374+
}
367375
if actual != nil {
368376
if err2 := ValidateInnerB(actual); err2 != nil {
369377
err = goa.MergeErrors(err, err2)

http/codegen/testdata/golden/server_decode_decode-constructor-union-custom-keys.go.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ func DecodeShowRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.De
2525
switch string(body.Kind()) {
2626
case "TextPayload":
2727
actual, _ := body.AsTextPayload()
28+
if actual == nil {
29+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
30+
break
31+
}
2832
if actual != nil {
2933
if err2 := ValidateTextPayload(actual); err2 != nil {
3034
err = goa.MergeErrors(err, err2)
3135
}
3236
}
3337
case "JSONPayload":
3438
actual, _ := body.AsJSONPayload()
39+
if actual == nil {
40+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
41+
break
42+
}
3543
if actual != nil {
3644
if err2 := ValidateJSONPayload(actual); err2 != nil {
3745
err = goa.MergeErrors(err, err2)

http/codegen/testdata/golden/server_decode_decode-constructor-union-optional-body-with-header.go.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ func DecodeShowRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.De
2525
switch string(body.Kind()) {
2626
case "TextPayload":
2727
actual, _ := body.AsTextPayload()
28+
if actual == nil {
29+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
30+
break
31+
}
2832
if actual != nil {
2933
if err2 := ValidateTextPayload(actual); err2 != nil {
3034
err = goa.MergeErrors(err, err2)
3135
}
3236
}
3337
case "JSONPayload":
3438
actual, _ := body.AsJSONPayload()
39+
if actual == nil {
40+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
41+
break
42+
}
3543
if actual != nil {
3644
if err2 := ValidateJSONPayload(actual); err2 != nil {
3745
err = goa.MergeErrors(err, err2)

http/codegen/testdata/golden/server_decode_decode-constructor-union-required-body-with-header.go.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ func DecodeShowRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.De
2525
switch string(body.Kind()) {
2626
case "RequiredBodyWithHeaderTextPayload":
2727
actual, _ := body.AsRequiredBodyWithHeaderTextPayload()
28+
if actual == nil {
29+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
30+
break
31+
}
2832
if actual != nil {
2933
if err2 := ValidateRequiredBodyWithHeaderTextPayload(actual); err2 != nil {
3034
err = goa.MergeErrors(err, err2)
3135
}
3236
}
3337
case "RequiredBodyWithHeaderJSONPayload":
3438
actual, _ := body.AsRequiredBodyWithHeaderJSONPayload()
39+
if actual == nil {
40+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
41+
break
42+
}
3543
if actual != nil {
3644
if err2 := ValidateRequiredBodyWithHeaderJSONPayload(actual); err2 != nil {
3745
err = goa.MergeErrors(err, err2)

http/codegen/testdata/golden/server_decode_decode-constructor-union.go.golden

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ func DecodeShowRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.De
2525
switch string(body.Kind()) {
2626
case "TextPayload":
2727
actual, _ := body.AsTextPayload()
28+
if actual == nil {
29+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
30+
break
31+
}
2832
if actual != nil {
2933
if err2 := ValidateTextPayload(actual); err2 != nil {
3034
err = goa.MergeErrors(err, err2)
3135
}
3236
}
3337
case "JSONPayload":
3438
actual, _ := body.AsJSONPayload()
39+
if actual == nil {
40+
err = goa.MergeErrors(err, goa.MissingFieldError("value", "body.value"))
41+
break
42+
}
3543
if actual != nil {
3644
if err2 := ValidateJSONPayload(actual); err2 != nil {
3745
err = goa.MergeErrors(err, err2)

0 commit comments

Comments
 (0)