Skip to content

Commit 1d9047d

Browse files
committed
Avoid matching polyfilled global names in NameEngine.
Fix #22. PiperOrigin-RevId: 354387758 Change-Id: I659f36dd0eb608aa252a908f6271a2fafd126191
1 parent 3c528e0 commit 1d9047d

File tree

3 files changed

+33
-11
lines changed

3 files changed

+33
-11
lines changed

src/third_party/tsetse/util/absolute_matcher.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,6 @@ export class AbsoluteMatcher {
111111
debugLog(() => `start matching ${n.getText()} in ${p?.getText()}`);
112112

113113
if (p !== undefined) {
114-
// Do not match global symbols appearing on the LHS of an assignment
115-
// expression. This avoids false positives in polyfill code.
116-
if (this.filePath === GLOBAL && ts.isBinaryExpression(p) &&
117-
p.left === n && p.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
118-
return false;
119-
}
120-
121114
// Check if the node is being declared. Declaration may be imported
122115
// without programmer being aware of. We should not alert them about that.
123116
// Since import statments are also declarations, this have two notable

src/third_party/tsetse/util/ast_tools.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ export function shouldExamineNode(n: ts.Node) {
7070
isInStockLibraries(n));
7171
}
7272

73+
/**
74+
* Returns the top-level property access expression (or element access
75+
* expression) that the input node is part of.
76+
*
77+
* For example, given an expression `c` which is part of `a['b'].c = 1;`,
78+
* the function returns the whole LHS expression `a['b'].c`.
79+
*/
80+
export function walkUpPropertyAndElementAccess(n: ts.Node): ts.Node {
81+
while (ts.isPropertyAccessExpression(n.parent) ||
82+
ts.isElementAccessExpression(n.parent)) {
83+
n = n.parent;
84+
}
85+
return n;
86+
}
87+
7388
/**
7489
* Return whether the given Node is (or is in) a library included as default.
7590
* We currently look for a node_modules/typescript/ prefix, but this could

src/third_party/tsetse/util/pattern_engines/name_engine.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as ts from 'typescript';
22

33
import {Checker} from '../../checker';
44
import {AbsoluteMatcher} from '../absolute_matcher';
5+
import {walkUpPropertyAndElementAccess} from '../ast_tools';
56
import {isExpressionOfAllowedTrustedType} from '../is_trusted_type';
67
import {TrustedTypesConfig} from '../trusted_types_configuration';
78

@@ -21,19 +22,32 @@ function isCalledWithAllowedTrustedType(
2122
return false;
2223
}
2324

25+
function isPolyfill(n: ts.Node, matcher: AbsoluteMatcher) {
26+
if (matcher.filePath === 'GLOBAL') {
27+
const wholeExp = walkUpPropertyAndElementAccess(n);
28+
const parent = wholeExp.parent;
29+
if (parent && ts.isBinaryExpression(parent) && parent.left === wholeExp &&
30+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
31+
return true;
32+
}
33+
}
34+
return false;
35+
}
36+
2437
function checkIdentifierNode(
2538
tc: ts.TypeChecker, n: ts.Identifier, matcher: AbsoluteMatcher,
2639
allowedTrustedType: TrustedTypesConfig|undefined): ts.Node|undefined {
40+
if (isPolyfill(n, matcher)) return;
2741
if (!matcher.matches(n, tc)) return;
2842
if (isCalledWithAllowedTrustedType(tc, n, allowedTrustedType)) return;
2943

30-
3144
return n;
3245
}
3346

3447
function checkElementAccessNode(
3548
tc: ts.TypeChecker, n: ts.ElementAccessExpression, matcher: AbsoluteMatcher,
3649
allowedTrustedType: TrustedTypesConfig|undefined): ts.Node|undefined {
50+
if (isPolyfill(n, matcher)) return;
3751
if (!matcher.matches(n.argumentExpression, tc)) return;
3852
if (isCalledWithAllowedTrustedType(tc, n, allowedTrustedType)) return;
3953

@@ -46,9 +60,9 @@ export class NameEngine extends PatternEngine {
4660
for (const value of this.config.values) {
4761
const matcher = new AbsoluteMatcher(value);
4862

49-
// `String.prototype.split` only returns emtpy array when both the string
50-
// and the splitter are empty. Here we should be able to safely assert pop
51-
// returns a non-null result.
63+
// `String.prototype.split` only returns emtpy array when both the
64+
// string and the splitter are empty. Here we should be able to safely
65+
// assert pop returns a non-null result.
5266
const bannedIdName = matcher.bannedName.split('.').pop()!;
5367
checker.onNamedIdentifier(
5468
bannedIdName,

0 commit comments

Comments
 (0)