Skip to content

Commit a4a6fa1

Browse files
committed
fix: Stack overflow during validation of schemas with circular $ref chains
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent 13598ba commit a4a6fa1

20 files changed

+2993
-80
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixed
66

7+
- Stack overflow during validation of schemas with circular `$ref` chains (e.g., `a``b``a`).
78
- Local `$ref` resolution within fragment-extracted external resources. [#892](https://github.com/Stranger6667/jsonschema/issues/892)
89

910
### Removed

crates/jsonschema-py/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixed
66

7+
- Stack overflow during validation of schemas with circular `$ref` chains (e.g., `a``b``a`).
78
- Local `$ref` resolution within fragment-extracted external resources. [#892](https://github.com/Stranger6667/jsonschema/issues/892)
89

910
## [0.37.3] - 2025-11-28

crates/jsonschema/src/keywords/additional_items.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
node::SchemaNode,
66
paths::{LazyLocation, Location},
77
types::{JsonType, JsonTypeSet},
8-
validator::Validate,
8+
validator::{Validate, ValidationContext},
99
};
1010
use serde_json::{Map, Value};
1111

@@ -67,6 +67,48 @@ impl Validate for AdditionalItemsObjectValidator {
6767
}
6868
Ok(())
6969
}
70+
71+
fn is_valid_ctx(&self, instance: &Value, ctx: &mut ValidationContext) -> bool {
72+
if let Value::Array(items) = instance {
73+
items
74+
.iter()
75+
.skip(self.items_count)
76+
.all(|item| self.node.is_valid_ctx(item, ctx))
77+
} else {
78+
true
79+
}
80+
}
81+
82+
fn validate_ctx<'i>(
83+
&self,
84+
instance: &'i Value,
85+
location: &LazyLocation,
86+
ctx: &mut ValidationContext,
87+
) -> Result<(), ValidationError<'i>> {
88+
if let Value::Array(items) = instance {
89+
for (idx, item) in items.iter().enumerate().skip(self.items_count) {
90+
self.node.validate_ctx(item, &location.push(idx), ctx)?;
91+
}
92+
}
93+
Ok(())
94+
}
95+
96+
fn iter_errors_ctx<'i>(
97+
&self,
98+
instance: &'i Value,
99+
location: &LazyLocation,
100+
ctx: &mut ValidationContext,
101+
) -> ErrorIterator<'i> {
102+
if let Value::Array(items) = instance {
103+
let mut errors = Vec::new();
104+
for (idx, item) in items.iter().enumerate().skip(self.items_count) {
105+
errors.extend(self.node.iter_errors_ctx(item, &location.push(idx), ctx));
106+
}
107+
ErrorIterator::from_iterator(errors.into_iter())
108+
} else {
109+
no_error()
110+
}
111+
}
70112
}
71113

72114
pub(crate) struct AdditionalItemsBooleanValidator {

0 commit comments

Comments
 (0)