strictjson

package
v0.19.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 4 Imported by: 0

README

strictjson

import "github.com/kitsunium/sdk/pkg/v1/data/codec/strictjson"

Package strictjson decodes exactly one JSON document into a Go value, within a byte bound, refusing what a permissive decoder lets through — and no refusal ever repeats a byte of the document.

var in CreateItem
if err := strictjson.Decode(file, &in, 1<<20); err != nil {
	return err // DOCUMENT_TOO_LARGE, MEMBER_UNKNOWN, VALUE_MISMATCHED…
}
Why a second JSON decoder

The codec package's json format is encoding/json with its defaults, which is the right thing for data this program wrote. For a document somebody else wrote it is five ways for one document to be read two ways, none of which fails: no size limit, unknown members silently dropped, a member matched to a field case-insensitively, a duplicated name won by whichever came last, and data after the value ignored. This package refuses all five, plus invalid UTF-8 and a number out of its field's range. It is built on encoding/json/v2, whose defaults already refuse most of them.

The refusals

Each is a typed error with a fixed sentence and an HTTP status, matched with errs.HasCode:

CodeDocumentTooLarge      413  longer than the bound — the part read is not decoded as if it were whole
CodeDocumentEmpty         400  zero bytes: "no body", which a caller may accept
CodeDocumentMalformed     400  syntax, truncation, trailing data, a duplicate name, invalid UTF-8
CodeMemberUnknown         400  a member the target does not declare, including a case-only variant
CodeValueMismatched       400  the wrong kind, a number out of range, a field's own unmarshaler refused
CodeMediaTypeUnsupported  415  a request body (httpbody) that does not declare application/json or +json
CodeDocumentUnreadable    400  the reader failed before the document ended
CodeDecodeMisconfigured   500  a non-positive bound, or a target that is not a non-nil pointer

A zero bound is refused rather than read as "unlimited": the two readings of zero are opposites, and the unlimited one is a memory-exhaustion bug.

Never the input

No Public, no Private and no field of a refusal carries a value from the document, and the decoder's own error — whose text quotes the offending member name and, for a field's own unmarshaler, the offending value — is not kept in the chain. Where the document failed is available separately, from PointerOf, as a JSON Pointer ("/items/1/price") built from the document's own member names and indices and bounded to MaxPointerBytes: a location, never a value, and still the document's text, so render it as untrusted.

Request bodies

The JSON body of an HTTP request is decoded by the package beneath this one, github.com/kitsunium/sdk/pkg/v1/data/codec/strictjson/httpbody, whose DecodeRequest adds what only an HTTP body has — a bound net/http enforces too, an empty body settled first, a media type that must declare JSON — and refuses with this package's codes. It is a package of its own so that this one links no net/http: a program decoding documents from files, queues or sockets does not carry an HTTP stack it never serves.

Index

Constants

CodeDecodeMisconfigured identifies a call no input can satisfy: a non-positive bound, or a target that is not a non-nil pointer.

const CodeDecodeMisconfigured errs.Code = corestrictjson.CodeDecodeMisconfigured

CodeDocumentEmpty identifies a document of zero bytes (400).

const CodeDocumentEmpty errs.Code = corestrictjson.CodeDocumentEmpty

CodeDocumentMalformed identifies a document that is not exactly one well-formed JSON value (400).

const CodeDocumentMalformed errs.Code = corestrictjson.CodeDocumentMalformed

CodeDocumentTooLarge identifies a document longer than the bound (413).

const CodeDocumentTooLarge errs.Code = corestrictjson.CodeDocumentTooLarge

CodeDocumentUnreadable identifies a reader that failed before the document ended (400).

const CodeDocumentUnreadable errs.Code = corestrictjson.CodeDocumentUnreadable

CodeMediaTypeUnsupported identifies a request body that does not declare JSON (415).

const CodeMediaTypeUnsupported errs.Code = corestrictjson.CodeMediaTypeUnsupported

CodeMemberUnknown identifies a member the target does not declare (400).

const CodeMemberUnknown errs.Code = corestrictjson.CodeMemberUnknown

CodeValueMismatched identifies a value the target cannot hold (400).

const CodeValueMismatched errs.Code = corestrictjson.CodeValueMismatched

MaxPointerBytes bounds the JSON Pointer PointerOf returns; a longer one is cut back to the deepest ancestor that fits.

const MaxPointerBytes int = svcstrict.MaxPointerBytes

Variables

var (
    // DocumentTooLarge refuses a document longer than the bound.
    DocumentTooLarge = corestrictjson.DocumentTooLarge
    // DocumentEmpty reports a document of zero bytes.
    DocumentEmpty = corestrictjson.DocumentEmpty
    // DocumentMalformed refuses a document that is not one well-formed value.
    DocumentMalformed = corestrictjson.DocumentMalformed
    // MemberUnknown refuses a member the target does not declare.
    MemberUnknown = corestrictjson.MemberUnknown
    // ValueMismatched refuses a value the target cannot hold.
    ValueMismatched = corestrictjson.ValueMismatched
    // MediaTypeUnsupported refuses a request body that does not declare JSON.
    MediaTypeUnsupported = corestrictjson.MediaTypeUnsupported
    // DocumentUnreadable wraps a reader that failed before the document ended.
    DocumentUnreadable = corestrictjson.DocumentUnreadable
    // DecodeMisconfigured refuses a call no input can satisfy.
    DecodeMisconfigured = corestrictjson.DecodeMisconfigured
)

func Decode

func Decode(r io.Reader, v any, maxBytes int64) error

Decode reads exactly one JSON value from r into v, a non-nil pointer, reading at most maxBytes bytes of it — the bound plus the one byte that proves it was exceeded, never more. On a refusal v may be partly written.

func PointerOf

func PointerOf(err error) (pointer string, ok bool)

PointerOf returns where a refused document went wrong, as a JSON Pointer ("" is the document itself), and whether err carries one. It is the document's own member names and indices, bounded to MaxPointerBytes — render it as untrusted text.

Generated by gomarkdoc

Documentation

Overview

Package strictjson decodes exactly one JSON document into a Go value, within a byte bound, refusing what a permissive decoder lets through — and no refusal ever repeats a byte of the document.

var in CreateItem
if err := strictjson.Decode(file, &in, 1<<20); err != nil {
	return err // DOCUMENT_TOO_LARGE, MEMBER_UNKNOWN, VALUE_MISMATCHED…
}

Why a second JSON decoder

The codec package's json format is encoding/json with its defaults, which is the right thing for data this program wrote. For a document somebody else wrote it is five ways for one document to be read two ways, none of which fails: no size limit, unknown members silently dropped, a member matched to a field case-insensitively, a duplicated name won by whichever came last, and data after the value ignored. This package refuses all five, plus invalid UTF-8 and a number out of its field's range. It is built on encoding/json/v2, whose defaults already refuse most of them.

The refusals

Each is a typed error with a fixed sentence and an HTTP status, matched with errs.HasCode:

CodeDocumentTooLarge      413  longer than the bound — the part read is not decoded as if it were whole
CodeDocumentEmpty         400  zero bytes: "no body", which a caller may accept
CodeDocumentMalformed     400  syntax, truncation, trailing data, a duplicate name, invalid UTF-8
CodeMemberUnknown         400  a member the target does not declare, including a case-only variant
CodeValueMismatched       400  the wrong kind, a number out of range, a field's own unmarshaler refused
CodeMediaTypeUnsupported  415  a request body (httpbody) that does not declare application/json or +json
CodeDocumentUnreadable    400  the reader failed before the document ended
CodeDecodeMisconfigured   500  a non-positive bound, or a target that is not a non-nil pointer

A zero bound is refused rather than read as "unlimited": the two readings of zero are opposites, and the unlimited one is a memory-exhaustion bug.

Never the input

No Public, no Private and no field of a refusal carries a value from the document, and the decoder's own error — whose text quotes the offending member name and, for a field's own unmarshaler, the offending value — is not kept in the chain. Where the document failed is available separately, from PointerOf, as a JSON Pointer ("/items/1/price") built from the document's own member names and indices and bounded to MaxPointerBytes: a location, never a value, and still the document's text, so render it as untrusted.

Request bodies

The JSON body of an HTTP request is decoded by the package beneath this one, github.com/kitsunium/sdk/pkg/v1/data/codec/strictjson/httpbody, whose DecodeRequest adds what only an HTTP body has — a bound net/http enforces too, an empty body settled first, a media type that must declare JSON — and refuses with this package's codes. It is a package of its own so that this one links no net/http: a program decoding documents from files, queues or sockets does not carry an HTTP stack it never serves.

Index

Constants

View Source
const CodeDecodeMisconfigured errs.Code = corestrictjson.CodeDecodeMisconfigured

CodeDecodeMisconfigured identifies a call no input can satisfy: a non-positive bound, or a target that is not a non-nil pointer.

CodeDocumentEmpty identifies a document of zero bytes (400).

View Source
const CodeDocumentMalformed errs.Code = corestrictjson.CodeDocumentMalformed

CodeDocumentMalformed identifies a document that is not exactly one well-formed JSON value (400).

CodeDocumentTooLarge identifies a document longer than the bound (413).

View Source
const CodeDocumentUnreadable errs.Code = corestrictjson.CodeDocumentUnreadable

CodeDocumentUnreadable identifies a reader that failed before the document ended (400).

View Source
const CodeMediaTypeUnsupported errs.Code = corestrictjson.CodeMediaTypeUnsupported

CodeMediaTypeUnsupported identifies a request body that does not declare JSON (415).

CodeMemberUnknown identifies a member the target does not declare (400).

CodeValueMismatched identifies a value the target cannot hold (400).

View Source
const MaxPointerBytes int = svcstrict.MaxPointerBytes

MaxPointerBytes bounds the JSON Pointer PointerOf returns; a longer one is cut back to the deepest ancestor that fits.

Variables

View Source
var (
	// DocumentTooLarge refuses a document longer than the bound.
	DocumentTooLarge = corestrictjson.DocumentTooLarge
	// DocumentEmpty reports a document of zero bytes.
	DocumentEmpty = corestrictjson.DocumentEmpty
	// DocumentMalformed refuses a document that is not one well-formed value.
	DocumentMalformed = corestrictjson.DocumentMalformed
	// MemberUnknown refuses a member the target does not declare.
	MemberUnknown = corestrictjson.MemberUnknown
	// ValueMismatched refuses a value the target cannot hold.
	ValueMismatched = corestrictjson.ValueMismatched
	// MediaTypeUnsupported refuses a request body that does not declare JSON.
	MediaTypeUnsupported = corestrictjson.MediaTypeUnsupported
	// DocumentUnreadable wraps a reader that failed before the document ended.
	DocumentUnreadable = corestrictjson.DocumentUnreadable
	// DecodeMisconfigured refuses a call no input can satisfy.
	DecodeMisconfigured = corestrictjson.DecodeMisconfigured
)

Functions

func Decode

func Decode(r io.Reader, v any, maxBytes int64) error

Decode reads exactly one JSON value from r into v, a non-nil pointer, reading at most maxBytes bytes of it — the bound plus the one byte that proves it was exceeded, never more. On a refusal v may be partly written.

func PointerOf

func PointerOf(err error) (pointer string, ok bool)

PointerOf returns where a refused document went wrong, as a JSON Pointer ("" is the document itself), and whether err carries one. It is the document's own member names and indices, bounded to MaxPointerBytes — render it as untrusted text.

Types

This section is empty.

Directories

Path Synopsis
Package httpbody decodes the JSON body of an HTTP request with the strict decoder of github.com/kitsunium/sdk/pkg/v1/data/codec/strictjson, adding what only an HTTP body has.
Package httpbody decodes the JSON body of an HTTP request with the strict decoder of github.com/kitsunium/sdk/pkg/v1/data/codec/strictjson, adding what only an HTTP body has.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL