Regular expression injection¶
ID: rust/regex-injection
Kind: path-problem
Security severity: 7.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-020
- external/cwe/cwe-074
Query suites:
- rust-code-scanning.qls
- rust-security-extended.qls
- rust-security-and-quality.qls
Click to see the query in the CodeQL repository
Constructing a regular expression with unsanitized user input can be dangerous. A malicious user may be able to modify the meaning of the expression, causing it to match unexpected strings and construct large regular expressions by using counted repetitions.
Recommendation¶
Before embedding user input into a regular expression, escape the input string using a function such as regex::escape to escape meta-characters that have special meaning.
If purposefully supporting user supplied regular expressions, then use RegexBuilder::size_limit to limit the pattern size so that it is no larger than necessary.
Example¶
The following example constructs a regular expressions from the user input key
without escaping it first.
use regex::Regex;
fn get_value<'h>(key: &str, property: &'h str) -> Option<&'h str> {
// BAD: User provided `key` is interpolated into the regular expression.
let pattern = format!(r"^property:{key}=(.*)$");
let re = Regex::new(&pattern).unwrap();
re.captures(property)?.get(1).map(|m| m.as_str())
}
The regular expression is intended to match strings starting with "property"
such as "property:foo=bar"
. However, a malicious user might inject the regular expression ".*^|key"
and unexpectedly cause strings such as "key=secret"
to match.
If user input is used to construct a regular expression, it should be escaped first. This ensures that malicious users cannot insert characters that have special meanings in regular expressions.
use regex::{escape, Regex};
fn get_value<'h>(key: &str, property: &'h str) -> option<&'h str> {
// GOOD: User input is escaped before being used in the regular expression.
let escaped_key = escape(key);
let pattern = format!(r"^property:{escaped_key}=(.*)$");
let re = regex::new(&pattern).unwrap();
re.captures(property)?.get(1).map(|m| m.as_str())
}
References¶
regex
crate documentation: Untrusted patterns.Common Weakness Enumeration: CWE-20.
Common Weakness Enumeration: CWE-74.