Skip to content

Fix issue #180: Prevents recursive parsing of $ref references. #181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 7, 2019
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
40 changes: 40 additions & 0 deletions src/main/java/com/networknt/schema/JsonSchemaRef.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.networknt.schema;

import java.util.Set;

import com.fasterxml.jackson.databind.JsonNode;

/**
* Use this object instead a JsonSchema for references.
*
* This reference may be empty (if the reference is being parsed) or with data (after the reference has been parsed),
* helping to prevent recursive reference to cause an infinite loop.
*/

public class JsonSchemaRef {

private JsonSchema schema;
private ValidationContext validationContext;
private String refValue;

public JsonSchemaRef(ValidationContext validationContext, String refValue) {
this.validationContext = validationContext;
this.refValue = refValue;
}

public JsonSchemaRef(JsonSchema schema) {
this.schema = schema;
}

public void set(JsonSchema schema) {
this.schema = schema;
}

public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
return schema.validate(node, rootNode, at);
}

public JsonSchema getSchema() {
return schema;
}
}
24 changes: 12 additions & 12 deletions src/main/java/com/networknt/schema/OneOfValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private static class ShortcutValidator {
ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema,
ValidationContext validationContext, JsonSchema schema) {
JsonNode refNode = schemaNode.get(ValidatorTypeCode.REF.getValue());
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext,refNode.textValue()) : null;
JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext,refNode.textValue()).getSchema() : null;
this.constants = extractConstants(schemaNode, resolvedRefSchema);
this.schema = schema;
}
Expand All @@ -61,13 +61,13 @@ private Map<String, String> extractConstants(JsonNode schemaNode, JsonSchema res
joined.putAll(refMap);
return joined;
}

private Map<String, String> extractConstants(JsonNode schemaNode) {
Map<String, String> result = new HashMap<String, String>();
if (!schemaNode.isObject()) {
return result;
}

JsonNode propertiesNode = schemaNode.get("properties");
if (propertiesNode == null || !propertiesNode.isObject()) {
return result;
Expand All @@ -81,7 +81,7 @@ private Map<String, String> extractConstants(JsonNode schemaNode) {
result.put(fieldName, constantFieldValue);
}
}
return result;
return result;
}
private String getConstantFieldValue(JsonNode jsonNode) {
if (jsonNode == null || !jsonNode.isObject() || !jsonNode.has("enum")) {
Expand All @@ -100,7 +100,7 @@ private String getConstantFieldValue(JsonNode jsonNode) {
}
return valueNode.textValue();
}

public boolean allConstantsMatch(JsonNode node) {
for (Map.Entry<String, String> e: constants.entrySet()) {
JsonNode valueNode = node.get(e.getKey());
Expand Down Expand Up @@ -130,15 +130,15 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS

public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
debug(logger, node, rootNode, at);

// this validator considers a missing node as an error
// set it here to true, however re-set it to its original value upon finishing the validation
boolean missingNodeAsError = config.isMissingNodeAsError();
config.setMissingNodeAsError(true);

int numberOfValidSchema = 0;
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();

for (ShortcutValidator validator : schemas) {
if (!validator.allConstantsMatch(node)) {
// take a shortcut: if there is any constant that does not match,
Expand All @@ -164,11 +164,11 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
}
}
}

if (numberOfValidSchema == 0) {
for (Iterator<ValidationMessage> it = errors.iterator(); it.hasNext();) {
ValidationMessage msg = it.next();

if (ValidatorTypeCode.ADDITIONAL_PROPERTIES.getValue().equals(msg.getType())) {
it.remove();
}
Expand All @@ -181,10 +181,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
if (numberOfValidSchema > 1) {
errors = Collections.singleton(buildValidationMessage(at, ""));
}

// reset the flag for error handling
config.setMissingNodeAsError(missingNodeAsError);

return Collections.unmodifiableSet(errors);
}

Expand Down
19 changes: 13 additions & 6 deletions src/main/java/com/networknt/schema/RefValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
public class RefValidator extends BaseJsonValidator implements JsonValidator {
private static final Logger logger = LoggerFactory.getLogger(RefValidator.class);

protected JsonSchema schema;
protected JsonSchemaRef schema;

private static final String REF_CURRENT = "#";

public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
Expand All @@ -44,7 +44,7 @@ public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSch
}
}

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

if (index < 0) {
return parentSchema.findAncestor();
return new JsonSchemaRef(parentSchema.findAncestor());
} else {
refValue = refValue.substring(index);
}
}
if (refValue.equals(REF_CURRENT)) {
return parentSchema.findAncestor();
return new JsonSchemaRef(parentSchema.findAncestor());
} else {
JsonNode node = parentSchema.getRefSchemaNode(refValue);
if (node != null) {
return new JsonSchema(validationContext, refValue, parentSchema.getCurrentUri(), node, parentSchema);
JsonSchemaRef ref = validationContext.getReferenceParsingInProgress(refValue);
if (ref == null) {
ref = new JsonSchemaRef(validationContext, refValue);
validationContext.setReferenceParsingInProgress(refValue, ref);
JsonSchema ret = new JsonSchema(validationContext, refValue, parentSchema.getCurrentUri(), node, parentSchema);
ref.set(ret);
}
return ref;
}
}
return null;
Expand Down
21 changes: 17 additions & 4 deletions src/main/java/com/networknt/schema/ValidationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package com.networknt.schema;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.uri.URIFactory;

Expand All @@ -24,7 +27,8 @@ public class ValidationContext {
private final JsonMetaSchema metaSchema;
private final JsonSchemaFactory jsonSchemaFactory;
private SchemaValidatorsConfig config;

private final Map<String, JsonSchemaRef> refParsingInProgress = new HashMap<>();

public ValidationContext(URIFactory uriFactory, JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchemaFactory) {
if (uriFactory == null) {
throw new IllegalArgumentException("URIFactory must not be null");
Expand All @@ -39,16 +43,16 @@ public ValidationContext(URIFactory uriFactory, JsonMetaSchema metaSchema, JsonS
this.metaSchema = metaSchema;
this.jsonSchemaFactory = jsonSchemaFactory;
}

public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
JsonSchema parentSchema) {
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema);
}

public URIFactory getURIFactory() {
return this.uriFactory;
}

public JsonSchemaFactory getJsonSchemaFactory() {
return jsonSchemaFactory;
}
Expand All @@ -60,4 +64,13 @@ public SchemaValidatorsConfig getConfig() {
public void setConfig(SchemaValidatorsConfig config) {
this.config = config;
}

public void setReferenceParsingInProgress(String refValue, JsonSchemaRef ref) {
refParsingInProgress.put(refValue, ref);
}

public JsonSchemaRef getReferenceParsingInProgress(String refValue) {
return refParsingInProgress.get(refValue);
}

}