aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json/v2
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2025-06-21 21:27:09 -0700
committerGopher Robot <gobot@golang.org>2025-06-24 06:41:42 -0700
commit2e9bb62bfed92ef24a6744fbdc3cf24eb672cd56 (patch)
treed9c4a66c873fa306149de87d713d69f4555cc377 /src/encoding/json/v2
parented7815726db4a0eb904d7cae2532cde48348d7ff (diff)
downloadgo-2e9bb62bfed92ef24a6744fbdc3cf24eb672cd56.tar.xz
encoding/json/v2: reject unquoted dash as a JSON field name
In this blog: https://blog.trailofbits.com/2025/06/17/unexpected-security-footguns-in-gos-parsers/ the concern was raised that whenever "-" is combined with other options, the "-" is intepreted as as a name, rather than an ignored field, which may go contrary to user expectation. Static analysis demonstrates that there are ~2k instances of `json:"-,omitempty" in the wild, where almost all of them intended for the field to be ignored. To prevent this footgun, reject any tags that has "-," as a prefix and warn the user to choose one of the reasonable alternatives. The documentation of json/v2 already suggests `json:"'-'"` as the recommended way to explicitly specify dash as the name. See Example_fieldNames for example usages of the single-quoted literal. Update the v1 json documentation to suggest the same thing. Updates #71497 Change-Id: I7687b6eecdf82a5d894d057c78a4a90af4f5a6e4 Reviewed-on: https://go-review.googlesource.com/c/go/+/683175 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com> Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Daniel Martí <mvdan@mvdan.cc> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Diffstat (limited to 'src/encoding/json/v2')
-rw-r--r--src/encoding/json/v2/fields.go8
-rw-r--r--src/encoding/json/v2/fields_test.go13
2 files changed, 21 insertions, 0 deletions
diff --git a/src/encoding/json/v2/fields.go b/src/encoding/json/v2/fields.go
index 9413189c08..4a02be7327 100644
--- a/src/encoding/json/v2/fields.go
+++ b/src/encoding/json/v2/fields.go
@@ -404,6 +404,7 @@ type fieldOptions struct {
// the JSON member name and other features.
func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool, err error) {
tag, hasTag := sf.Tag.Lookup("json")
+ tagOrig := tag
// Check whether this field is explicitly ignored.
if tag == "-" {
@@ -453,6 +454,13 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
err = cmp.Or(err, fmt.Errorf("Go struct field %s has JSON object name %q with invalid UTF-8", sf.Name, name))
name = string([]rune(name)) // replace invalid UTF-8 with utf8.RuneError
}
+ if name == "-" && tag[0] == '-' {
+ defer func() { // defer to let other errors take precedence
+ err = cmp.Or(err, fmt.Errorf("Go struct field %s has JSON object name %q; either "+
+ "use `json:\"-\"` to ignore the field or "+
+ "use `json:\"'-'%s` to specify %q as the name", sf.Name, out.name, strings.TrimPrefix(strconv.Quote(tagOrig), `"-`), name))
+ }()
+ }
if err2 == nil {
out.hasName = true
out.name = name
diff --git a/src/encoding/json/v2/fields_test.go b/src/encoding/json/v2/fields_test.go
index 1c36f80905..ae58182f29 100644
--- a/src/encoding/json/v2/fields_test.go
+++ b/src/encoding/json/v2/fields_test.go
@@ -503,6 +503,19 @@ func TestParseTagOptions(t *testing.T) {
wantOpts: fieldOptions{hasName: true, name: "-", quotedName: `"-"`},
wantErr: errors.New("Go struct field V has malformed `json` tag: invalid trailing ',' character"),
}, {
+ name: jsontest.Name("DashCommaOmitEmpty"),
+ in: struct {
+ V int `json:"-,omitempty"`
+ }{},
+ wantOpts: fieldOptions{hasName: true, name: "-", quotedName: `"-"`, omitempty: true},
+ wantErr: errors.New("Go struct field V has JSON object name \"-\"; either use `json:\"-\"` to ignore the field or use `json:\"'-',omitempty\"` to specify \"-\" as the name"),
+ }, {
+ name: jsontest.Name("QuotedDashCommaOmitEmpty"),
+ in: struct {
+ V int `json:"'-',omitempty"`
+ }{},
+ wantOpts: fieldOptions{hasName: true, name: "-", quotedName: `"-"`, omitempty: true},
+ }, {
name: jsontest.Name("QuotedDashName"),
in: struct {
V int `json:"'-'"`