Loading…
Loading…
Configure automatic regex pattern generation for string fields in OpenAPI specs. Learn about policy modes, pattern strategies, and safety checks.
This document describes the system for automatically generating pattern attributes for string fields in OpenAPI specifications. This feature addresses the issue of unconstrained string fields in API definitions, which can pose data quality and security risks.
When an API specification is generated, this system can automatically add a regular expression pattern to any string field in a request body that does not already have one. This process is controlled by a system-wide setting and several configurable strategies.
The primary goals of this system are:
The pattern generation logic is applied server-side during specification generation. This means the original, immutable API mapping is not modified; the pattern is added to the published OpenAPI specification that API consumers and gateways use.
The behavior of the automatic pattern generation is governed by the conversion_defaults_string_without_regex system setting. This setting accepts one of four possible modes. The default mode is auto-apply-silent.
| Mode | Description |
|---|---|
auto-apply-silent | (Default) Automatically adds a derived pattern to every unconstrained string field in the requestBody of the generated OpenAPI specification. The pattern is derived based on the configured pattern strategy. |
notify-and-suggest | Highlights unconstrained string fields in the UI and suggests applying the generic regular expression on a per-field basis. |
notify-only | Highlights unconstrained string fields in the UI but does not suggest or apply any patterns automatically. |
none | Disables all features related to default string patterns. No fields are highlighted, and no patterns are applied. |
When the policy mode is set to auto-apply-silent, the system uses a defined strategy to derive the appropriate regular expression for each unconstrained string field.
The pattern generation logic is encapsulated in the derivePattern function. It determines the regex based on the chosen strategy and the field's existing minLength and maxLength constraints.
The process follows these steps:
flowchart TD
subgraph Pattern Derivation Logic
direction LR
Input[("Inputs: <br/>- Strategy<br/>- Generic Regex<br/>- minLength<br/>- maxLength")] --> A{Strategy?};
A -- "generic" --> B{Generic Regex<br/>is provided?};
B -- Yes --> C[Use provided<br/>Generic Regex];
B -- No --> D[Use default:<br/><code>^.{0,1000}$</code>];
C --> Output([Derived Pattern]);
D --> Output;
A -- "alphanumeric-length" --> E[Set character class:<br/><code>[0-9a-zA-Z]</code>];
A -- "length-bound" (Default) --> F[Set character class:<br/><code>[\\s\\S]</code>];
E --> G{maxLength > 1000?};
F --> G;
G -- Yes --> H[Generate unbounded pattern:<br/><code>^<char-class>*$</code>];
G -- No --> I[Generate bounded pattern:<br/><code>^<char-class>{min,max}$</code>];
H --> Output;
I --> Output;
endThe system supports three strategies for deriving patterns. The default strategy is length-bound.
| Strategy | Default | Description |
|---|---|---|
length-bound | Yes | Generates a regex based on the field's minLength and maxLength facets. The pattern is ^[\s\S]{min,max}$. This strategy accepts any character, including newlines and Unicode characters, ensuring that runtime validation behavior does not change from what maxLength already enforces. |
alphanumeric-length | No | Generates a stricter regex, ^[0-9a-zA-Z]{min,max}$, based on the field's length facets. This pattern only accepts alphanumeric characters and rejects spaces, punctuation, and non-Latin text. This can introduce stricter validation at runtime. |
generic | No | Applies a single, publisher-defined generic regular expression to all unconstrained string fields. If no custom regex is provided, it uses the system default (^.{0,1000}$). |
IMPORTANT
The alphanumeric-length strategy introduces stricter validation than a simple maxLength constraint. If the runtime enforcement mode is active, API calls with strings containing spaces, punctuation, or non-Latin characters will be rejected.
To prevent the use of insecure or non-portable regular expressions, the system validates any custom genericRegex supplied by an API publisher. A regex is considered unsafe if it is likely to perform poorly (e.g., via catastrophic backtracking) or is incompatible with downstream components like RE2-based API gateways.
The regexSafetyProblem function performs these checks. If the regex is safe, the function returns null. If it is unsafe, it returns a string describing the problem.
The following conditions will cause a regex to be rejected:
1000 characters (MAX_REGEX_LENGTH).java.util.regex.Pattern engine.\1 or \k<name> are not supported.\Q...\E quoting: This syntax is not supported.(?=...), (?!...), (?<=...), and (?<!...) are not supported.(?>...) is not supported.(?x) flag is not supported.*+, ++, ?+, }+ are not supported.{n,m} where n or m is greater than 1000 (MAX_DERIVED_QUANTIFIER) are rejected. This is to maintain compatibility with gateways that have a lower quantifier limit.(a+)+, (a*)*) is rejected. This structure is a primary cause of catastrophic backtracking.NOTE
The safety check is a robust guard, not a formal proof of safety. It does not detect all potential issues, such as alternation overlap (e.g., (a|aa)+). API publishers should still exercise caution when writing complex regular expressions.
The following table lists the key constants and default values for the pattern generation system.
| Key | Default Value | Description |
|---|---|---|
SETTING_KEY | conversion_defaults_string_without_regex | The key for the system setting that controls the policy mode. |
DEFAULT_MODE | auto-apply-silent | The default policy mode applied to new installations. |
DEFAULT_PATTERN_STRATEGY | length-bound | The default strategy used for deriving patterns. |
DEFAULT_GENERIC_REGEX | ^.{0,1000}$ | The seed default generic regex. It is Unicode-permissive and length-bound to 1000 characters. |
MAX_DERIVED_QUANTIFIER | 1000 | The largest quantifier bound (e.g., in {min,max}) that will be emitted in a derived pattern. If a field's maxLength exceeds this, an unbounded * quantifier is used instead. |
MAX_REGEX_LENGTH | 1000 | The maximum permitted length for a custom genericRegex. |
The system exposes two core functions for its logic.
derivePatternDerives a regular expression pattern for an unconstrained string field.
Signature:
String derivePattern(String strategy, String genericRegex, Integer minLength, Integer maxLength)
Parameters:
| Name | Type | Description |
|---|---|---|
strategy | String | The pattern strategy to use. One of length-bound, alphanumeric-length, or generic. If null, defaults to length-bound. |
genericRegex | String | The custom regex to use when strategy is generic. |
minLength | Integer | The minLength constraint of the string field. Defaults to 0 if null, negative, or greater than maxLength. |
maxLength | Integer | The maxLength constraint of the string field. |
Returns: A String containing the derived regular expression. If maxLength is null, negative, or greater than MAX_DERIVED_QUANTIFIER (1000), the derived pattern will use an unbounded quantifier (*).
regexSafetyProblemValidates a regular expression to ensure it is safe and portable.
Signature:
String regexSafetyProblem(String regex)
Parameters:
| Name | Type | Description |
|---|---|---|
regex | String | The regular expression to validate. |
Returns: null if the regex is considered safe. Otherwise, returns a String describing the first problem found. See Generic Regex Safety Validation for a list of possible problems.
length-bound PatternGiven a string field with minLength: 10 and maxLength: 200.
Invocation:
derivePattern("length-bound", null, 10, 200);
Result: The function returns the following pattern, which accepts any character within the specified length range.
^[\s\S]{10,200}$
maxLength > 1000Given a string field with maxLength: 2000.
Invocation:
derivePattern("length-bound", null, 0, 2000);
Result:
Because maxLength exceeds MAX_DERIVED_QUANTIFIER, the function returns a pattern with an unbounded quantifier. The maxLength constraint is still enforced separately.
^[\s\S]*$
Invocation:
regexSafetyProblem("^[a-zA-Z0-9_-]{1,50}$");
Result:
null
Invocation:
regexSafetyProblem("^(a+)+$");
Result: The function identifies the nested quantifier, which can lead to catastrophic backtracking, and returns an error message.
"nested quantifiers can backtrack catastrophically"