Skip to content

Commit 9ead708

Browse files
authored
Merge pull request #181 from davidvisiedo/fix/180
Fix issue #180: Prevents recursive parsing of $ref references.
2 parents 1de8822 + b67154b commit 9ead708

File tree

4 files changed

+82
-22
lines changed

4 files changed

+82
-22
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.networknt.schema;
2+
3+
import java.util.Set;
4+
5+
import com.fasterxml.jackson.databind.JsonNode;
6+
7+
/**
8+
* Use this object instead a JsonSchema for references.
9+
*
10+
* This reference may be empty (if the reference is being parsed) or with data (after the reference has been parsed),
11+
* helping to prevent recursive reference to cause an infinite loop.
12+
*/
13+
14+
public class JsonSchemaRef {
15+
16+
private JsonSchema schema;
17+
private ValidationContext validationContext;
18+
private String refValue;
19+
20+
public JsonSchemaRef(ValidationContext validationContext, String refValue) {
21+
this.validationContext = validationContext;
22+
this.refValue = refValue;
23+
}
24+
25+
public JsonSchemaRef(JsonSchema schema) {
26+
this.schema = schema;
27+
}
28+
29+
public void set(JsonSchema schema) {
30+
this.schema = schema;
31+
}
32+
33+
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
34+
return schema.validate(node, rootNode, at);
35+
}
36+
37+
public JsonSchema getSchema() {
38+
return schema;
39+
}
40+
}

src/main/java/com/networknt/schema/OneOfValidator.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private static class ShortcutValidator {
4242
ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema,
4343
ValidationContext validationContext, JsonSchema schema) {
4444
JsonNode refNode = schemaNode.get(ValidatorTypeCode.REF.getValue());
45-
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext,refNode.textValue()) : null;
45+
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext,refNode.textValue()).getSchema() : null;
4646
this.constants = extractConstants(schemaNode, resolvedRefSchema);
4747
this.schema = schema;
4848
}
@@ -61,13 +61,13 @@ private Map<String, String> extractConstants(JsonNode schemaNode, JsonSchema res
6161
joined.putAll(refMap);
6262
return joined;
6363
}
64-
64+
6565
private Map<String, String> extractConstants(JsonNode schemaNode) {
6666
Map<String, String> result = new HashMap<String, String>();
6767
if (!schemaNode.isObject()) {
6868
return result;
6969
}
70-
70+
7171
JsonNode propertiesNode = schemaNode.get("properties");
7272
if (propertiesNode == null || !propertiesNode.isObject()) {
7373
return result;
@@ -81,7 +81,7 @@ private Map<String, String> extractConstants(JsonNode schemaNode) {
8181
result.put(fieldName, constantFieldValue);
8282
}
8383
}
84-
return result;
84+
return result;
8585
}
8686
private String getConstantFieldValue(JsonNode jsonNode) {
8787
if (jsonNode == null || !jsonNode.isObject() || !jsonNode.has("enum")) {
@@ -100,7 +100,7 @@ private String getConstantFieldValue(JsonNode jsonNode) {
100100
}
101101
return valueNode.textValue();
102102
}
103-
103+
104104
public boolean allConstantsMatch(JsonNode node) {
105105
for (Map.Entry<String, String> e: constants.entrySet()) {
106106
JsonNode valueNode = node.get(e.getKey());
@@ -130,15 +130,15 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
130130

131131
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
132132
debug(logger, node, rootNode, at);
133-
133+
134134
// this validator considers a missing node as an error
135135
// set it here to true, however re-set it to its original value upon finishing the validation
136136
boolean missingNodeAsError = config.isMissingNodeAsError();
137137
config.setMissingNodeAsError(true);
138-
138+
139139
int numberOfValidSchema = 0;
140140
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
141-
141+
142142
for (ShortcutValidator validator : schemas) {
143143
if (!validator.allConstantsMatch(node)) {
144144
// take a shortcut: if there is any constant that does not match,
@@ -164,11 +164,11 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
164164
}
165165
}
166166
}
167-
167+
168168
if (numberOfValidSchema == 0) {
169169
for (Iterator<ValidationMessage> it = errors.iterator(); it.hasNext();) {
170170
ValidationMessage msg = it.next();
171-
171+
172172
if (ValidatorTypeCode.ADDITIONAL_PROPERTIES.getValue().equals(msg.getType())) {
173173
it.remove();
174174
}
@@ -181,10 +181,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
181181
if (numberOfValidSchema > 1) {
182182
errors = Collections.singleton(buildValidationMessage(at, ""));
183183
}
184-
184+
185185
// reset the flag for error handling
186186
config.setMissingNodeAsError(missingNodeAsError);
187-
187+
188188
return Collections.unmodifiableSet(errors);
189189
}
190190

src/main/java/com/networknt/schema/RefValidator.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
public class RefValidator extends BaseJsonValidator implements JsonValidator {
3131
private static final Logger logger = LoggerFactory.getLogger(RefValidator.class);
3232

33-
protected JsonSchema schema;
34-
33+
protected JsonSchemaRef schema;
34+
3535
private static final String REF_CURRENT = "#";
3636

3737
public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
@@ -44,7 +44,7 @@ public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSch
4444
}
4545
}
4646

47-
static JsonSchema getRefSchema(JsonSchema parentSchema, ValidationContext validationContext, String refValue) {
47+
static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext validationContext, String refValue) {
4848
if (!refValue.startsWith(REF_CURRENT)) {
4949
// This will be the uri extracted from the refValue (this may be a relative or absolute uri).
5050
final String refUri;
@@ -66,17 +66,24 @@ static JsonSchema getRefSchema(JsonSchema parentSchema, ValidationContext valida
6666
parentSchema = validationContext.getJsonSchemaFactory().getSchema(schemaUri, validationContext.getConfig());
6767

6868
if (index < 0) {
69-
return parentSchema.findAncestor();
69+
return new JsonSchemaRef(parentSchema.findAncestor());
7070
} else {
7171
refValue = refValue.substring(index);
7272
}
7373
}
7474
if (refValue.equals(REF_CURRENT)) {
75-
return parentSchema.findAncestor();
75+
return new JsonSchemaRef(parentSchema.findAncestor());
7676
} else {
7777
JsonNode node = parentSchema.getRefSchemaNode(refValue);
7878
if (node != null) {
79-
return new JsonSchema(validationContext, refValue, parentSchema.getCurrentUri(), node, parentSchema);
79+
JsonSchemaRef ref = validationContext.getReferenceParsingInProgress(refValue);
80+
if (ref == null) {
81+
ref = new JsonSchemaRef(validationContext, refValue);
82+
validationContext.setReferenceParsingInProgress(refValue, ref);
83+
JsonSchema ret = new JsonSchema(validationContext, refValue, parentSchema.getCurrentUri(), node, parentSchema);
84+
ref.set(ret);
85+
}
86+
return ref;
8087
}
8188
}
8289
return null;

src/main/java/com/networknt/schema/ValidationContext.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.networknt.schema;
1818

19+
import java.util.HashMap;
20+
import java.util.Map;
21+
1922
import com.fasterxml.jackson.databind.JsonNode;
2023
import com.networknt.schema.uri.URIFactory;
2124

@@ -24,7 +27,8 @@ public class ValidationContext {
2427
private final JsonMetaSchema metaSchema;
2528
private final JsonSchemaFactory jsonSchemaFactory;
2629
private SchemaValidatorsConfig config;
27-
30+
private final Map<String, JsonSchemaRef> refParsingInProgress = new HashMap<>();
31+
2832
public ValidationContext(URIFactory uriFactory, JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchemaFactory) {
2933
if (uriFactory == null) {
3034
throw new IllegalArgumentException("URIFactory must not be null");
@@ -39,16 +43,16 @@ public ValidationContext(URIFactory uriFactory, JsonMetaSchema metaSchema, JsonS
3943
this.metaSchema = metaSchema;
4044
this.jsonSchemaFactory = jsonSchemaFactory;
4145
}
42-
46+
4347
public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
4448
JsonSchema parentSchema) {
4549
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema);
4650
}
47-
51+
4852
public URIFactory getURIFactory() {
4953
return this.uriFactory;
5054
}
51-
55+
5256
public JsonSchemaFactory getJsonSchemaFactory() {
5357
return jsonSchemaFactory;
5458
}
@@ -60,4 +64,13 @@ public SchemaValidatorsConfig getConfig() {
6064
public void setConfig(SchemaValidatorsConfig config) {
6165
this.config = config;
6266
}
67+
68+
public void setReferenceParsingInProgress(String refValue, JsonSchemaRef ref) {
69+
refParsingInProgress.put(refValue, ref);
70+
}
71+
72+
public JsonSchemaRef getReferenceParsingInProgress(String refValue) {
73+
return refParsingInProgress.get(refValue);
74+
}
75+
6376
}

0 commit comments

Comments
 (0)