Contact Us 1-800-596-4880

Using JSON Validator Library Functions

To view an example policy project that uses Flex Gateway Policy Development Kit (PDK)'s JSON Validator library, see JSON Validation Policy Example.

JSON APIs can receive payloads with deep nesting, oversized arrays or objects, or long strings and keys. Attackers use those patterns to stress parsers and memory. With validate_chunk, you feed the payload incrementally while the PDK JSON Validator library enforces configurable limits. Use the library to build custom policies. These limits match the constraint types in policies-included-json-threat-protection.adoc for Flex Gateway.

Use the library when your custom policy must perform these tasks.

  • Accept JSON request or response bodies and reject malformed or oversized payloads before the gateway fully buffers them.

  • Apply limits that match your API’s threat model, such as maximum nesting depth, array length, object entry count, or string and key size.

  • Validate JSON with streaming body state. Call validate_chunk for each chunk, or buffer the full stream in memory first, then call validate_chunk once with end_of_stream set to true.

Import the types you need from the json_validator module of the PDK crate.

use pdk::json_validator::{JsonValidatorBuilder, ValidationError, ValidationResult};

Build a JSON Validator

Create a validator with JsonValidatorBuilder::new(), chain the configuration methods for the limits you need, and call build().

Builder methods match the constraint types in policies-included-json-threat-protection.adoc. Policy UI parameter names differ slightly, but the intent matches.

Method Description

with_max_depth

Maximum nesting depth of JSON containers, objects, and arrays combined.

with_max_array_length

Maximum number of elements in a single array.

with_max_object_entries

Maximum number of entries in a single object.

with_max_key_length

Maximum length of an object entry name, as UTF-8 byte length.

with_max_string_length

Maximum length of a string value, as UTF-8 byte length.

If you omit a with_* method, that limit isn’t applied, so there’s no restriction on that field. Call only the methods for limits you want enforced. For type details and any extra options, see the PDK Rust API documentation for your release.

Validate JSON Body Chunks

Call validate_chunk for each slice of the body you read from the client. The second argument is end_of_stream. Set it to true only on the final chunk so the parser knows the document is complete.

validate_chunk returns one of these outcomes:

  • Ok(ValidationResult::Incomplete), the JSON is valid so far, and you need more chunks before validation can finish.

  • Ok(ValidationResult::Complete), the JSON is complete and valid.

  • Err(ValidationError::…​), the payload is malformed or violates a configured limit. See the PDK Rust API documentation for every variant, such as UnableToProcessPayload.

This example reads every chunk from the body stream into a single buffer, then validates the buffer in one call with end_of_stream set to true. For very large bodies, call validate_chunk once per chunk instead, and set end_of_stream to true only on the last chunk so you don’t hold the full payload in memory.

use anyhow::Result;
use futures::StreamExt;
use pdk::hl::*;
use pdk::json_validator::{JsonValidatorBuilder, ValidationError, ValidationResult};

async fn request_filter(state: RequestState) -> Flow<()> {
    let headers_state = state.into_headers_state().await;

    if !headers_state.contains_body() {
        return Flow::Continue(());
    }

    let body_stream_state = headers_state.into_body_stream_state().await;
    let mut validator = JsonValidatorBuilder::new()
        .with_max_depth(10)
        .with_max_array_length(1000)
        .with_max_object_entries(1000)
        .with_max_key_length(256)
        .with_max_string_length(10000)
        .build();

    let mut stream = body_stream_state.stream();
    let mut buf = Vec::new();
    while let Some(chunk) = stream.next().await {
        buf.extend_from_slice(&chunk.into_bytes());
    }

    match validator.validate_chunk(&buf, true) {
        Ok(ValidationResult::Incomplete) => {
            Flow::Break(Response::new(400).with_body("Incomplete JSON payload"))
        }
        Ok(ValidationResult::Complete) => Flow::Continue(()),
        Err(ValidationError::UnableToProcessPayload) => {
            Flow::Break(Response::new(400).with_body("Invalid or disallowed JSON payload"))
        }
        Err(_) => Flow::Break(Response::new(400).with_body("Invalid or disallowed JSON payload")),
    }
}

#[entrypoint]
async fn configure(launcher: Launcher, Configuration(_configuration): Configuration) -> Result<()> {
    launcher
        .launch(on_request(|request| request_filter(request)))
        .await?;
    Ok(())
}

On success, the example continues the request so upstream processing runs. On failure, the policy returns 400 Bad Request. Change the status code and body to match your API’s contract.

When end_of_stream is true and the validator returns ValidationResult::Incomplete, the client didn’t send a complete JSON value. Treat that outcome as an error for your API.

Initialize the Validator from Policy Configuration

If you supply limits through your policy schema in GCL, parse the configuration and build the JsonValidatorBuilder in the #[entrypoint] function. If the configuration is invalid or incompatible, you see a failure when you apply the policy, before traffic reaches the API. Pass the validator into your request filter. When you need shared ownership across closures, wrap the validator in std::rc::Rc or std::sync::Arc, depending on your policy layout.

Map your generated config::Config fields to the builder methods in Build a JSON Validator. Use the types the PDK API expects for your release.

Interpret Validation Errors

If validate_chunk returns Err, reject the payload. The JSON might be malformed or violate a configured limit. If you need operational detail, log the error at the debug or warn level. If you can’t expose parser details, return a generic message to clients.