SchemaSafe JSON Safe Parser & Schema Validator

repository·master·Indexed 41 days ago

https://github.com/exodusoss/schemasafe

A high-performance, secure JSON Schema validator and parser (version 1.3.0) that generates optimized JavaScript code from schemas. It supports multiple JSON Schema drafts and features an 'Auditable' design with zero external dependencies. Key features include a parser for handling incoming JSON strings, a validator for existing objects, complexity checks for DoS prevention, and the ability to generate self-contained modules via .toModule().

Tokens
130.4K
Snippets
699
Records
807
Agent score
48%

What's inside SchemaSafe

  1. How propertyDependencies works with objects and values

    master

    The propertyDependencies keyword defines dependencies between properties in an object. It is important to note the following behaviors:

    1. Object Requirement: propertyDependencies only acts on data that is a non-array object. If the data being validated is not an object, the dependency rules are not applied.
    2. Value Matching: The dependency logic triggers based on the value of the dependent property. If the value of the property (e.g., foo) does not match the key specified in the dependency map (e.g., bar), the rules are not applied. In the provided examples, if foo is not equal to the target value, the validation logic for that dependency is skipped.
    { "propertyDependencies": { "foo": { "bar": false } } }
  2. Understand unevaluatedProperties behavior in nested schemas

    master

    The unevaluatedProperties keyword allows you to restrict an object to only the properties that were evaluated by subschemas (like properties, patternProperties, or additionalProperties). When nesting unevaluatedProperties within allOf or other combinators, the behavior depends on whether the outer or inner schema controls the restriction.

    Nested: Outer false, Inner true

    If the outer schema sets unevaluatedProperties: false and an inner allOf branch sets unevaluatedProperties: true, the outer restriction typically dominates the final validation result for the object.

    Nested: Outer true, Inner false

    If the outer schema sets unevaluatedProperties: true but an inner branch in allOf sets unevaluatedProperties: false, the inner branch will restrict the object to only the properties defined within that specific branch.

    Cousin schemas in allOf

    When using allOf, multiple schemas act as 'cousins'. If one branch defines properties and another branch sets unevaluatedProperties: false, the resulting validation will only allow properties that satisfy the requirements of the branches that actually evaluated them.

    {
      "type": "object",
      "allOf": [
        {
          "properties": { "foo": { "type": "string" } },
          "unevaluatedProperties": false
        }
      ],
      "unevaluatedProperties": true
    }
  3. Use the `dependencies` keyword for conditional validation

    master

    The dependencies keyword allows you to specify that if a certain property is present in the data, other properties must also be present. This can be used with simple arrays of property names or with complex subschemas.

    Single Dependency

    If property bar exists, property foo must also exist.

    Multiple Dependents

    If property quux exists, both foo and bar must also exist.

    Boolean Subschemas

    You can use boolean values within dependencies. For example, setting a dependency to false effectively means that if the key is present, validation fails (often used to forbid certain keys). Note that using true as a subschema may trigger strong mode notices depending on your configuration.

    // Single dependency: if 'bar' exists, 'foo' must exist
    { "dependencies": { "bar": ["foo"] } }
    
    // Multiple dependents: if 'quux' exists, 'foo' and 'bar' must exist
    { "dependencies": { "quux": ["foo", "bar"] } }
    
    // Boolean subschemas
    { "dependencies": { "foo": true, "bar": false } }
  4. Understand regex behavior in patternProperties

    master

    When using patternProperties in an object schema, the regex patterns are applied to the object keys. Be aware of the following behaviors:

    1. Unicode Semantics: Patterns like \p{Letter} will use Unicode semantics if the u flag is present.
    2. \w behavior: The \w shorthand matches [A-Za-z0-9_] and does not match Unicode letters.
    3. \d behavior: The \d shorthand matches [0-9] and does not match Unicode digits (use \p{digit} for Unicode digits).
    4. Anchoring: For predictable key validation, it is recommended to anchor patterns with ^ and $ (e.g., ^\p{Letter}cole$).

    Strong mode notices:

    • [requireValidation] schema = true is not allowed when using certain patterns in patternProperties.
    • [complexityChecks] maxLength should be specified for complex patterns.
    {
      "type": "object",
      "patternProperties": { "\\p{Letter}cole": true },
      "additionalProperties": false
    }
  5. How $dynamicRef and $dynamicAnchor behave in Draft 2020-12

    master

    In Draft 2020-12 schemas, $dynamicRef and $dynamicAnchor provide mechanisms for dynamic resolution. The behavior depends on the relationship between the reference and the anchor:

    • $dynamicRef to a $dynamicAnchor in the same resource: Behaves like a standard $ref to an $anchor.
    • $dynamicRef to an $anchor in the same resource: Behaves like a standard $ref to an $anchor.
    • $ref to a $dynamicAnchor in the same resource: Behaves like a standard $ref to an $anchor.
    • Resolution Logic: A $dynamicRef resolves to the first $dynamicAnchor still in scope that is encountered during schema evaluation. This allows for polymorphic schema structures where the specific validation logic can be swapped out based on the evaluation context.
  6. Use remote references with $ref

    master

    You can reference external JSON schemas using the $ref keyword with a URI. This allows for modular schema design where parts of a schema are hosted at different locations.

    Remote Reference Types

    1. Full Remote Reference: Points to an entire external schema file.

      • Schema: { "$ref": "http://localhost:1234/integer.json" }
    2. Fragment within Remote Reference: Points to a specific definition or fragment inside an external schema file using a hash (#).

      • Schema: { "$ref": "http://localhost:1234/subSchemas.json#/definitions/integer" }
    3. Nested Reference: A remote reference that points to a fragment which itself contains further references.

      • Schema: { "$ref": "http://localhost:1234/subSchemas.json#/definitions/refToInteger" }
    4. Root Ref in Remote Ref: A schema that uses an id to establish its own base URI, then uses $ref to point to a fragment in another remote file.

      • Schema:
        {
          "id": "http://localhost:1234/object",
          "type": "object",
          "properties": { "name": { "$ref": "name.json#/definitions/orNull" } }
        }
    { "$ref": "http://localhost:1234/integer.json" }
  7. Handling unknown keywords and $id identifiers

    master

    When using JSON Schema, if a schema contains a keyword that is not supported by the validator (e.g., array_of_schemas inside a not block), the validator treats it as an unknown keyword.

    Crucially, an $id defined inside an unknown keyword is not treated as a real identifier for the purpose of schema referencing. In the example below, even though id_in_unknown0 contains an $id, it cannot be used to resolve references that point to that specific identifier, as the validator does not recognize the parent keyword structure.

    {
      "$defs": {
        "id_in_unknown0": {
          "not": {
            "array_of_schemas": [
              {
                "$id": "https://localhost:1234/draft2019-09/unknownKeyword/my_identifier.json",
                "type": "null"
              }
            ]
          }
        }
      }
    }
  8. Use patternProperties to validate properties matching a regex

    master

    The patternProperties keyword allows you to validate object properties whose names match a specific regular expression. When a key in the data object matches the provided regex, the corresponding schema is applied to that property's value.

    Key Behaviors

    • Regex Anchoring: By default, regexes are not anchored. They will match any part of the key unless you explicitly use ^ and $ in your pattern.
    • Case Sensitivity: Regex matching is case-sensitive.
    • Multiple Patterns: If multiple patternProperties patterns match a single key, all applicable schemas must be validated against that property.
    {
      "patternProperties": {
        "f.*o": { "type": "integer" }
      }
    }
  9. Use the dependentRequired keyword in JSON Schema

    master

    The dependentRequired keyword (from the JSON Schema Draft 2019-09 specification) is used to specify that if a certain property is present in an object, then other specific properties must also be present.

    Single Dependency

    To require that if bar exists, foo must also exist:

    { "dependentRequired": { "bar": ["foo"] } }

    Multiple Dependents

    To require that if quux exists, both foo and bar must also exist:

    { "dependentRequired": { "quux": ["foo", "bar"] } }

    Empty Dependents

    If the array of required properties is empty, the validation always passes (it effectively does nothing):

    { "dependentRequired": { "bar": [] } }

    Dependencies with Escaped Characters

    dependentRequired supports property names containing escaped characters like newlines or quotes:

    { "dependentRequired": { "foo\nbar": ["foo\rbar"], "foo\"bar": ["foo'bar"] } }
  10. Understand ECMA 262 regex behavior in schemas

    master

    When using JSON Schema pattern or patternProperties, the generated validation logic uses ECMA 262 (JavaScript) regular expression semantics. This includes specific behaviors for special characters and shorthand classes:

    • The $ anchor: In ECMA 262, $ does not match a trailing newline. To ensure a strict match of the entire string, use ^ and $ anchors.
    • Whitespace: \t is converted to a horizontal tab.
    • Control Codes: Control codes can be escaped using \cC (upper case) or \cc (lower case).
    • Shorthand Classes:
      • \d matches ASCII digits [0-9], not Unicode digits.
      • \D matches everything except ASCII digits.
      • \w matches ASCII letters, digits, and underscores [A-Za-z0-9_], not Unicode letters.
      • \W matches everything except ASCII [A-Za-z0-9_].
      • \s matches whitespace.
      • \S matches everything except whitespace.
    • Unicode Support: To use Unicode properties (e.g., \p{Letter} or \p{digit}), ensure the pattern is compatible with Unicode semantics. The generated code uses the u flag in new RegExp(pattern, "u") to support this.
  11. How generated code is formatted for auditing

    master

    Code generated by @exodus/schemasafe is optimized for both performance and auditability. The generator follows these principles:

    • Readability: Generated code is formatted to be human-readable without requiring reformatting, facilitating manual audit processes.
    • Compactness: The generator prioritizes compact code to reduce output size.
    • Cleanliness: The generator code itself is kept clean to allow for auditing of the generation logic.
    • Optimization: To prevent output pollution, empty code branches are typically removed during the generation process.