From 1186f3f2c4ed9f8236d7b8b999d0b3e15658ba29 Mon Sep 17 00:00:00 2001 From: webschik Date: Sat, 13 Apr 2019 11:46:32 +0200 Subject: [PATCH] [refactor] rebuilt AST walkers to improve performance https://palantir.github.io/tslint/develop/custom-rules/walker-design.html --- npm-shrinkwrap.json | 2 +- package.json | 2 +- src/rules/tsrDetectBufferNoassertRule.ts | 46 +++---- src/rules/tsrDetectChildProcessRule.ts | 80 ++++++------ src/rules/tsrDetectEvalWithExpressionRule.ts | 50 +++++--- src/rules/tsrDetectHtmlInjectionRule.ts | 75 +++++++----- ...tsrDetectNoCsrfBeforeMethodOverrideRule.ts | 28 +++-- src/rules/tsrDetectNonLiteralBufferRule.ts | 30 +++-- .../tsrDetectNonLiteralFsFilenameRule.ts | 114 +++++++++--------- src/rules/tsrDetectNonLiteralRegexpRule.ts | 30 +++-- src/rules/tsrDetectNonLiteralRequireRule.ts | 30 +++-- .../tsrDetectPossibleTimingAttacksRule.ts | 39 +++--- src/rules/tsrDetectPseudoRandomBytesRule.ts | 24 ++-- src/rules/tsrDetectSqlLiteralInjectionRule.ts | 51 ++++---- ...etectUnsafeCrossOriginCommunicationRule.ts | 31 +++-- .../tsrDetectUnsafePropertiesAccessRule.ts | 28 +++-- src/rules/tsrDetectUnsafeRegexpRule.ts | 56 +++++---- src/rules/tsrDisableMustacheEscapeRule.ts | 36 +++--- 18 files changed, 426 insertions(+), 326 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 994b5ac..ed6b154 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "tslint-config-security", - "version": "1.15.0", + "version": "1.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 1ac6cef..4fd95e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tslint-config-security", - "version": "1.15.0", + "version": "1.16.0", "description": "TSLint security rules", "main": "./index.js", "files": [ diff --git a/src/rules/tsrDetectBufferNoassertRule.ts b/src/rules/tsrDetectBufferNoassertRule.ts index 92b78d7..c68d506 100644 --- a/src/rules/tsrDetectBufferNoassertRule.ts +++ b/src/rules/tsrDetectBufferNoassertRule.ts @@ -37,35 +37,39 @@ const writeMethods: string[] = [ export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const {name} = node; - const parent: ts.CallExpression = node.parent as ts.CallExpression; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.PropertyAccessExpression) { + const {name, expression} = node as ts.PropertyAccessExpression; + const parent: ts.CallExpression = node.parent as ts.CallExpression; - if (parent && parent.kind === ts.SyntaxKind.CallExpression && node.expression && name) { - const methodName: string = name.getText(); - let argumentIndex: number = -1; + if (parent && parent.kind === ts.SyntaxKind.CallExpression && expression && name) { + const methodName: string = name.text; + let argumentIndex: number = -1; - if (readMethods.indexOf(methodName) !== -1) { - argumentIndex = 1; - } else if (writeMethods.indexOf(methodName) !== -1) { - argumentIndex = 2; - } + if (readMethods.indexOf(methodName) !== -1) { + argumentIndex = 1; + } else if (writeMethods.indexOf(methodName) !== -1) { + argumentIndex = 2; + } - if ( - argumentIndex !== -1 && - parent.arguments && - parent.arguments[argumentIndex] && - parent.arguments[argumentIndex].kind === ts.SyntaxKind.TrueKeyword - ) { - this.addFailureAtNode(node, `Found Buffer.${methodName} with noAssert flag set true`); + if ( + argumentIndex !== -1 && + parent.arguments && + parent.arguments[argumentIndex] && + parent.arguments[argumentIndex].kind === ts.SyntaxKind.TrueKeyword + ) { + ctx.addFailureAtNode(node, `Found Buffer.${methodName} with noAssert flag set true`); + } } } - super.visitPropertyAccessExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectChildProcessRule.ts b/src/rules/tsrDetectChildProcessRule.ts index 34b2d99..cd2224a 100644 --- a/src/rules/tsrDetectChildProcessRule.ts +++ b/src/rules/tsrDetectChildProcessRule.ts @@ -1,48 +1,60 @@ import * as Lint from 'tslint'; import * as ts from 'typescript'; -import {StringLiteral, stringLiteralKinds} from '../node-kind'; +import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - private names: string[] = []; - - visitCallExpression(node: ts.CallExpression) { - const {expression} = node; - const firstArgument: StringLiteral = node.arguments && (node.arguments[0] as StringLiteral); - - if ( - firstArgument && - expression && - stringLiteralKinds.includes(firstArgument.kind) && - firstArgument.text === 'child_process' && - expression.getText() === 'require' - ) { - const parent: ts.VariableDeclaration = node.parent as ts.VariableDeclaration; - - this.names.length = 0; - - if (parent && parent.kind === ts.SyntaxKind.VariableDeclaration) { - this.names.push(parent.name.getText()); +function walk(ctx: Lint.WalkContext) { + const names: string[] = []; + + function visitNode(node: ts.Node): void { + switch (node.kind) { + case ts.SyntaxKind.CallExpression: { + const {expression, arguments: args} = node as ts.CallExpression; + const firstArgument = args && args[0]; + + if ( + firstArgument && + expression && + stringLiteralKinds.includes(firstArgument.kind) && + (firstArgument as ts.StringLiteral).text === 'child_process' && + (expression as ts.StringLiteral).text === 'require' + ) { + const parent: ts.VariableDeclaration = node.parent as ts.VariableDeclaration; + + names.length = 0; + + if (parent && parent.kind === ts.SyntaxKind.VariableDeclaration) { + names.push((parent.name as ts.Identifier).text); + } + + ctx.addFailureAtNode(node, 'Found require("child_process")'); + } + break; } - - this.addFailureAtNode(node, 'Found require("child_process")'); + case ts.SyntaxKind.PropertyAccessExpression: { + const {name, expression} = node as ts.PropertyAccessExpression; + + if ( + name && + expression && + name.text === 'exec' && + names.indexOf((expression as ts.Identifier).text) >= 0 + ) { + ctx.addFailureAtNode(node, 'Found child_process.exec() with non StringLiteral first argument'); + } + break; + } + default: + // } - super.visitCallExpression(node); + return ts.forEachChild(node, visitNode); } - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const {name, expression} = node; - - if (name && expression && name.getText() === 'exec' && this.names.indexOf(expression.getText()) >= 0) { - this.addFailureAtNode(node, 'Found child_process.exec() with non StringLiteral first argument'); - } - - super.visitPropertyAccessExpression(node); - } + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectEvalWithExpressionRule.ts b/src/rules/tsrDetectEvalWithExpressionRule.ts index 8844708..7a55c10 100644 --- a/src/rules/tsrDetectEvalWithExpressionRule.ts +++ b/src/rules/tsrDetectEvalWithExpressionRule.ts @@ -5,30 +5,44 @@ import syntaxKindToName from '../syntax-kind-to-name'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitCallExpression(node: ts.CallExpression) { - const firstArgument: ts.Expression = node.arguments[0]; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + switch (node.kind) { + case ts.SyntaxKind.CallExpression: { + const {expression, arguments: args} = node as ts.CallExpression; + const firstArgument: ts.Expression | undefined = args && args[0]; - if (node.expression.getText() === 'eval' && firstArgument && !stringLiteralKinds.includes(firstArgument.kind)) { - this.addFailureAtNode(node, `eval with argument of type ${syntaxKindToName(firstArgument.kind)}`); - } - - super.visitCallExpression(node); - } + if ( + firstArgument && + (expression as ts.StringLiteral).text === 'eval' && + !stringLiteralKinds.includes(firstArgument.kind) + ) { + ctx.addFailureAtNode(node, `eval with argument of type ${syntaxKindToName(firstArgument.kind)}`); + } + break; + } + case ts.SyntaxKind.NewExpression: { + const {expression, arguments: args} = node as ts.NewExpression; - visitNewExpression(node: ts.NewExpression) { - if ( - node.arguments && - node.expression.getText() === 'Function' && - node.arguments.some((node: ts.Node) => !stringLiteralKinds.includes(node.kind)) - ) { - this.addFailureAtNode(node, 'Found function constructor with non-literal argument'); + if ( + args && + (expression as ts.StringLiteral).text === 'Function' && + args.some((node: ts.Node) => !stringLiteralKinds.includes(node.kind)) + ) { + ctx.addFailureAtNode(node, 'Found function constructor with non-literal argument'); + } + break; + } + default: + // } - super.visitNewExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectHtmlInjectionRule.ts b/src/rules/tsrDetectHtmlInjectionRule.ts index 223f57c..56fb290 100644 --- a/src/rules/tsrDetectHtmlInjectionRule.ts +++ b/src/rules/tsrDetectHtmlInjectionRule.ts @@ -4,7 +4,7 @@ import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } @@ -12,43 +12,54 @@ const unsafeDocumentHtmlMethods: string[] = ['writeln', 'write']; const unsafeElementHtmlMethods: string[] = ['insertAdjacentHTML']; const unsafeElementHtmlProps: string[] = ['outerHTML', 'innerHTML']; -class RuleWalker extends Lint.RuleWalker { - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const expression: ts.Identifier = node.expression as ts.Identifier; - const name: ts.Identifier = node.name; - const parent: ts.CallExpression = node.parent as ts.CallExpression; - const firstArgument: undefined | ts.Expression = parent && parent.arguments && parent.arguments[0]; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + switch (node.kind) { + case ts.SyntaxKind.PropertyAccessExpression: { + const {expression, name} = node as ts.PropertyAccessExpression; + const parent: ts.CallExpression = node.parent as ts.CallExpression; + const firstArgument: undefined | ts.Expression = parent && parent.arguments && parent.arguments[0]; - if (expression && name && firstArgument && !stringLiteralKinds.includes(firstArgument.kind)) { - const method: string = name.text; + if (expression && name && firstArgument && !stringLiteralKinds.includes(firstArgument.kind)) { + const method: string = name.text; - if (expression.text === 'document' && unsafeDocumentHtmlMethods.includes(method)) { - this.addFailureAtNode(parent, `Found document.${method} with non-literal argument`); - } else if (unsafeElementHtmlMethods.includes(method)) { - this.addFailureAtNode(parent, `Found Element.${method} with non-literal argument`); + if ( + (expression as ts.Identifier).text === 'document' && + unsafeDocumentHtmlMethods.includes(method) + ) { + ctx.addFailureAtNode(parent, `Found document.${method} with non-literal argument`); + } else if (unsafeElementHtmlMethods.includes(method)) { + ctx.addFailureAtNode(parent, `Found Element.${method} with non-literal argument`); + } + } + + break; } - } + case ts.SyntaxKind.BinaryExpression: { + const {left, right, operatorToken} = node as ts.BinaryExpression; + const leftName: ts.Identifier | undefined = left && (left as ts.PropertyAccessExpression).name; - super.visitPropertyAccessExpression(node); - } + if ( + operatorToken && + operatorToken.kind === ts.SyntaxKind.EqualsToken && + left && + left.kind === ts.SyntaxKind.PropertyAccessExpression && + leftName && + right && + !stringLiteralKinds.includes(right.kind) && + unsafeElementHtmlProps.includes(leftName.text) + ) { + ctx.addFailureAtNode(node, `Found Element.${leftName.text} with non-literal value`); + } - visitBinaryExpression(node: ts.BinaryExpression) { - const left: ts.PropertyAccessExpression = node.left as ts.PropertyAccessExpression; - const right: ts.Expression = node.right; - - if ( - node.operatorToken && - node.operatorToken.kind === ts.SyntaxKind.EqualsToken && - left && - left.kind === ts.SyntaxKind.PropertyAccessExpression && - left.name && - right && - !stringLiteralKinds.includes(right.kind) && - unsafeElementHtmlProps.includes(left.name.text) - ) { - this.addFailureAtNode(node, `Found Element.${left.name.text} with non-literal value`); + break; + } + default: + // } - super.visitBinaryExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectNoCsrfBeforeMethodOverrideRule.ts b/src/rules/tsrDetectNoCsrfBeforeMethodOverrideRule.ts index 21e5fed..a6d7953 100644 --- a/src/rules/tsrDetectNoCsrfBeforeMethodOverrideRule.ts +++ b/src/rules/tsrDetectNoCsrfBeforeMethodOverrideRule.ts @@ -3,25 +3,29 @@ import * as ts from 'typescript'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - private isCsrfFound?: boolean; +function walk(ctx: Lint.WalkContext) { + let isCsrfFound: boolean | undefined; - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const name: ts.Identifier = node.name as ts.Identifier; - const expression: ts.Identifier = node.expression as ts.Identifier; + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.PropertyAccessExpression) { + const {name, expression} = node as ts.PropertyAccessExpression; + const nameText: string | undefined = name && (name as ts.Identifier).text; - if (name && expression && expression.text === 'express') { - if (name.text === 'methodOverride' && this.isCsrfFound) { - this.addFailureAtNode(node, 'express.csrf() middleware found before express.methodOverride()'); - } else if (name.text === 'csrf') { - this.isCsrfFound = true; + if (expression && (expression as ts.Identifier).text === 'express') { + if (isCsrfFound && nameText === 'methodOverride') { + ctx.addFailureAtNode(node, 'express.csrf() middleware found before express.methodOverride()'); + } else if (nameText === 'csrf') { + isCsrfFound = true; + } } } - super.visitPropertyAccessExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectNonLiteralBufferRule.ts b/src/rules/tsrDetectNonLiteralBufferRule.ts index df7ce7c..845f382 100644 --- a/src/rules/tsrDetectNonLiteralBufferRule.ts +++ b/src/rules/tsrDetectNonLiteralBufferRule.ts @@ -4,24 +4,28 @@ import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitNewExpression(node: ts.NewExpression) { - const expression: ts.Expression = node.expression; - const firstArgument: undefined | ts.Expression = node.arguments && node.arguments[0]; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.NewExpression) { + const {expression, arguments: args} = node as ts.NewExpression; + const firstArgument: undefined | ts.Expression = args && args[0]; - if ( - expression && - (expression as ts.Identifier).text === 'Buffer' && - firstArgument && - !stringLiteralKinds.includes(firstArgument.kind) - ) { - this.addFailureAtNode(node, 'Found new Buffer with non-literal argument'); + if ( + expression && + firstArgument && + (expression as ts.Identifier).text === 'Buffer' && + !stringLiteralKinds.includes(firstArgument.kind) + ) { + ctx.addFailureAtNode(node, 'Found new Buffer with non-literal argument'); + } } - super.visitNewExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectNonLiteralFsFilenameRule.ts b/src/rules/tsrDetectNonLiteralFsFilenameRule.ts index c1d169f..057e9f3 100644 --- a/src/rules/tsrDetectNonLiteralFsFilenameRule.ts +++ b/src/rules/tsrDetectNonLiteralFsFilenameRule.ts @@ -5,82 +5,86 @@ import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } const expressionsToCheck: string[] = ['fs', `require('fs')`, 'require("fs")', 'require(`fs`)']; const reservedIdentifiers: string[] = ['__dirname']; -class RuleWalker extends Lint.RuleWalker { - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const {name, expression} = node; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.PropertyAccessExpression) { + const {expression, name} = node as ts.PropertyAccessExpression; - if (name && node.parent && expression) { - const methodName: string = name.getText(); - const parent: ts.CallExpression = node.parent as ts.CallExpression; - const fsArgsInfo: number[] | void = fsModuleMethodsArgumentsInfo.get(methodName); - const methodArguments: ts.NodeArray = parent.arguments; + if (name && node.parent && expression) { + const methodName: string = name.text; + const parent: ts.CallExpression = node.parent as ts.CallExpression; + const fsArgsInfo: number[] | void = fsModuleMethodsArgumentsInfo.get(methodName); + const methodArguments: ts.NodeArray = parent.arguments; - if (fsArgsInfo && methodArguments && expressionsToCheck.includes(expression.getText())) { - const invalidArgumentIndices: number[] = fsArgsInfo.filter((index: number) => { - const arg: ts.Expression = methodArguments[index]; + if (fsArgsInfo && methodArguments && expressionsToCheck.includes(expression.getText())) { + const invalidArgumentIndices: number[] = fsArgsInfo.filter((index: number) => { + const arg: ts.Expression = methodArguments[index]; - if (!arg) { - return false; - } - const {kind} = arg; + if (!arg) { + return false; + } + const {kind} = arg; - if (kind === ts.SyntaxKind.BinaryExpression) { - const {left, right} = arg as ts.BinaryExpression; + if (kind === ts.SyntaxKind.BinaryExpression) { + const {left, right} = arg as ts.BinaryExpression; - if ( - left && - left.kind === ts.SyntaxKind.Identifier && - reservedIdentifiers.includes(left.getText()) - ) { - return Boolean(right && !stringLiteralKinds.includes(right.kind)); - } + if ( + left && + left.kind === ts.SyntaxKind.Identifier && + reservedIdentifiers.includes((left as ts.Identifier).text) + ) { + return Boolean(right && !stringLiteralKinds.includes(right.kind)); + } - if ( - right && - right.kind === ts.SyntaxKind.Identifier && - reservedIdentifiers.includes(right.getText()) - ) { - return Boolean(left && !stringLiteralKinds.includes(left.kind)); + if ( + right && + right.kind === ts.SyntaxKind.Identifier && + reservedIdentifiers.includes((right as ts.Identifier).text) + ) { + return Boolean(left && !stringLiteralKinds.includes(left.kind)); + } } - } - if (kind === ts.SyntaxKind.TemplateExpression) { - const {templateSpans = []} = arg as ts.TemplateExpression; - const [firstTemplateSpan] = templateSpans; - const firstTemplateSpanExpr: ts.Expression | void = - firstTemplateSpan && firstTemplateSpan.expression; - - if ( - firstTemplateSpanExpr && - firstTemplateSpanExpr.kind === ts.SyntaxKind.Identifier && - reservedIdentifiers.includes(firstTemplateSpanExpr.getText()) && - !templateSpans[1] - ) { - return false; + if (kind === ts.SyntaxKind.TemplateExpression) { + const {templateSpans = []} = arg as ts.TemplateExpression; + const [firstTemplateSpan] = templateSpans; + const firstTemplateSpanExpr: ts.Expression | void = + firstTemplateSpan && firstTemplateSpan.expression; + + if ( + firstTemplateSpanExpr && + firstTemplateSpanExpr.kind === ts.SyntaxKind.Identifier && + reservedIdentifiers.includes(firstTemplateSpanExpr.getText()) && + !templateSpans[1] + ) { + return false; + } } - } - return !stringLiteralKinds.includes(kind); - }); + return !stringLiteralKinds.includes(kind); + }); - if (invalidArgumentIndices[0] !== undefined) { - const errorIndex: string = invalidArgumentIndices.join(', '); + if (invalidArgumentIndices[0] !== undefined) { + const errorIndex: string = invalidArgumentIndices.join(', '); - this.addFailureAtNode( - node, - `Found fs.${methodName} with non-literal argument at index ${errorIndex}` - ); + ctx.addFailureAtNode( + node, + `Found fs.${methodName} with non-literal argument at index ${errorIndex}` + ); + } } } } - super.visitPropertyAccessExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectNonLiteralRegexpRule.ts b/src/rules/tsrDetectNonLiteralRegexpRule.ts index b69bf92..e2df665 100644 --- a/src/rules/tsrDetectNonLiteralRegexpRule.ts +++ b/src/rules/tsrDetectNonLiteralRegexpRule.ts @@ -4,24 +4,28 @@ import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitNewExpression(node: ts.NewExpression) { - const expression: ts.Identifier = node.expression as ts.Identifier; - const firstArgument: undefined | ts.Expression = node.arguments && node.arguments[0]; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.NewExpression) { + const {expression, arguments: args} = node as ts.NewExpression; + const firstArgument: undefined | ts.Expression = args && args[0]; - if ( - expression && - expression.text === 'RegExp' && - firstArgument && - !stringLiteralKinds.includes(firstArgument.kind) - ) { - this.addFailureAtNode(node, 'Found non-literal argument to RegExp Constructor'); + if ( + expression && + firstArgument && + (expression as ts.Identifier).text === 'RegExp' && + !stringLiteralKinds.includes(firstArgument.kind) + ) { + ctx.addFailureAtNode(node, 'Found non-literal argument to RegExp Constructor'); + } } - super.visitNewExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectNonLiteralRequireRule.ts b/src/rules/tsrDetectNonLiteralRequireRule.ts index bc0d68d..e98ce59 100644 --- a/src/rules/tsrDetectNonLiteralRequireRule.ts +++ b/src/rules/tsrDetectNonLiteralRequireRule.ts @@ -4,24 +4,28 @@ import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitCallExpression(node: ts.CallExpression) { - const expression: ts.Identifier = node.expression as ts.Identifier; - const firstArgument: undefined | ts.Expression = node.arguments && node.arguments[0]; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.CallExpression) { + const {expression, arguments: args} = node as ts.CallExpression; + const firstArgument: undefined | ts.Expression = args && args[0]; - if ( - expression && - expression.text === 'require' && - firstArgument && - !stringLiteralKinds.includes(firstArgument.kind) - ) { - this.addFailureAtNode(node, 'Found non-literal argument in require'); + if ( + expression && + firstArgument && + (expression as ts.Identifier).text === 'require' && + !stringLiteralKinds.includes(firstArgument.kind) + ) { + ctx.addFailureAtNode(node, 'Found non-literal argument in require'); + } } - super.visitCallExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectPossibleTimingAttacksRule.ts b/src/rules/tsrDetectPossibleTimingAttacksRule.ts index e49da43..95993bd 100644 --- a/src/rules/tsrDetectPossibleTimingAttacksRule.ts +++ b/src/rules/tsrDetectPossibleTimingAttacksRule.ts @@ -77,29 +77,34 @@ function isVulnerablePropertyAccessExpression(node: ts.PropertyAccessExpression) export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitBinaryExpression(node: ts.BinaryExpression) { - const operatorTokenKind = node.operatorToken.kind; - - if ( - operatorTokenKind === ts.SyntaxKind.EqualsEqualsToken || - operatorTokenKind === ts.SyntaxKind.EqualsEqualsEqualsToken || - operatorTokenKind === ts.SyntaxKind.ExclamationEqualsToken || - operatorTokenKind === ts.SyntaxKind.ExclamationEqualsEqualsToken - ) { - if (isVulnerableType(node.left) && isVulnerableType(node.right)) { - if (containsKeyword(node.left)) { - this.addFailureAtNode(node, 'Potential timing attack on the left side of expression'); - } else if (containsKeyword(node.right)) { - this.addFailureAtNode(node, 'Potential timing attack on the right side of expression'); +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.BinaryExpression) { + const {left, right, operatorToken} = node as ts.BinaryExpression; + const operatorTokenKind = operatorToken.kind; + + if ( + operatorTokenKind === ts.SyntaxKind.EqualsEqualsToken || + operatorTokenKind === ts.SyntaxKind.EqualsEqualsEqualsToken || + operatorTokenKind === ts.SyntaxKind.ExclamationEqualsToken || + operatorTokenKind === ts.SyntaxKind.ExclamationEqualsEqualsToken + ) { + if (isVulnerableType(left) && isVulnerableType(right)) { + if (containsKeyword(left)) { + ctx.addFailureAtNode(node, 'Potential timing attack on the left side of expression'); + } else if (containsKeyword(right)) { + ctx.addFailureAtNode(node, 'Potential timing attack on the right side of expression'); + } } } } - super.visitBinaryExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectPseudoRandomBytesRule.ts b/src/rules/tsrDetectPseudoRandomBytesRule.ts index 8409335..08cce7b 100644 --- a/src/rules/tsrDetectPseudoRandomBytesRule.ts +++ b/src/rules/tsrDetectPseudoRandomBytesRule.ts @@ -3,21 +3,25 @@ import * as ts from 'typescript'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const name: ts.Identifier = node.name; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.PropertyAccessExpression) { + const {name} = node as ts.PropertyAccessExpression; - if (name && name.text === 'pseudoRandomBytes') { - this.addFailureAtNode( - node, - 'Found crypto.pseudoRandomBytes which does not produce cryptographically strong numbers' - ); + if (name && name.text === 'pseudoRandomBytes') { + ctx.addFailureAtNode( + node, + 'Found crypto.pseudoRandomBytes which does not produce cryptographically strong numbers' + ); + } } - super.visitPropertyAccessExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectSqlLiteralInjectionRule.ts b/src/rules/tsrDetectSqlLiteralInjectionRule.ts index f440e40..e383a80 100644 --- a/src/rules/tsrDetectSqlLiteralInjectionRule.ts +++ b/src/rules/tsrDetectSqlLiteralInjectionRule.ts @@ -3,35 +3,42 @@ import * as ts from 'typescript'; import {isSqlQuery} from '../is-sql-query'; import {stringLiteralKinds} from '../node-kind'; +const generalErrorMessage: string = 'Found possible SQL injection'; + export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -const generalErrorMessage: string = 'Found possible SQL injection'; - -class RuleWalker extends Lint.RuleWalker { - visitTemplateExpression(node: ts.TemplateExpression) { - const {parent} = node; - - if ( - (!parent || parent.kind !== ts.SyntaxKind.TaggedTemplateExpression) && - isSqlQuery(node.getText().slice(1, -1)) - ) { - this.addFailureAtNode(node, generalErrorMessage); +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + switch (node.kind) { + case ts.SyntaxKind.TemplateExpression: { + const {parent} = node; + + if ( + (!parent || parent.kind !== ts.SyntaxKind.TaggedTemplateExpression) && + isSqlQuery(node.getText().slice(1, -1)) + ) { + ctx.addFailureAtNode(node, generalErrorMessage); + } + break; + } + case ts.SyntaxKind.BinaryExpression: { + const {left} = node as ts.BinaryExpression; + + if (left && stringLiteralKinds.includes(left.kind) && isSqlQuery(left.getText().slice(1, -1))) { + ctx.addFailureAtNode(left, generalErrorMessage); + } + break; + } + default: + // } - super.visitTemplateExpression(node); + return ts.forEachChild(node, visitNode); } - visitBinaryExpression(node: ts.BinaryExpression) { - const {left} = node; - - if (left && stringLiteralKinds.includes(left.kind) && isSqlQuery(left.getText().slice(1, -1))) { - this.addFailureAtNode(left, generalErrorMessage); - } - - super.visitBinaryExpression(node); - } + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectUnsafeCrossOriginCommunicationRule.ts b/src/rules/tsrDetectUnsafeCrossOriginCommunicationRule.ts index b5a53e5..2d90698 100644 --- a/src/rules/tsrDetectUnsafeCrossOriginCommunicationRule.ts +++ b/src/rules/tsrDetectUnsafeCrossOriginCommunicationRule.ts @@ -3,24 +3,29 @@ import * as ts from 'typescript'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitCallExpression(node: ts.CallExpression) { - const {name} = node.expression as ts.PropertyAccessExpression; - const [, targetOrigin]: ts.NodeArray = node.arguments || []; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.CallExpression) { + const {expression, arguments: args} = node as ts.CallExpression; + const {name} = expression as ts.PropertyAccessExpression; + const [, targetOrigin]: ts.NodeArray = args || []; - if ( - name && - targetOrigin && - name.getText() === 'postMessage' && - ((targetOrigin as ts.StringLiteral).text || '').trim() === '*' - ) { - this.addFailureAtNode(node, 'Found a wildcard keyword (*) in the targetOrigin argument'); + if ( + name && + targetOrigin && + name.text === 'postMessage' && + ((targetOrigin as ts.StringLiteral).text || '').trim() === '*' + ) { + ctx.addFailureAtNode(node, 'Found a wildcard keyword (*) in the targetOrigin argument'); + } } - super.visitCallExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectUnsafePropertiesAccessRule.ts b/src/rules/tsrDetectUnsafePropertiesAccessRule.ts index 42fb3dd..e5d8820 100644 --- a/src/rules/tsrDetectUnsafePropertiesAccessRule.ts +++ b/src/rules/tsrDetectUnsafePropertiesAccessRule.ts @@ -3,23 +3,27 @@ import * as ts from 'typescript'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitCallExpression(node: ts.CallExpression) { - const {expression, arguments: args} = node; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.CallExpression) { + const {expression, arguments: args} = node as ts.CallExpression; - if ( - expression && - args && - expression.kind === ts.SyntaxKind.ElementAccessExpression && - args.find(ts.isIdentifier) - ) { - this.addFailureAtNode(node, 'Found unsafe properties access'); + if ( + expression && + args && + expression.kind === ts.SyntaxKind.ElementAccessExpression && + args.find(ts.isIdentifier) + ) { + ctx.addFailureAtNode(node, 'Found unsafe properties access'); + } } - super.visitCallExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDetectUnsafeRegexpRule.ts b/src/rules/tsrDetectUnsafeRegexpRule.ts index 196a8e2..889ebab 100644 --- a/src/rules/tsrDetectUnsafeRegexpRule.ts +++ b/src/rules/tsrDetectUnsafeRegexpRule.ts @@ -2,38 +2,48 @@ import * as isSafeRegexp from 'safe-regex'; import * as Lint from 'tslint'; import * as ts from 'typescript'; -import {StringLiteral, stringLiteralKinds} from '../node-kind'; +import {stringLiteralKinds} from '../node-kind'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitRegularExpressionLiteral(node: ts.RegularExpressionLiteral) { - if (node.text && !isSafeRegexp(node.text)) { - this.addFailureAtNode(node, 'Unsafe Regular Expression'); - } - - super.visitRegularExpressionLiteral(node); - } +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + switch (node.kind) { + case ts.SyntaxKind.RegularExpressionLiteral: { + const {text} = node as ts.RegularExpressionLiteral; - visitNewExpression(node: ts.NewExpression) { - const expression: ts.Identifier = node.expression as ts.Identifier; - const firstArgument: undefined | StringLiteral = node.arguments && (node.arguments[0] as StringLiteral); + if (text && !isSafeRegexp(text)) { + ctx.addFailureAtNode(node, 'Unsafe Regular Expression'); + } + break; + } + case ts.SyntaxKind.NewExpression: { + const {expression, arguments: args} = node as ts.NewExpression; + const firstArgument = args && args[0]; + const firstArgumentText: string | undefined = firstArgument && (firstArgument as ts.StringLiteral).text; - if ( - expression && - expression.text === 'RegExp' && - firstArgument && - stringLiteralKinds.includes(firstArgument.kind) && - firstArgument.text && - !isSafeRegexp(firstArgument.text) - ) { - this.addFailureAtNode(node, 'Unsafe Regular Expression (new RegExp)'); + if ( + expression && + firstArgument && + (expression as ts.Identifier).text === 'RegExp' && + stringLiteralKinds.includes(firstArgument.kind) && + firstArgumentText && + !isSafeRegexp(firstArgumentText) + ) { + ctx.addFailureAtNode(node, 'Unsafe Regular Expression (new RegExp)'); + } + break; + } + default: + // } - super.visitNewExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); } diff --git a/src/rules/tsrDisableMustacheEscapeRule.ts b/src/rules/tsrDisableMustacheEscapeRule.ts index 67f88ac..e15467e 100644 --- a/src/rules/tsrDisableMustacheEscapeRule.ts +++ b/src/rules/tsrDisableMustacheEscapeRule.ts @@ -3,27 +3,31 @@ import * as ts from 'typescript'; export class Rule extends Lint.Rules.AbstractRule { apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new RuleWalker(sourceFile, this.getOptions())); + return this.applyWithFunction(sourceFile, walk); } } -class RuleWalker extends Lint.RuleWalker { - visitPropertyAccessExpression(node: ts.PropertyAccessExpression) { - const {name} = node; - const parent: ts.BinaryExpression = node.parent as ts.BinaryExpression; +function walk(ctx: Lint.WalkContext) { + function visitNode(node: ts.Node): void { + if (node.kind === ts.SyntaxKind.PropertyAccessExpression) { + const {name} = node as ts.PropertyAccessExpression; + const parent: ts.BinaryExpression = node.parent as ts.BinaryExpression; - if ( - name && - parent && - parent.operatorToken && - parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && - parent.right && - parent.right.kind === ts.SyntaxKind.FalseKeyword && - name.getText() === 'escapeMarkup' - ) { - this.addFailureAtNode(node, 'Markup escaping disabled'); + if ( + name && + parent && + parent.operatorToken && + parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && + parent.right && + parent.right.kind === ts.SyntaxKind.FalseKeyword && + name.text === 'escapeMarkup' + ) { + ctx.addFailureAtNode(node, 'Markup escaping disabled'); + } } - super.visitPropertyAccessExpression(node); + return ts.forEachChild(node, visitNode); } + + return ts.forEachChild(ctx.sourceFile, visitNode); }