From 32f1984c01d7b789ed57bd18da2b0641c54f40ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BB=D0=B8=D1=81=D0=BA=D0=B8=D0=BD=20=D0=95=D0=B2?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Tue, 2 May 2017 10:50:11 +0300 Subject: [PATCH 1/4] Added function $parse.is_valid_identifier --- src/ng/parse.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/ng/parse.js b/src/ng/parse.js index c9fa42b63e8..8d97f2e75e5 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -1742,6 +1742,37 @@ function $ParseProvider() { isIdentifierStart: isFunction(identStart) && identStart, isIdentifierContinue: isFunction(identContinue) && identContinue }; + + // Check if given text is a valid identifier. 01.05.17 E.P. + var valid_identifier_lexer; + $parse.is_valid_identifier = function( + var_name + ){ + // Check argument + if ( !var_name || + typeof var_name !== 'string' + ) return false; + //console.log('$parse.is_valid_identifier entry "' + var_name + '"'); + + // Instantiate lexer once, when required + if ( valid_identifier_lexer === undefined) + valid_identifier_lexer = new Lexer($parseOptions); + + // Parse given text into tokens + var tokens = valid_identifier_lexer.lex(var_name); + //console.log('$parse.is_valid_identifier tokens = ' + JSON.stringify(tokens)); + + // The whole text expected to be one token of "identifier" type + var result = ( + tokens && + tokens.length === 1 && + tokens[0].index === 0 && + tokens[0].text === var_name && + tokens[0].identifier === true ); + //console.log('$parse.is_valid_identifier result = ' + result); + return result; + } + return $parse; function $parse(exp, interceptorFn) { From 354d6f2979a02a20abb39a4aee14118ce20d7229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BB=D0=B8=D1=81=D0=BA=D0=B8=D0=BD=20=D0=95=D0=B2?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Tue, 2 May 2017 10:51:34 +0300 Subject: [PATCH 2/4] Use $parse service to check identifiers in ngOptions --- src/ng/directive/ngOptions.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js index b276fddb461..c26f79aeee8 100644 --- a/src/ng/directive/ngOptions.js +++ b/src/ng/directive/ngOptions.js @@ -234,8 +234,9 @@ var ngOptionsMinErr = minErr('ngOptions'); */ /* eslint-disable max-len */ -// //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999 -var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([$\w][$\w]*)|(?:\(\s*([$\w][$\w]*)\s*,\s*([$\w][$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/; +// Changed groups 5-7 like this: ([$\w][$\w]*) => ([^\s(),]+). 02.05.17 E.P. + //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555500000000066666666666000000077777777777000000000000000888888888800000000000000000009999999999 +var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([^\s(),]+)|(?:\(\s*([^\s(),]+)\s*,\s*([^\s(),]+)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/; // 1: value expression (valueFn) // 2: label expression (displayFn) // 3: group by expression (groupByFn) @@ -268,6 +269,13 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, // The variable name for the key of the item in the collection var keyName = match[6]; + // Use $parse service to check identifiers. 02.05.17 E.P. + if (!$parse.is_valid_identifier(valueName)) + throw ngOptionsMinErr('badident', 'Not valid option value identifier: \'{0}\'.', valueName); + + if (keyName && !$parse.is_valid_identifier(keyName)) + throw ngOptionsMinErr('badident', 'Not valid option key identifier: \'{0}\'.', keyName); + // An expression that generates the viewValue for an option if there is a label expression var selectAs = / as /.test(match[0]) && match[1]; // An expression that is used to track the id of each object in the options collection From 371d60ab5e5d900b90a01e873bdc197875e7468e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BB=D0=B8=D1=81=D0=BA=D0=B8=D0=BD=20=D0=95=D0=B2?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Tue, 2 May 2017 10:52:01 +0300 Subject: [PATCH 3/4] Use $parse service to check identifiers in ngRepeat --- src/ng/directive/ngRepeat.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index c83a6223c65..121583a207a 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -385,7 +385,8 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani var aliasAs = match[3]; var trackByExp = match[4]; - match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/); + // Changed groups like this: ([$\w]+) => ([^\s(),]+). 02.05.17 E.P. + match = lhs.match(/^(?:\s*([^\s(),]+)|\(\s*([^\s(),]+)\s*,\s*([^\s(),]+)\s*\))$/); if (!match) { throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.', @@ -394,7 +395,14 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani var valueIdentifier = match[3] || match[1]; var keyIdentifier = match[2]; - if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) || + // Use $parse service to check identifiers. 01.05.17 E.P. + if (!$parse.is_valid_identifier(valueIdentifier)) + throw ngRepeatMinErr('badident', 'Not valid loop value identifier: \'{0}\'.', valueIdentifier); + + if (keyIdentifier && !$parse.is_valid_identifier(keyIdentifier)) + throw ngRepeatMinErr('badident', 'Not valid loop key identifier: \'{0}\'.', keyIdentifier); + + if (aliasAs && (!$parse.is_valid_identifier(aliasAs) || /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) { throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.', aliasAs); From b8410ee79c96dd0c6c6f85d652f9fa94c11033c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BB=D0=B8=D1=81=D0=BA=D0=B8=D0=BD=20=D0=95=D0=B2?= =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Tue, 2 May 2017 11:50:48 +0300 Subject: [PATCH 4/4] removed extra spaces --- src/ng/parse.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ng/parse.js b/src/ng/parse.js index 8d97f2e75e5..560018fe34a 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -1747,15 +1747,15 @@ function $ParseProvider() { var valid_identifier_lexer; $parse.is_valid_identifier = function( var_name - ){ + ) { // Check argument - if ( !var_name || - typeof var_name !== 'string' + if (!var_name || + typeof var_name !== 'string' ) return false; //console.log('$parse.is_valid_identifier entry "' + var_name + '"'); // Instantiate lexer once, when required - if ( valid_identifier_lexer === undefined) + if (valid_identifier_lexer === undefined) valid_identifier_lexer = new Lexer($parseOptions); // Parse given text into tokens @@ -1768,10 +1768,10 @@ function $ParseProvider() { tokens.length === 1 && tokens[0].index === 0 && tokens[0].text === var_name && - tokens[0].identifier === true ); + tokens[0].identifier === true); //console.log('$parse.is_valid_identifier result = ' + result); return result; - } + }; return $parse;