Skip to content

Detect problematic JSON tags with dash prefix

Description

This rule detects a security vulnerability in Go's JSON unmarshaling. When a struct field has a JSON tag that starts with -,, it can be unexpectedly unmarshaled with the - key.

According to the Go documentation, if the field tag is -, the field should be omitted. However, a field with name - can still be unmarshaled using the tag -,.

This creates a security issue where developers think they are preventing a field from being unmarshaled (like IsAdmin in authentication), but attackers can still set that field by providing the - key in JSON input.

go
type User struct {
    Username string `json:"username,omitempty"`
    Password string `json:"password,omitempty"`
    IsAdmin  bool   `json:"-,omitempty"`  // Intended to prevent marshaling
}

// This still works and sets IsAdmin to true!
json.Unmarshal([]byte(`{"-": true}`), &user)
// Result: main.User{Username:"", Password:"", IsAdmin:true}

YAML

yaml
id: unmarshal-tag-is-dash
severity: error
message: Struct field can be decoded with the `-` key because the JSON tag
  starts with a `-` but is followed by a comma.
rule:
  pattern: '`$TAG`'
  inside:
    kind: field_declaration
constraints:
  TAG:
    regex: json:"-,.*"

Example

go
package main

type TestStruct1 struct {
	A string `json:"id"` // ok
}

type TestStruct2 struct {
	B string `json:"-,omitempty"` // wrong
}

type TestStruct3 struct {
	C string `json:"-,123"` // wrong
}

type TestStruct4 struct {
	D string `json:"-,"` // wrong
}

Fix

To properly omit a field from JSON marshaling/unmarshaling, use just - without a comma:

go
type User struct {
    Username string `json:"username,omitempty"`
    Password string `json:"password,omitempty"`
    IsAdmin  bool   `json:"-"`  // Correctly prevents marshaling/unmarshaling
}

Contributed by

Inspired by Trail of Bits blog post and their public Semgrep rule.

Made with ❤️ with Rust