Merge "Add string length limits"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 21 Nov 2017 15:21:26 +0000 (15:21 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 21 Nov 2017 15:21:26 +0000 (15:21 +0000)
includes/api/ApiBase.php
includes/api/ApiHelp.php
includes/api/ApiParamInfo.php
includes/api/i18n/en.json
includes/api/i18n/qqq.json
tests/phpunit/structure/ApiStructureTest.php

index bf2b977..83d2ae9 100644 (file)
@@ -217,6 +217,18 @@ abstract class ApiBase extends ContextSource {
         */
        const PARAM_ISMULTI_LIMIT2 = 22;
 
+       /**
+        * (integer) Maximum length of a string in bytes (in UTF-8 encoding).
+        * @since 1.31
+        */
+       const PARAM_MAX_BYTES = 23;
+
+       /**
+        * (integer) Maximum length of a string in characters (unicode codepoints).
+        * @since 1.31
+        */
+       const PARAM_MAX_CHARS = 24;
+
        /**@}*/
 
        const ALL_DEFAULT_STRING = '*';
@@ -1173,9 +1185,9 @@ abstract class ApiBase extends ContextSource {
                        );
                }
 
-               // More validation only when choices were not given
-               // choices were validated in parseMultiValue()
                if ( isset( $value ) ) {
+                       // More validation only when choices were not given
+                       // choices were validated in parseMultiValue()
                        if ( !is_array( $type ) ) {
                                switch ( $type ) {
                                        case 'NULL': // nothing to do
@@ -1285,6 +1297,23 @@ abstract class ApiBase extends ContextSource {
                                $value = array_unique( $value );
                        }
 
+                       if ( in_array( $type, [ 'NULL', 'string', 'text', 'password' ], true ) ) {
+                               foreach ( (array)$value as $val ) {
+                                       if ( isset( $paramSettings[self::PARAM_MAX_BYTES] )
+                                               && strlen( $val ) > $paramSettings[self::PARAM_MAX_BYTES]
+                                       ) {
+                                               $this->dieWithError( [ 'apierror-maxbytes', $encParamName,
+                                                       $paramSettings[self::PARAM_MAX_BYTES] ] );
+                                       }
+                                       if ( isset( $paramSettings[self::PARAM_MAX_CHARS] )
+                                               && mb_strlen( $val, 'UTF-8' ) > $paramSettings[self::PARAM_MAX_CHARS]
+                                       ) {
+                                               $this->dieWithError( [ 'apierror-maxchars', $encParamName,
+                                                       $paramSettings[self::PARAM_MAX_CHARS] ] );
+                                       }
+                               }
+                       }
+
                        // Set a warning if a deprecated parameter has been passed
                        if ( $deprecated && $value !== false ) {
                                $feature = $encParamName;
index ea4f724..529b32c 100644 (file)
@@ -713,6 +713,15 @@ class ApiHelp extends ApiBase {
                                                }
                                        }
 
+                                       if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
+                                               $info[] = $context->msg( 'api-help-param-maxbytes' )
+                                                       ->numParams( $settings[self::PARAM_MAX_BYTES] );
+                                       }
+                                       if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
+                                               $info[] = $context->msg( 'api-help-param-maxchars' )
+                                                       ->numParams( $settings[self::PARAM_MAX_CHARS] );
+                                       }
+
                                        // Add default
                                        $default = isset( $settings[ApiBase::PARAM_DFLT] )
                                                ? $settings[ApiBase::PARAM_DFLT]
index 2fa20a9..93fc51a 100644 (file)
@@ -471,6 +471,12 @@ class ApiParamInfo extends ApiBase {
                        if ( !empty( $settings[ApiBase::PARAM_RANGE_ENFORCE] ) ) {
                                $item['enforcerange'] = true;
                        }
+                       if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
+                               $item['maxbytes'] = $settings[self::PARAM_MAX_BYTES];
+                       }
+                       if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
+                               $item['maxchars'] = $settings[self::PARAM_MAX_CHARS];
+                       }
                        if ( !empty( $settings[ApiBase::PARAM_DEPRECATED_VALUES] ) ) {
                                $deprecatedValues = array_keys( $settings[ApiBase::PARAM_DEPRECATED_VALUES] );
                                if ( is_array( $item['type'] ) ) {
index dbd5451..85f17de 100644 (file)
        "api-help-param-direction": "In which direction to enumerate:\n;newer:List oldest first. Note: $1start has to be before $1end.\n;older:List newest first (default). Note: $1start has to be later than $1end.",
        "api-help-param-continue": "When more results are available, use this to continue.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(no description)</span>",
+       "api-help-param-maxbytes": "Cannot be longer than $1 {{PLURAL:$1|byte|bytes}}.",
+       "api-help-param-maxchars": "Cannot be longer than $1 {{PLURAL:$1|character|characters}}.",
        "api-help-examples": "{{PLURAL:$1|Example|Examples}}:",
        "api-help-permissions": "{{PLURAL:$1|Permission|Permissions}}:",
        "api-help-permissions-granted-to": "{{PLURAL:$1|Granted to}}: $2",
        "apierror-invalidurlparam": "Invalid value for <var>$1urlparam</var> (<kbd>$2=$3</kbd>).",
        "apierror-invaliduser": "Invalid username \"$1\".",
        "apierror-invaliduserid": "User ID <var>$1</var> is not valid.",
+       "apierror-maxbytes": "Parameter <var>$1</var> cannot be longer than $2 {{PLURAL:$2|byte|bytes}}",
+       "apierror-maxchars": "Parameter <var>$1</var> cannot be longer than $2 {{PLURAL:$2|character|characters}}",
        "apierror-maxlag-generic": "Waiting for a database server: $1 {{PLURAL:$1|second|seconds}} lagged.",
        "apierror-maxlag": "Waiting for $2: $1 {{PLURAL:$1|second|seconds}} lagged.",
        "apierror-mimesearchdisabled": "MIME search is disabled in Miser Mode.",
index 6aaaac7..3bdf7c6 100644 (file)
        "api-help-param-direction": "{{doc-apihelp-param|description=any standard \"dir\" parameter|noseealso=1}}",
        "api-help-param-continue": "{{doc-apihelp-param|description=any standard \"continue\" parameter, or other parameter with the same semantics|noseealso=1}}",
        "api-help-param-no-description": "Displayed on API parameters that lack any description",
+       "api-help-param-maxbytes": "Used to display the maximum allowed length of a parameter, in bytes.",
+       "api-help-param-maxchars": "Used to display the maximum allowed length of a parameter, in characters.",
        "api-help-examples": "Label for the API help examples section\n\nParameters:\n* $1 - Number of examples to be displayed\n{{Identical|Example}}",
        "api-help-permissions": "Label for the \"permissions\" section in the main module's help output.\n\nParameters:\n* $1 - Number of permissions displayed\n{{Identical|Permission}}",
        "api-help-permissions-granted-to": "Used to introduce the list of groups each permission is assigned to.\n\nParameters:\n* $1 - Number of groups\n* $2 - List of group names, comma-separated",
        "apierror-invalidurlparam": "{{doc-apierror}}\n\nParameters:\n* $1 - Module parameter prefix, e.g. \"bl\".\n* $2 - Key\n* $3 - Value.",
        "apierror-invaliduser": "{{doc-apierror}}\n\nParameters:\n* $1 - User name that is invalid.",
        "apierror-invaliduserid": "{{doc-apierror}}",
+       "apierror-maxbytes": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n* $2 - Maximum allowed bytes.",
+       "apierror-maxchars": "{{doc-apierror}}\n\nParameters:\n* $1 - Parameter name.\n* $2 - Maximum allowed characters.",
        "apierror-maxlag-generic": "{{doc-apierror}}\n\nParameters:\n* $1 - Database is lag in seconds.",
        "apierror-maxlag": "{{doc-apierror}}\n\nParameters:\n* $1 - Database lag in seconds.\n* $2 - Database server that is lagged.",
        "apierror-mimesearchdisabled": "{{doc-apierror}}",
index 7912f97..cbc74f4 100644 (file)
@@ -182,8 +182,7 @@ class ApiStructureTest extends MediaWikiTestCase {
 
                foreach ( [ $paramsPlain, $paramsForHelp ] as $params ) {
                        foreach ( $params as $param => $config ) {
-                               if (
-                                       isset( $config[ApiBase::PARAM_ISMULTI_LIMIT1] )
+                               if ( isset( $config[ApiBase::PARAM_ISMULTI_LIMIT1] )
                                        || isset( $config[ApiBase::PARAM_ISMULTI_LIMIT2] )
                                ) {
                                        $this->assertTrue( !empty( $config[ApiBase::PARAM_ISMULTI] ), $param
@@ -199,6 +198,15 @@ class ApiStructureTest extends MediaWikiTestCase {
                                                $config[ApiBase::PARAM_ISMULTI_LIMIT2], $param
                                                . 'PARAM_ISMULTI limit cannot be smaller for users with apihighlimits rights' );
                                }
+                               if ( isset( $config[ApiBase::PARAM_MAX_BYTES] )
+                                       || isset( $config[ApiBase::PARAM_MAX_CHARS] )
+                               ) {
+                                       $default = isset( $config[ApiBase::PARAM_DFLT] ) ? $config[ApiBase::PARAM_DFLT] : null;
+                                       $type = isset( $config[ApiBase::PARAM_TYPE] ) ? $config[ApiBase::PARAM_TYPE]
+                                               : gettype( $default );
+                                       $this->assertContains( $type, [ 'NULL', 'string', 'text', 'password' ],
+                                               'PARAM_MAX_BYTES/CHARS is only supported for string-like types' );
+                               }
                        }
                }
        }