Skip to content

Top-level null payload silently bypasses required-field validation #556

Description

@plheide

Summary

When a generated Unmarshal{JSON,YAML} is called with a top-level null document, the required-field check is silently skipped and an all-zero struct is returned with no error. Any schema with required is affected.

Reproducer

Using the existing in-tree fixture tests/data/validation/requiredFields/requiredFields.json:

package main

import (
    "encoding/json"
    "fmt"
    test "github.com/atombender/go-jsonschema/tests/data/validation/requiredFields"
)

func main() {
    var r test.RequiredFields
    err := json.Unmarshal([]byte(`null`), &r)
    fmt.Printf("err = %v, MyString = %v\n", err, r.MyString)
}

Output:

err = <nil>, MyString = <nil>

Expected: a field myString in RequiredFields: required error.

Root cause

pkg/generator/validator.go, requiredValidator.generate:

out.Printlnf(`if _, ok := %s["%s"]; %s != nil && !ok {`, varNameRawMap, v.jsonName, varNameRawMap)

When the input is JSON null, json.Unmarshal(value, &raw) succeeds and leaves raw == nil. The raw != nil && short-circuit makes the required check skip entirely. The same pattern exists in readOnlyValidator.generate and the additional-properties strict path.

This guard was added in commit 8407170 (#215, "Removes nil check for required properties") to fix #97 — JSON Schema §6.5.3 says required checks presence, not non-nullness, so {"x": null} should be a valid required:["x"] payload. The fix correctly handled that case but inadvertently also lets the entire document be null.

Why this is still wrong

The inline comment in validator.go reads:

The container itself may be null (if the type is ["null", "object"]), in which case the map will be nil and none of the properties are present. This shouldn't fail the validation, though, as that's allowed as long as the container is allowed to be null.

The behavior matches that comment only when the container's type list includes null. For pure "object" schemas — the common case — a top-level null document should be rejected, but currently isn't.

Possible fixes

  1. Type-aware guard. Only short-circuit when the container schema's type list includes "null". The required validator would need access to the parent schema's type info; threading it through is straightforward (requiredValidator already gets declName). Most precise; matches the comment's stated intent.

  2. Top-level null rejection. Add a if raw == nil { return fmt.Errorf("...: cannot be null") } block before the per-property checks, gated similarly. Simpler but slightly different error shape.

  3. Caller-side guard. Reject null in generateUnmarshalBody (unmarshal_body.go) for non-nullable container types, before any validator runs. Keeps requiredValidator itself unchanged.

Blast radius

A behavior change here regenerates roughly every golden file with a required: schema (~50–80 files in tests/data/). Mostly mechanical (the raw != nil && literal disappears or becomes type-conditional), but worth surfacing up front.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions