Werdna has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/100521


Change subject: Add function for checking options arrays for SQL injection
......................................................................

Add function for checking options arrays for SQL injection

Now used anywhere where external options are accepted

Change-Id: I8749aab2242fa29e4f3f47af07a7427cd3eefaa4
---
M includes/Block/TopicList.php
M includes/Data/ObjectManager.php
M includes/Data/RevisionStorage.php
3 files changed, 87 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow 
refs/changes/21/100521/1

diff --git a/includes/Block/TopicList.php b/includes/Block/TopicList.php
index 505b6ee..974a3be 100644
--- a/includes/Block/TopicList.php
+++ b/includes/Block/TopicList.php
@@ -62,6 +62,11 @@
                $storage = $this->storage;
                $defStorage = $this->storage->getStorage( 'Definition' );
                $sourceDef = $defStorage->get( 
$this->workflow->getDefinitionId() );
+
+               if ( ! $sourceDef ) {
+                       throw new \MWException( "Unable to retrieve definition 
for this workflow" );
+               }
+
                $topicDef = $defStorage->get( $sourceDef->getOption( 
'topic_definition_id' ) );
                if ( !$topicDef ) {
                        throw new \MWException( 'Invalid definition owns this 
TopicList, needs a valid topic_definition_id option assigned' );
diff --git a/includes/Data/ObjectManager.php b/includes/Data/ObjectManager.php
index 4e01c48..61a10f1 100644
--- a/includes/Data/ObjectManager.php
+++ b/includes/Data/ObjectManager.php
@@ -663,12 +663,83 @@
                                return true;
                        }
 
-                       if ( ! preg_match( '/^[A-Za-z0-9\._]+$/', $key ) ) {
+                       if ( ! preg_match( '/^' . $this->getFieldRegex() . 
'$/', $key ) ) {
                                return true;
                        }
                }
 
                return false;
+       }
+
+       /**
+        * Returns a regular expression fragment suitable for matching a valid
+        * SQL field name, and hopefully no injection attacks
+        * @return string Regular expression fragment
+        */
+       protected function getFieldRegexFragment() {
+               return '\s*[A-Za-z0-9\._]+\s*';
+       }
+
+       /**
+        * Internal security function to check an options array for
+        * SQL injection and other funkiness
+        * @todo Currently only supports LIMIT, OFFSET and ORDER BY
+        * @param  array $options An options array passed to a query.
+        * @return boolean
+        */
+       protected function validateOptions( $options ) {
+               static $validUnaryOptions = array(
+                       'UNIQUE',
+                       'EXPLAIN',
+               );
+
+               $fieldRegex = $this->getFieldRegexFragment();
+
+               foreach( $options as $key => $value ) {
+                       if ( is_numeric( $key ) && in_array( strtoupper( $value 
), $validUnaryOptions ) ) {
+                               continue;
+                       } elseif ( is_numeric( $key ) ) {
+                               wfDebug( __METHOD__.": Unrecognised unary 
operator $value\n" );
+                               return false;
+                       }
+
+                       if ( $key === 'LIMIT' ) {
+                               // LIMIT is one or two integers, separated by a 
comma.
+                               if ( ! preg_match ( '/^\d+(,\d+)?$/', $value ) 
) {
+                                       wfDebug( __METHOD__.": Invalid LIMIT 
$value\n" );
+                                       return false;
+                               }
+                       } elseif ( $key === 'ORDER BY' ) {
+                               // ORDER BY is a list of field names with ASC / 
DESC afterwards
+                               if ( is_string( $value ) ) {
+                                       $value = explode( ',', $value );
+                               }
+                               $orderByRegex = 
"/^\s*$fieldRegex\s*(ASC|DESC)?\s*$/i";
+
+                               foreach( $value as $orderByField ) {
+                                       if ( ! preg_match( $orderByRegex, 
$orderByField ) ) {
+                                               wfDebug( __METHOD__.": invalid 
ORDER BY field $orderByField\n" );
+                                               return false;
+                                       }
+                               }
+                       } elseif ( $key === 'OFFSET' ) {
+                               // OFFSET is just an integer
+                               if ( ! is_numeric( $value ) ) {
+                                       wfDebug( __METHOD__.": non-numeric 
offset $offset\n" );
+                                       return false;
+                               }
+                       } elseif ( $key === 'GROUP BY' ) {
+                               if ( ! preg_match( 
"/^{$fieldRegex}(,{$fieldRegex})+$/", $value ) ) {
+                                       wfDebug( __METHOD__.": invalid GROUP BY 
field\n" );
+                               }
+                       } else {
+                               wfDebug( __METHOD__.": Unknown option $key\n" );
+                               return false;
+                       }
+               }
+
+               // Everything passes
+               return true;
        }
 }
 
@@ -765,6 +836,10 @@
 
                $attributes = $this->preprocessSqlArray( $attributes );
 
+               if ( ! $this->validateOptions( $options ) ) {
+                       throw new MWException( "Validation error in database 
options" );
+               }
+
                $res = $this->dbFactory->getDB( DB_MASTER )->select(
                        $this->table,
                        '*',
diff --git a/includes/Data/RevisionStorage.php 
b/includes/Data/RevisionStorage.php
index 4843c45..4349bd4 100644
--- a/includes/Data/RevisionStorage.php
+++ b/includes/Data/RevisionStorage.php
@@ -7,6 +7,7 @@
 use Flow\Repository\TreeRepository;
 use DatabaseBase;
 use ExternalStore;
+use MWException;
 use User;
 
 abstract class RevisionStorage extends DbStorage {
@@ -44,6 +45,11 @@
 
        protected function findInternal( array $attributes, array $options = 
array() ) {
                $dbr = $this->dbFactory->getDB( DB_MASTER );
+
+               if ( ! $this->validateOptions( $options ) ) {
+                       throw new MWException( "Validation error in database 
options" );
+               }
+
                $res = $dbr->select(
                        array( $this->joinTable(), 'rev' => 'flow_revision' ),
                        '*',

-- 
To view, visit https://gerrit.wikimedia.org/r/100521
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8749aab2242fa29e4f3f47af07a7427cd3eefaa4
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Flow
Gerrit-Branch: master
Gerrit-Owner: Werdna <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to