Skip to content

Commit

Permalink
Fix SolidCache JSON sanitisation
Browse files Browse the repository at this point in the history
The issue lies on the fact that a binary MySQL string (`x"..."`)
appears before a regular MySQL string (`"..."`). This trips the
sanitiser, which recognises the leading `x` as a literal value type
indicator. After the literal value type indicator state is over,
the sanitiser forgets that it's inside a VALUES statement.

Fix this by keeping track of a "context state" to return to after
certain state transitions, instead of always returning to the
default state.

Co-authored-by: Luismi Ramirez <luismi@appsignal.com>
  • Loading branch information
unflxw and luismiramirez committed Jan 12, 2024
1 parent 5d66bdf commit ff3de0e
Showing 1 changed file with 14 additions and 4 deletions.
18 changes: 14 additions & 4 deletions src/sanitizer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Keyword, Sql, Token};

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone, Copy)]
enum State {
Default,
ComparisonOperator,
Expand Down Expand Up @@ -28,6 +28,7 @@ impl SqlSanitizer {

pub fn sanitize(mut self) -> Sql {
let mut state = State::Default;
let mut context_state = State::Default;

let mut pos = 0;
loop {
Expand Down Expand Up @@ -55,7 +56,7 @@ impl SqlSanitizer {
state = State::KeywordScopeStarted
}
Token::Keyword(_) => state = State::Keyword,
Token::LiteralValueTypeIndicator(_) => state = State::LiteralValueTypeIndicator,
Token::LiteralValueTypeIndicator(_) => {context_state = state; state = State::LiteralValueTypeIndicator},
Token::ParentheseOpen if state == State::ComparisonOperator => {
state = State::ComparisonScopeStarted
}
Expand All @@ -65,7 +66,8 @@ impl SqlSanitizer {
Token::ParentheseOpen if state == State::InsertValues => (),
Token::SquareBracketOpen if state == State::Array => state = State::ArrayStarted,
Token::ParentheseClose if state == State::InsertValues => {
state = State::InsertValuesJustClosed
state = State::InsertValuesJustClosed;
context_state = State::Default;
}
Token::Comma if state == State::InsertValuesJustClosed => (),
Token::ParentheseOpen if state == State::InsertValuesJustClosed => {
Expand Down Expand Up @@ -130,7 +132,7 @@ impl SqlSanitizer {
// Keep state the same if we're in a insert values or keyword scope state
_ if state == State::InsertValues || state == State::KeywordScopeStarted => (),
// Reset state to default if there were no matches
_ => state = State::Default,
_ => state = context_state,
}

pos += 1;
Expand Down Expand Up @@ -371,6 +373,14 @@ mod tests {
);
}

#[test]
fn test_solidcache_json() {
assert_eq!(
sanitize_string("INSERT INTO `solid_cache_entries` (`key`,`value`,`created_at`) VALUES (x'76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31', '{\"p\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31\",\"h\":{\"iv\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31\",\"at\":\"/76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31==\",\"e\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31==\",\"k\":{\"p\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31=\",\"h\":{\"iv\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31\",\"at\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31==\",\"e\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31==\"}},\"i\":\"76696577732f6974656d732f696e6465783a38323536366332313963306634393838626133333533366630623233623336382f6974656d732f31==\",\"c\":true}}', CURRENT_TIMESTAMP(6)) ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)".to_string()),
"INSERT INTO `solid_cache_entries` (`key`,`value`,`created_at`) VALUES (x?, ?, CURRENT_TIMESTAMP(?)) ON DUPLICATE KEY UPDATE `value`=VALUES(`value`)"
);
}

#[test]
fn test_case_then_else_subquery() {
assert_eq!(
Expand Down

0 comments on commit ff3de0e

Please sign in to comment.