From cfde3c1958d72a57a72617c5fed4462161c550f7 Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Tue, 8 Oct 2013 12:14:52 -0600 Subject: [PATCH] Escape backticks when quoting MySQL identifiers Previously mysql_real_escape_string() was improperly used which escapes for inclusion in a plain string rather than an identifier. Also adds basic test support for the DatabaseMysqlBase class. Co-Authored by: Antoine Musso Bug: 55427 Change-Id: Ic045e195c89d8d5d0f0edbda0cd1df781de7025c --- includes/db/DatabaseMysqlBase.php | 4 +- .../includes/db/DatabaseMysqlBaseTest.php | 128 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/includes/db/DatabaseMysqlBaseTest.php diff --git a/includes/db/DatabaseMysqlBase.php b/includes/db/DatabaseMysqlBase.php index 5614ed209b..d33d7c71be 100644 --- a/includes/db/DatabaseMysqlBase.php +++ b/includes/db/DatabaseMysqlBase.php @@ -469,7 +469,9 @@ abstract class DatabaseMysqlBase extends DatabaseBase { * @return string */ public function addIdentifierQuotes( $s ) { - return "`" . $this->strencode( $s ) . "`"; + // Characters in the range \u0001-\uFFFF are valid in a quoted identifier + // Remove NUL bytes and escape backticks by doubling + return '`' . str_replace( array( "\0", '`' ), array( '', '``' ), $s ) . '`'; } /** diff --git a/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php new file mode 100644 index 0000000000..75c48374d2 --- /dev/null +++ b/tests/phpunit/includes/db/DatabaseMysqlBaseTest.php @@ -0,0 +1,128 @@ +addIdentifierQuotes( $in ); + $this->assertEquals($expected, $quoted); + } + + + /** + * Feeds testAddIdentifierQuotes + * + * Named per bug 20281 convention. + */ + function provideDiapers() { + return array( + // Format: expected, input + array( '``', '' ), + + // Yeah I really hate loosely typed PHP idiocies nowadays + array( '``', null ), + + // Dear codereviewer, guess what addIdentifierQuotes() + // will return with thoses: + array( '``', false ), + array( '`1`', true ), + array( '`Array`', array() ), + //array( '`Object`', new stdClass() ), + // ^ Error: Object of class stdClass could not be converted to string + + // We never know what could happen + array( '`0`', 0 ), + array( '`1`', 1 ), + + // Whatchout! Should probably use something more meaningful + array( "`'`", "'" ), # single quote + array( '`"`', '"' ), # double quote + array( '````', '`' ), # backtick + array( '`’`', '’' ), # apostrophe (look at your encyclopedia) + + // sneaky NUL bytes are lurking everywhere + array( '``', "\0" ), + array( '`xyzzy`', "\0x\0y\0z\0z\0y\0" ), + + // unicode chars + array( + self::createUnicodeString( '`\u0001a\uFFFFb`' ), + self::createUnicodeString( '\u0001a\uFFFFb' ) + ), + array( + self::createUnicodeString( '`\u0001\uFFFF`' ), + self::createUnicodeString( '\u0001\u0000\uFFFF\u0000' ) + ), + array( '`☃`', '☃' ), + array( '`メインページ`', 'メインページ' ), + array( '`Басты_бет`', 'Басты_бет' ), + + // Real world: + array( '`Alix`', 'Alix' ), # while( ! $recovered ) { sleep(); } + array( '`Backtick: ```', 'Backtick: `' ), + array( '`This is a test`', 'This is a test' ), + ); + } + + private static function createUnicodeString($str) { + return json_decode( '"' . $str . '"' ); + } + +} -- 2.20.1