Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,6 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0
with:
arch: x64

- name: Configure
run: cmake -S . -B out/build-msvc -A x64

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases follow
- Correct schema Unicode-length counting, numeric-bound and `multipleOf`
precision behavior, malformed keyword handling, format validation, and
speculative combinator error reporting.
- Prevent schema-validation stack exhaustion by resolving consecutive local
references iteratively and enforcing a conservative recursive-depth ceiling.
- Make relocatable pkg-config metadata generation portable to Windows and
nested installation library directories.
- Preserve destination state and structured errors on allocation, output-budget,
and Patch/Merge Patch failures.

Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -937,12 +937,13 @@ Notes:
limits pattern and subject sizes and rejects unsafe expressions.
- `SchemaOptions` defaults `maxRegexPatternBytes` to 256,
`maxRegexSubjectBytes` to 4096, `allowUnsafeRegex` to `false`,
`maxValidationDepth` to 512, `maxRefResolutions` to 1024,
`maxValidationDepth` to 64, `maxRefResolutions` to 1024,
`maxValidationWork` to 1,000,000, `maxErrors` to 100, and
`validateFormats` to `true`. Zero removes only a regex byte limit; zero for a
validation, reference, work, or error budget retains its documented hard
ceiling. `trustedRegex()` removes only the regex limits/safety screen; reserve
it for trusted schemas and data.
ceiling. Validation depth has an absolute hard ceiling of 64, so larger
configured values are clamped to 64. `trustedRegex()` removes only the regex
limits/safety screen; reserve it for trusted schemas and data.

This is the documented pjson subset, not a complete JSON Schema draft. See
[the schema tutorial](docs/06-schema-validation.md) and the
Expand Down
25 changes: 18 additions & 7 deletions cmake/RunInstallConsumer.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ run_checked("pjson package install"
file(RENAME "${stage_prefix}" "${relocated_prefix}")

file(GLOB_RECURSE config_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/*/cmake/pjson/pjsonConfig.cmake")
"${relocated_prefix}/*/pjsonConfig.cmake")
file(GLOB_RECURSE version_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/*/cmake/pjson/pjsonConfigVersion.cmake")
"${relocated_prefix}/*/pjsonConfigVersion.cmake")
file(GLOB_RECURSE target_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/*/cmake/pjson/pjsonTargets.cmake")
"${relocated_prefix}/*/pjsonTargets.cmake")
file(GLOB_RECURSE package_cmake_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/*/cmake/pjson/*.cmake")
"${relocated_prefix}/*/pjson/*.cmake")
file(GLOB_RECURSE pc_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/*/pkgconfig/pjson.pc")
"${relocated_prefix}/*/pjson.pc")
file(GLOB_RECURSE library_files LIST_DIRECTORIES FALSE
"${relocated_prefix}/libpjson.*"
"${relocated_prefix}/libpjson*.dylib"
Expand All @@ -144,6 +144,19 @@ endforeach()
if(NOT EXISTS "${relocated_prefix}/include/pjson.h")
message(FATAL_ERROR "Installed package is missing include/pjson.h")
endif()
list(GET pc_files 0 pc_file)
get_filename_component(pc_dir "${pc_file}" DIRECTORY)
file(RELATIVE_PATH pc_dir_from_prefix "${relocated_prefix}" "${pc_dir}")
set(expected_pc_prefix ".")
cmake_path(RELATIVE_PATH expected_pc_prefix
BASE_DIRECTORY "${pc_dir_from_prefix}")
cmake_path(NORMAL_PATH expected_pc_prefix)
file(STRINGS "${pc_file}" pc_prefix_line REGEX "^prefix=")
if(NOT pc_prefix_line STREQUAL
"prefix=\${pcfiledir}/${expected_pc_prefix}")
message(FATAL_ERROR
"Installed pkg-config prefix is not relative to pcfiledir: ${pc_prefix_line}")
endif()
foreach(metadata_file IN LISTS package_cmake_files pc_files)
file(READ "${metadata_file}" metadata_contents)
string(FIND "${metadata_contents}" "${stage_prefix}" old_prefix_position)
Expand Down Expand Up @@ -183,8 +196,6 @@ if(NOT DEFINED PJSON_PKG_CONFIG_EXECUTABLE OR
find_program(PJSON_PKG_CONFIG_EXECUTABLE NAMES pkg-config pkgconf)
endif()
if(PJSON_PKG_CONFIG_EXECUTABLE)
list(GET pc_files 0 pc_file)
get_filename_component(pc_dir "${pc_file}" DIRECTORY)
run_checked("relocated pkg-config validation"
"${CMAKE_COMMAND}" -E env
"PKG_CONFIG_PATH=${pc_dir}"
Expand Down
4 changes: 3 additions & 1 deletion docs/06-schema-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pjson::SchemaOptions options;
options.maxRegexPatternBytes = 256;
options.maxRegexSubjectBytes = 4096;
options.allowUnsafeRegex = false;
options.maxValidationDepth = 512;
options.maxValidationDepth = 64;
options.maxRefResolutions = 1024;
options.maxValidationWork = 1000000;
options.maxErrors = 100;
Expand All @@ -151,6 +151,8 @@ These are the defaults. A zero regex byte limit disables that individual regex
limit and should be reserved for trusted input. Zero for the validation-depth,
reference-resolution, work, or error-count budget retains that budget's
documented hard ceiling rather than disabling it.
Validation depth has an absolute hard ceiling of 64; larger configured values
are clamped to 64 to bound native-stack use during recursive keyword evaluation.
`SchemaOptions::trustedRegex()` disables both regex byte limits and permits
unsafe regular expressions while retaining all other defaults. Set
`validateFormats = false` when known formats should act only as annotations.
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/pjson-api.dox
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@
* extend the allocator's lifetime.
*/

/**
* @struct ByteDance::pjson::SchemaOptions
* @brief Bounds schema-validation work and controls optional format checks.
*
* Recursive validation depth defaults to an absolute hard ceiling of 64. A
* zero value selects that ceiling, and larger values are clamped to it so no
* caller configuration can make recursive keyword evaluation exceed the
* conservative native-stack bound. Other zero-valued validation budgets retain their
* documented hard ceilings; only regex byte limits use zero as unlimited.
*/

/**
* @struct ByteDance::pjson::PatchOptions
* @brief Bounds JSON Patch and Merge Patch transactional amplification.
Expand Down
2 changes: 1 addition & 1 deletion examples/src/06_schema_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ int main() {
// These limits bound traversal and reference work. Known string formats,
// such as the date above, are checked because validateFormats is enabled.
pjson::SchemaOptions options;
options.maxValidationDepth = 512;
options.maxValidationDepth = 64;
options.maxRefResolutions = 1024;
options.validateFormats = true;
std::cout << "good is valid: " << (good->validate(*schema, options) ? "yes" : "no") << "\n";
Expand Down
9 changes: 7 additions & 2 deletions pjsonlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,13 @@ install(FILES
# prefix. This keeps the metadata valid after an installed tree is relocated,
# including when CMAKE_INSTALL_LIBDIR is changed to lib64 or a nested path.
set(PJSON_INSTALL_PKGCONFIGDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
file(RELATIVE_PATH PJSON_PC_PREFIX_FROM_PCFILEDIR
"/${PJSON_INSTALL_PKGCONFIGDIR}" "/")
# Keep this calculation lexical and relative. Synthesizing a root with a
# leading slash is not a native absolute path on Windows, so file(RELATIVE_PATH)
# rejects it during configuration.
set(PJSON_PC_PREFIX_FROM_PCFILEDIR ".")
cmake_path(RELATIVE_PATH PJSON_PC_PREFIX_FROM_PCFILEDIR
BASE_DIRECTORY "${PJSON_INSTALL_PKGCONFIGDIR}")
cmake_path(NORMAL_PATH PJSON_PC_PREFIX_FROM_PCFILEDIR)
configure_file(
"${CMAKE_CURRENT_LIST_DIR}/../cmake/pjson.pc.in"
"${CMAKE_CURRENT_BINARY_DIR}/pjson.pc"
Expand Down
3 changes: 2 additions & 1 deletion pjsonlib/include/pjson.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,8 @@ namespace ByteDance {
size_t maxRegexPatternBytes; // 0 = unlimited (default: 256)
size_t maxRegexSubjectBytes; // 0 = unlimited (default: 4096)
bool allowUnsafeRegex; // default false
/// Recursive depth (default 512); zero still selects the hard ceiling of 512.
/// Recursive validation depth (default and absolute hard ceiling: 64).
/// Zero selects 64, and larger values are clamped to 64.
size_t maxValidationDepth;
/// Resolved references (default 1024); zero selects the hard ceiling of 1024.
size_t maxRefResolutions;
Expand Down
Loading
Loading