secure-json-parse 0.1.0

prototype-poisoning-safe JSON parsing of untrusted input
Documentation
  • Coverage
  • 100%
    24 out of 24 items documented4 out of 13 items with examples
  • Size
  • Source code size: 53.87 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 589.58 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 5s Average build duration of successful builds.
  • all releases: 5s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • hey-jj/secure-json-parse
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • hey-jj

secure-json-parse

Parse JSON while blocking prototype-poisoning keys.

This crate parses untrusted JSON into a serde_json::Value and detects two key patterns that pollute a JavaScript object's prototype once the value is copied, merged, or iterated:

  1. a key literally named __proto__
  2. a key named constructor whose value is an object containing a prototype key (a constructor.prototype nesting)

For each pattern you pick an action: error, remove, or ignore. A safe flag turns any detected violation into Ok(None) instead of an error.

Installation

[dependencies]
secure-json-parse = "0.1"

Usage

use secure_json_parse::{parse, Action, Options, Error};

// Default options error on a forbidden key.
let err = parse(r#"{"__proto__": {"x": 7}}"#, &Options::default());
assert!(matches!(err, Err(Error::ForbiddenProperty)));

// Remove the key instead.
let opts = Options::default().proto_action(Action::Remove);
let value = parse(r#"{"a": 5, "__proto__": {"x": 7}}"#, &opts)
    .unwrap()
    .unwrap();
assert_eq!(value, serde_json::json!({"a": 5}));

Safe parsing

safe_parse folds every outcome into one three-valued result:

use secure_json_parse::{safe_parse, SafeOutcome};

assert!(matches!(safe_parse(r#"{"a": 1}"#), SafeOutcome::Value(_)));
assert!(matches!(safe_parse(r#"{"__proto__": {}}"#), SafeOutcome::Violation));
assert!(matches!(safe_parse(r#"{"a": "#), SafeOutcome::Malformed));

Value means a clean parse, Violation means a forbidden key was found, and Malformed means the JSON text was invalid.

Scanning a parsed value

scan runs the same checks on a value you already hold, with no JSON parsing:

use secure_json_parse::{scan, Action, Options};
use serde_json::json;

let opts = Options::default().constructor_action(Action::Remove);
let cleaned = scan(json!({"constructor": {"prototype": {}}, "a": 1}), &opts)
    .unwrap()
    .unwrap();
assert_eq!(cleaned, json!({"a": 1}));

Options

Field Values Default
proto_action Error, Remove, Ignore Error
constructor_action Error, Remove, Ignore Error
safe true, false false

A constructor key counts as a violation only when its value is an object that holds a prototype key. A null, array, string, number, or bool value for constructor is kept. So is a constructor object with no prototype child.

Scalars and null skip scanning. Objects and arrays are walked, including arrays nested anywhere in the tree.

Byte input

parse_bytes and safe_parse_bytes take &[u8]. A leading UTF-8 byte order mark (EF BB BF) is stripped before parsing. The text path strips a leading U+FEFF for the same reason.

License

Licensed under the MIT license.