diff --git a/src/ng/directive/ngOptions.js b/src/ng/directive/ngOptions.js index b276fddb461d..c26f79aeee8a 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 diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index c83a6223c65f..121583a207a1 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); diff --git a/src/ng/parse.js b/src/ng/parse.js index c9fa42b63e87..560018fe34a5 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) {