import { Allowlist } from '../../third_party/tsetse/allowlist' ; import { Checker } from '../../third_party/tsetse/checker' ; import { ErrorCode } from '../../third_party/tsetse/error_code' ; import { AbstractRule } from '../../third_party/tsetse/rule' ; import { shouldExamineNode } from '../../third_party/tsetse/util/ast_tools' ; import { isLiteral } from '../../third_party/tsetse/util/is_literal' ; import { PropertyMatcher } from '../../third_party/tsetse/util/property_matcher' ; import * as ts from 'typescript' ; import { RuleConfiguration } from '../../rule_configuration' ; let errMsg = 'Do not use document.execCommand(\'insertHTML\'), as this can lead to XSS.' ; function matchNode ( tc , n , matcher ) { if ( ! shouldExamineNode ( n ) ) return ; if ( ! matcher . typeMatches ( tc . getTypeAtLocation ( n . expression ) ) ) return ; if ( ! ts . isCallExpression ( n . parent ) ) return n ; if ( n . parent . expression !== n ) return n ; if ( n . parent . arguments . length < 1 ) return ; const ty = tc . getTypeAtLocation ( n . parent . arguments [ 0 ] ) ; if ( ty . isStringLiteral ( ) && ty . value . toLowerCase ( ) !== 'inserthtml' && isLiteral ( tc , n . parent . arguments [ 0 ] ) ) { return ; } return n ; } /** A Rule that looks for use of Document#execCommand. */ export class Rule extends AbstractRule { static readonly RULE_NAME = 'ban-document-execcommand' ; readonly ruleName = Rule . RULE_NAME ; readonly code = ErrorCode . CONFORMANCE_PATTERN ; private readonly propMatcher : PropertyMatcher ; private readonly allowlist ? : Allowlist ; constructor ( configuration = { } ) { super ( ) ; this . propMatcher = PropertyMatcher . fromSpec ( 'Document.prototype.execCommand' ) ; if ( configuration . allowlistEntries ) { this . allowlist = new Allowlist ( configuration . allowlistEntries ) ; } } register ( checker ) { checker . onNamedPropertyAccess ( this . propMatcher . bannedProperty , ( c , n ) => { const node = matchNode ( c . typeChecker , n , this . propMatcher ) ; if ( node ) { checker . addFailureAtNode ( node , errMsg , Rule . RULE_NAME , this . allowlist ) ; } } , this . code ) ; checker . onStringLiteralElementAccess ( this . propMatcher . bannedProperty , ( c , n ) => { const node = matchNode ( c . typeChecker , n , this . propMatcher ) ; if ( node ) { checker . addFailureAtNode ( node , errMsg , Rule . RULE_NAME , this . allowlist ) ; } } , this . code ) ; } }