Skip to content

Commit 2bbc796

Browse files
committed
Introduce an option to override the regex implementation
This allows use of a different (more featureful) regex implementation to do validation of fields that have a "pattern" defined.
1 parent b771380 commit 2bbc796

File tree

6 files changed

+39
-6
lines changed

6 files changed

+39
-6
lines changed

openapi3/schema.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"math"
1010
"math/big"
1111
"reflect"
12-
"regexp"
1312
"sort"
1413
"strconv"
1514
"strings"
@@ -1019,7 +1018,7 @@ func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema,
10191018
}
10201019
}
10211020
if !validationOpts.schemaPatternValidationDisabled && schema.Pattern != "" {
1022-
if _, err := schema.compilePattern(); err != nil {
1021+
if _, err := schema.compilePattern(validationOpts.regexCompilerFunc); err != nil {
10231022
return stack, err
10241023
}
10251024
}
@@ -1729,10 +1728,10 @@ func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value
17291728
// "pattern"
17301729
if !settings.patternValidationDisabled && schema.Pattern != "" {
17311730
cpiface, _ := compiledPatterns.Load(schema.Pattern)
1732-
cp, _ := cpiface.(*regexp.Regexp)
1731+
cp, _ := cpiface.(RegexMatcher)
17331732
if cp == nil {
17341733
var err error
1735-
if cp, err = schema.compilePattern(); err != nil {
1734+
if cp, err = schema.compilePattern(settings.regexCompiler); err != nil {
17361735
if !settings.multiError {
17371736
return err
17381737
}

openapi3/schema_pattern.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ func intoGoRegexp(re string) string {
1313
}
1414

1515
// NOTE: racey WRT [writes to schema.Pattern] vs [reads schema.Pattern then writes to compiledPatterns]
16-
func (schema *Schema) compilePattern() (cp *regexp.Regexp, err error) {
16+
func (schema *Schema) compilePattern(c RegexCompilerFunc) (cp RegexMatcher, err error) {
1717
pattern := schema.Pattern
18-
if cp, err = regexp.Compile(intoGoRegexp(pattern)); err != nil {
18+
if c != nil {
19+
cp, err = c(pattern)
20+
} else {
21+
cp, err = regexp.Compile(intoGoRegexp(pattern))
22+
}
23+
if err != nil {
1924
err = &SchemaError{
2025
Schema: schema,
2126
SchemaField: "pattern",
@@ -24,6 +29,7 @@ func (schema *Schema) compilePattern() (cp *regexp.Regexp, err error) {
2429
}
2530
return
2631
}
32+
2733
var _ bool = compiledPatterns.CompareAndSwap(pattern, nil, cp)
2834
return
2935
}

openapi3/schema_validation_settings.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import (
77
// SchemaValidationOption describes options a user has when validating request / response bodies.
88
type SchemaValidationOption func(*schemaValidationSettings)
99

10+
type RegexCompilerFunc func(expr string) (RegexMatcher, error)
11+
12+
type RegexMatcher interface {
13+
MatchString(s string) bool
14+
}
15+
1016
type schemaValidationSettings struct {
1117
failfast bool
1218
multiError bool
@@ -16,6 +22,8 @@ type schemaValidationSettings struct {
1622
readOnlyValidationDisabled bool
1723
writeOnlyValidationDisabled bool
1824

25+
regexCompiler RegexCompilerFunc
26+
1927
onceSettingDefaults sync.Once
2028
defaultsSet func()
2129

@@ -70,6 +78,11 @@ func SetSchemaErrorMessageCustomizer(f func(err *SchemaError) string) SchemaVali
7078
return func(s *schemaValidationSettings) { s.customizeMessageError = f }
7179
}
7280

81+
// SetSchemaRegexCompiler allows to override the regex implementation used to validate field "pattern".
82+
func SetSchemaRegexCompiler(c RegexCompilerFunc) SchemaValidationOption {
83+
return func(s *schemaValidationSettings) { s.regexCompiler = c }
84+
}
85+
7386
func newSchemaValidationSettings(opts ...SchemaValidationOption) *schemaValidationSettings {
7487
settings := &schemaValidationSettings{}
7588
for _, opt := range opts {

openapi3/validation_options.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type ValidationOptions struct {
1313
schemaFormatValidationEnabled bool
1414
schemaPatternValidationDisabled bool
1515
schemaExtensionsInRefProhibited bool
16+
regexCompilerFunc RegexCompilerFunc
1617
extraSiblingFieldsAllowed map[string]struct{}
1718
}
1819

@@ -113,6 +114,14 @@ func ProhibitExtensionsWithRef() ValidationOption {
113114
}
114115
}
115116

117+
// SetRegexCompiler allows to override the regex implementation used to validate
118+
// field "pattern".
119+
func SetRegexCompiler(c RegexCompilerFunc) ValidationOption {
120+
return func(options *ValidationOptions) {
121+
options.regexCompilerFunc = c
122+
}
123+
}
124+
116125
// WithValidationOptions allows adding validation options to a context object that can be used when validating any OpenAPI type.
117126
func WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context {
118127
if len(opts) == 0 {

openapi3filter/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type Options struct {
2525

2626
MultiError bool
2727

28+
// Set RegexCompiler to override the regex implementation
29+
RegexCompiler openapi3.RegexCompilerFunc
30+
2831
// A document with security schemes defined will not pass validation
2932
// unless an AuthenticationFunc is defined.
3033
// See NoopAuthenticationFunc

openapi3filter/validate_request.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,9 @@ func ValidateRequestBody(ctx context.Context, input *RequestValidationInput, req
316316
if options.ExcludeReadOnlyValidations {
317317
opts = append(opts, openapi3.DisableReadOnlyValidation())
318318
}
319+
if options.RegexCompiler != nil {
320+
opts = append(opts, openapi3.SetSchemaRegexCompiler(options.RegexCompiler))
321+
}
319322

320323
// Validate JSON with the schema
321324
if err := contentType.Schema.Value.VisitJSON(value, opts...); err != nil {

0 commit comments

Comments
 (0)