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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.networknt.schema.InvalidSchemaException;
import com.networknt.schema.Error;
import com.networknt.schema.SchemaContext;

/**
* {@link RegularExpressionFactory} that allows regular expressions to be used.
Expand All @@ -34,9 +35,16 @@ public AllowRegularExpressionFactory(RegularExpressionFactory delegate, Predicat

@Override
public RegularExpression getRegularExpression(String regex) {
return getRegularExpression(regex, null);
}

@Override
public RegularExpression getRegularExpression(String regex, SchemaContext schemaContext) {
if (this.allowed.test(regex)) {
// Allowed to delegate
return this.delegate.getRegularExpression(regex);
return schemaContext != null
? this.delegate.getRegularExpression(regex, schemaContext)
: this.delegate.getRegularExpression(regex);
}
throw new InvalidSchemaException(Error.builder()
.message("Regular expression ''{0}'' is not allowed to be used.").arguments(regex).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.networknt.schema.regex;

import com.networknt.schema.SchemaContext;
import com.networknt.schema.utils.Classes;

/**
Expand Down Expand Up @@ -45,4 +46,9 @@ public static ECMAScriptRegularExpressionFactory getInstance() {
public RegularExpression getRegularExpression(String regex) {
return DELEGATE.getRegularExpression(regex);
}

@Override
public RegularExpression getRegularExpression(String regex, SchemaContext schemaContext) {
return DELEGATE.getRegularExpression(regex, schemaContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@ class GraalJSRegularExpression implements RegularExpression {
private final Value function;

GraalJSRegularExpression(String regex, GraalJSRegularExpressionContext context) {
this(regex, context, true);
}

GraalJSRegularExpression(String regex, GraalJSRegularExpressionContext context, boolean unicode) {
this.context = context;
synchronized(context.getContext()) {
this.function = context.getRegExpBuilder().execute(regex);
this.function = context.getRegExpBuilder(unicode).execute(regex);
}
}

Expand All @@ -40,4 +44,4 @@ public boolean matches(String value) {
return !function.execute(value).isNull();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
* GraalJSRegularExpressionContext.
*/
public class GraalJSRegularExpressionContext {
private static final String SOURCE = "pattern => {\n"
+ " const regex = new RegExp(pattern, 'u');\n"
private static final String SOURCE = "flags => pattern => {\n"
+ " const regex = new RegExp(pattern, flags);\n"
+ " return text => text.match(regex)\n"
+ "};";

private final Context context;
private final Value nonUnicodeRegExpBuilder;
private final Value regExpBuilder;

/**
Expand All @@ -41,7 +42,9 @@ public class GraalJSRegularExpressionContext {
public GraalJSRegularExpressionContext(Context context) {
this.context = context;
synchronized(this.context) {
this.regExpBuilder = this.context.eval("js", SOURCE);
Value builder = this.context.eval("js", SOURCE);
this.nonUnicodeRegExpBuilder = builder.execute("");
this.regExpBuilder = builder.execute("u");
}
}

Expand All @@ -63,4 +66,14 @@ public Context getContext() {
public Value getRegExpBuilder() {
return regExpBuilder;
}

/**
* Gets the RegExp builder.
*
* @param unicode whether the unicode flag should be used
* @return the regexp builder
*/
public Value getRegExpBuilder(boolean unicode) {
return unicode ? regExpBuilder : nonUnicodeRegExpBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.networknt.schema.regex;

import com.networknt.schema.SchemaContext;
import com.networknt.schema.SpecificationVersion;

import org.graalvm.polyglot.Context;

/**
Expand Down Expand Up @@ -62,4 +65,14 @@ public GraalJSRegularExpressionFactory(Context context) {
public RegularExpression getRegularExpression(String regex) {
return new GraalJSRegularExpression(regex, this.context);
}

@Override
public RegularExpression getRegularExpression(String regex, SchemaContext schemaContext) {
SpecificationVersion specificationVersion = schemaContext != null
? schemaContext.getDialect().getSpecificationVersion()
: null;
boolean unicode = specificationVersion == null
|| specificationVersion.getOrder() >= SpecificationVersion.DRAFT_2019_09.getOrder();
return new GraalJSRegularExpression(regex, this.context, unicode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public interface RegularExpression {

static RegularExpression compile(String regex, SchemaContext schemaContext) {
if (null == regex) return s -> true;
return schemaContext.getSchemaRegistryConfig().getRegularExpressionFactory().getRegularExpression(regex);
return schemaContext.getSchemaRegistryConfig().getRegularExpressionFactory().getRegularExpression(regex,
schemaContext);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.networknt.schema.regex;

import com.networknt.schema.SchemaContext;

/**
* Factory for {@link RegularExpression}.
*/
Expand All @@ -26,4 +28,15 @@ public interface RegularExpressionFactory {
* @return the regular expression
*/
RegularExpression getRegularExpression(String regex);

/**
* Gets a {@link RegularExpression} in the context of the schema being evaluated.
*
* @param regex the regular expression text value
* @param schemaContext the schema context
* @return the regular expression
*/
default RegularExpression getRegularExpression(String regex, SchemaContext schemaContext) {
return getRegularExpression(regex);
}
Comment thread
stevehu marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
package com.networknt.schema.regex;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.junit.jupiter.api.Test;

import com.networknt.schema.Error;
import com.networknt.schema.InputFormat;
import com.networknt.schema.OutputFormat;
import com.networknt.schema.Schema;
import com.networknt.schema.SchemaRegistry;
import com.networknt.schema.SchemaRegistryConfig;
Expand All @@ -32,6 +34,19 @@
* RegularExpressionTest.
*/
public class RegularExpressionTest {
private static final String DRAFT4_REGEX_FORMAT_SCHEMA = "{\r\n"
+ " \"$schema\": \"http://json-schema.org/draft-04/schema#\",\r\n"
+ " \"format\": \"regex\"\r\n"
+ "}";

private static final String DRAFT7_REGEX_FORMAT_SCHEMA = "{\r\n"
+ " \"$schema\": \"http://json-schema.org/draft-07/schema#\",\r\n"
+ " \"format\": \"regex\"\r\n"
+ "}";

private static final String LEGACY_IDENTITY_ESCAPE_REGEX = "\"\\\\a\"";
private static final String ISSUE_1248_REGEX = "\"\\\\d+:\\\\[1-9]+|N\"";

@Test
public void testInvalidRegexValidatorECMA262() throws Exception {
SchemaRegistryConfig schemaRegistryConfig = SchemaRegistryConfig.builder()
Expand All @@ -41,9 +56,44 @@ public void testInvalidRegexValidatorECMA262() throws Exception {
Schema schema = schemaRegistry.getSchema("{\r\n"
+ " \"format\": \"regex\"\r\n"
+ "}");
List<Error> errors = schema.validate("\"\\\\a\"", InputFormat.JSON, executionContext -> {
List<Error> errors = schema.validate(LEGACY_IDENTITY_ESCAPE_REGEX, InputFormat.JSON, executionContext -> {
executionContext.executionConfig(executionConfig -> executionConfig.formatAssertionsEnabled(true));
});
assertFalse(errors.isEmpty());
}
}

@Test
public void testDraft4RegexFormatValidatorUsesNonUnicodeECMA262() {
assertRegexFormatValid(GraalJSRegularExpressionFactory.getInstance(), DRAFT4_REGEX_FORMAT_SCHEMA,
LEGACY_IDENTITY_ESCAPE_REGEX);
assertRegexFormatValid(GraalJSRegularExpressionFactory.getInstance(), DRAFT4_REGEX_FORMAT_SCHEMA,
ISSUE_1248_REGEX);
}

@Test
public void testDraft7RegexFormatValidatorUsesNonUnicodeECMA262() {
assertRegexFormatValid(GraalJSRegularExpressionFactory.getInstance(), DRAFT7_REGEX_FORMAT_SCHEMA,
ISSUE_1248_REGEX);
}

@Test
public void testDelegatingRegularExpressionFactoriesPreserveSchemaContext() {
assertRegexFormatValid(ECMAScriptRegularExpressionFactory.getInstance(), DRAFT4_REGEX_FORMAT_SCHEMA,
ISSUE_1248_REGEX);
assertRegexFormatValid(new AllowRegularExpressionFactory(GraalJSRegularExpressionFactory.getInstance(),
regex -> true), DRAFT4_REGEX_FORMAT_SCHEMA, ISSUE_1248_REGEX);
}

private static void assertRegexFormatValid(RegularExpressionFactory regularExpressionFactory, String schemaText,
String valueText) {
SchemaRegistryConfig schemaRegistryConfig = SchemaRegistryConfig.builder()
.regularExpressionFactory(regularExpressionFactory).build();
SchemaRegistry schemaRegistry = SchemaRegistry.withDefaultDialect(Dialects.getDraft202012(),
builder -> builder.schemaRegistryConfig(schemaRegistryConfig));
Schema schema = schemaRegistry.getSchema(schemaText);
boolean valid = schema.validate(valueText, InputFormat.JSON, OutputFormat.BOOLEAN,
executionContext -> executionContext.executionConfig(
executionConfig -> executionConfig.formatAssertionsEnabled(true)));
assertTrue(valid);
}
}
Comment thread
stevehu marked this conversation as resolved.
Loading