Skip to content

Commit 0cc5c1f

Browse files
committed
fix(ngRepeat): make allowed aliasAs expressions more strict
Ensure that aliasAs expressions are valid simple identifiers. These are still assigned to $scope in the same way that they were previously, however now you won't accidentally create a property named "filtered.collection". This change additionally restricts identifiers to prevent the use of certain ECMAScript reserved words ("null", "undefined", "this" --- should probably add "super", "try", "catch" and "finally" there too), as well as certain properties used by $scope or ngRepeat, including $parent, $index, $even, $odd, $first, $middle, or $last. Fixes angular#8438
1 parent 00d5fde commit 0cc5c1f

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@ngdoc error
2+
@name ngRepeat:badident
3+
@fullName Invalid identifier expression
4+
@description
5+
6+
Occurs when an invalid identifier is specified in an {@link ng.directive:ngRepeat ngRepeat} expression.
7+
8+
The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope.
9+
10+
If the expression is not a simple identifier (such that you could declare it with `var {name}`, or if the expression is a reserved name,
11+
this error is thrown.
12+
13+
Reserved names include:
14+
15+
- `null`
16+
- `this`
17+
- `undefined`
18+
- `$parent`
19+
- `$even`
20+
- `$odd`
21+
- `$first`
22+
- `$last`
23+
- `$middle`
24+
25+
Invalid expressions might look like this:
26+
27+
```html
28+
<li ng-repeat="item in items | filter:searchString as this">{{item}}</li>
29+
<li ng-repeat="item in items | filter:searchString as some.objects["property"]">{{item}}</li>
30+
<li ng-repeat="item in items | filter:searchString as resultOfSomeMethod()">{{item}}</li>
31+
<li ng-repeat="item in items | filter:searchString as foo=6">{{item}}</li>
32+
```
33+
34+
Valid expressions might look like this:
35+
36+
```html
37+
<li ng-repeat="item in items | filter:searchString as collections">{{item}}</li>
38+
<li ng-repeat="item in items | filter:searchString as filteredCollection">{{item}}</li>
39+
```

src/ng/directive/ngRepeat.js

+6
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
266266
var valueIdentifier = match[3] || match[1];
267267
var keyIdentifier = match[2];
268268

269+
if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
270+
/^null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent$/.test(aliasAs))) {
271+
throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
272+
aliasAs);
273+
}
274+
269275
var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
270276
var hashFnLocals = {$id: hashKey};
271277

test/ng/directive/ngRepeatSpec.js

+46
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,52 @@ describe('ngRepeat', function() {
416416

417417
expect(trim(element.text())).toEqual('No results found...');
418418
});
419+
420+
421+
it('should throw if alias identifier is not a simple identifier', inject(function($exceptionHandler) {
422+
scope.x = 'bl';
423+
scope.items = [
424+
{ name : 'red' },
425+
{ name : 'blue' },
426+
{ name : 'green' },
427+
{ name : 'black' },
428+
{ name : 'orange' },
429+
{ name : 'blonde' }
430+
];
431+
432+
forEach([
433+
'null',
434+
'this',
435+
'undefined',
436+
'$parent',
437+
'$index',
438+
'$first',
439+
'$middle',
440+
'$last',
441+
'$even',
442+
'$odd',
443+
'obj[key]',
444+
'obj["key"]',
445+
'obj[\'key\']',
446+
'obj.property',
447+
'foo=6'
448+
], function(expr) {
449+
var expression = ('item in items | filter:x as ' + expr + ' track by $index').replace(/"/g, '&quot;');
450+
element = $compile(
451+
'<div>' +
452+
' <div ng-repeat="' + expression + '">{{item}}</div>' +
453+
'</div>')(scope);
454+
455+
var expected = new RegExp('^\\[ngRepeat:badident\\] alias \'' + escape(expr) + '\' is invalid --- must be a valid JS identifier which is not a reserved name');
456+
expect($exceptionHandler.errors.shift()[0].message).
457+
toMatch(expected);
458+
dealoc(element);
459+
});
460+
461+
function escape(text) {
462+
return text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
463+
}
464+
}));
419465
});
420466

421467

0 commit comments

Comments
 (0)