#391·gjson

Security: Stack Overflow DoS, @dig Memory Amplification, and Parse() Information Disclosure

Author: uug4naCreated Feb 23, 2026Updated Feb 23, 2026

Security Vulnerabilities in gjson v1.18.0

I've identified three security vulnerabilities during a security audit of gjson v1.18.0. Since this repository has no security policy or private vulnerability reporting enabled, I'm reporting them here.


Vulnerability 1: Stack Overflow via Deep Nesting (HIGH - CWE-674)

Root Cause: parseObject() at gjson.go:1288 calls itself recursively without any depth limit. Deeply nested JSON (~5000 levels) exhausts the goroutine stack.

Impact: This is a fatal, unrecoverable crashruntime: goroutine stack exceeds limit cannot be caught by recover(). Any server parsing untrusted JSON with gjson can be crashed with a small crafted payload.

PoC:

go
package main

import (
    "fmt"
    "strings"
    "github.com/tidwall/gjson"
)

func main() {
    depth := 5000
    json := strings.Repeat(`{"a":`, depth) + `1` + strings.Repeat(`}`, depth)
    result := gjson.Get(json, strings.Repeat("a.", depth-1)+"a")
    fmt.Println(result)
}

Suggested Fix: Add a recursion depth counter to parseObject/parseArray and return an error or empty result when exceeding a reasonable limit (e.g., 1000).


Vulnerability 2: @dig Memory Amplification DoS (HIGH - CWE-770)

Root Cause: parseRecursiveDescent() at gjson.go:3578-3589 recursively calls Get() + ForEach() on every nested node with no depth limit or output size cap. This produces O(N²) output from O(N) input.

Impact: A 117KB input with 20,000 nesting levels produces 1,144 MB of output and consumes 7 GB of memory.

Input Size Output Size Amplification Memory
6 KB 2.9 MB 500x 19 MB
29 KB 71.5 MB 2,500x 473 MB
59 KB 286 MB 5,000x 2,037 MB
117 KB 1,144 MB 10,000x 7,034 MB

PoC:

go
package main

import (
    "fmt"
    "strings"
    "github.com/tidwall/gjson"
)

func main() {
    depth := 10000
    json := strings.Repeat(`{"a":`, depth) + `"val"` + strings.Repeat(`}`, depth)
    result := gjson.Get(json, `@dig:a`)
    fmt.Printf("Input: %d bytes, Output: %d bytes (%.0fx amplification)\n",
        len(json), len(result.Raw), float64(len(result.Raw))/float64(len(json)))
}

Suggested Fix: Add a maximum output size or recursion depth limit to parseRecursiveDescent().


Vulnerability 3: Parse() Information Disclosure (MEDIUM - CWE-200)

Root Cause: At gjson.go line 470, Parse() sets value.Raw = json[i:] which captures everything from the JSON value to the end of the input string, including any trailing data.

Impact: If the input buffer contains data beyond the JSON (common in network protocols, HTTP pipelining, concatenated messages, or reused byte buffers), the Raw field exposes all trailing data. Applications that log or return Result.Raw will leak sensitive information.

PoC:

go
package main

import (
    "fmt"
    "github.com/tidwall/gjson"
)

func main() {
    input := `{"public":"data"}SECRET_API_KEY=sk-abc123`
    result := gjson.Parse(input)
    fmt.Printf("Raw: %s\n", result.Raw)
    // Output: Raw: {"public":"data"}SECRET_API_KEY=sk-abc123
}

Suggested Fix: Slice Raw to only include the parsed JSON value, not trailing content.


Environment

  • gjson v1.18.0
  • Go 1.25.6
  • Linux x86_64