Crate json_threat_protection

Source
Expand description

A crate to protect against malicious JSON payloads.

This crate provides functionality to validate JSON payloads against a set of constraints.

  • Maximum depth of the JSON structure.
  • Maximum length of strings.
  • Maximum number of entries in arrays.
  • Maximum number of entries in objects.
  • Maximum length of object entry names.
  • Whether to allow duplicate object entry names.

This crate is designed to process untrusted JSON payloads, such as it does not use recursion to validate the JSON structure.

§Examples

use json_threat_protection as jtp;

fn reject_highly_nested_json(data: &[u8], depth: usize) -> Result<(), jtp::Error> {
    jtp::from_slice(data).with_max_depth(depth).validate()
}

fn reject_too_long_strings(data: &[u8], max_string_length: usize) -> Result<(), jtp::Error> {
    jtp::from_slice(data).with_max_string_length(max_string_length).validate()
}

fn reject_too_many_array_entries(data: &[u8], max_array_entries: usize) -> Result<(), jtp::Error> {
    jtp::from_slice(data).with_max_array_entries(max_array_entries).validate()
}

fn reject_too_many_object_entries(data: &[u8], max_object_entries: usize) -> Result<(), jtp::Error> {
   jtp::from_slice(data).with_max_object_entries(max_object_entries).validate()
}

fn reject_too_long_object_entry_names(data: &[u8], max_object_entry_name_length: usize) -> Result<(), jtp::Error> {
   jtp::from_slice(data).with_max_object_entry_name_length(max_object_entry_name_length).validate()
}

fn reject_duplicate_object_entry_names(data: &[u8]) -> Result<(), jtp::Error> {
  jtp::from_slice(data).disallow_duplicate_object_entry_name().validate()
}

§Default constraints

By default, the validator just checks the JSON syntax without any constraints, and also allows duplicate object entry names.

You could set the limit to NO_LIMIT to disable a specific constraint.

§Incremental validation

The Validator struct is designed to be used incrementally, so you can validate huge JSON payloads in multiple function calls without blocking the current thread for a long time.

use json_threat_protection as jtp;

fn validate_incrementally(data: &[u8]) -> Result<(), jtp::Error> {
    let mut validator = jtp::from_slice(data);
     
    // validate the JSON payload in 2000 steps,
    // and return `Some(true)` if the validation is finished and no errors.
    // return `Some(false)` to continue the validation.
    // Otherwise, return `Err` if an error occurred.
    while validator.validate_with_steps(2000)? {
        // do something else such as processing other tasks
    }

    Ok(())
}

This feature is useful when you want to validate a JSON payload in a non-blocking way, the typical use case is used to build FFI bindings to other software that needs to validate JSON payloads in a non-blocking way to avoid blocking the thread.

§Error handling

This crate has limited place where might panic, most of errors are returned as Err. And some unintended bugs might also return as Err with explicit error kind to indicate we are running into buggy code.

Whatever the error kind is, it always contains the position where the error occurred, such as line, column, and offset, the offset is the byte offset from the beginning of the JSON payload.

§Special behavior compared to serde_json

This crate do it best to keep consistent with serde_json’s behavior using the cargo-fuzz to process the same JSON payloading with both this crate and serde_json and compare the validation results.

However, there are some differences between this crate and serde_json so far:

§Performance

This crate is designed to be fast and efficient, and has its own benchmark suite under the benches directory. You can run the benchmarks with the following command:

JSON_FILE=/path/to/file.json cargo bench --bench memory -- --verbose

This suite validates the JSON syntax using both this crate and serde_json, you could get your own performance number by specifying the JSON_FILE to your dataset.

§Fuzzing

This crate is fuzzed using the cargo-fuzz tool, program is under the fuzz directory.

The initial seed corpus is from nlohmann/json_test_data, and extra corpus follows the nlohmann/json/blob/develop/tests/fuzzing.

Re-exports§

pub use read::ReadError;

Modules§

read
Defines the Read trait, and provided implementations for std::io::Read, &str, and slice for u8.

Structs§

Validator
The JSON validator.

Enums§

Error
Error occurred during JSON validation
LexerError
An error that occurred while lexing a JSON input.

Constants§

NO_LIMIT
Represents no limit for a specific constraint.

Functions§

from_reader
Creates a new Validator instance with the given reader without any constraints.
from_slice
Creates a new Validator instance with the given slice of bytes without any constraints.
from_str
Creates a new Validator instance with the given &str without any constraints.