From e8cc524816026cd22fa215c462292a288ce9808d Mon Sep 17 00:00:00 2001 From: Niko Savas Date: Thu, 23 Sep 2021 11:10:50 -0700 Subject: [PATCH 01/15] Match all HTMLElement interface extensions Match all extensions of the HTMLInterface provided in the lib.dom.d.ts types from typescript --- lib/rules/no-inner-html.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-inner-html.js b/lib/rules/no-inner-html.js index 40f4a9d..08dcc15 100644 --- a/lib/rules/no-inner-html.js +++ b/lib/rules/no-inner-html.js @@ -42,7 +42,7 @@ module.exports = { function mightBeHTMLElement(node) { const type = getNodeTypeAsString(node); - return type === "HTMLElement" || type === "any"; + return type === type.match(/HTML.*Element/) || type === "any"; } return { @@ -85,4 +85,4 @@ module.exports = { } }; } -}; \ No newline at end of file +}; From d57d26360f866a48bdfcfe0c0e4b5d6f09491d14 Mon Sep 17 00:00:00 2001 From: Nikolai Savas Date: Thu, 23 Sep 2021 13:33:35 -0700 Subject: [PATCH 02/15] Fix bug --- lib/rules/no-inner-html.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-inner-html.js b/lib/rules/no-inner-html.js index 08dcc15..476e180 100644 --- a/lib/rules/no-inner-html.js +++ b/lib/rules/no-inner-html.js @@ -42,7 +42,7 @@ module.exports = { function mightBeHTMLElement(node) { const type = getNodeTypeAsString(node); - return type === type.match(/HTML.*Element/) || type === "any"; + return type.match(/HTML.*Element/) || type === "any"; } return { From 4e2b41b2fda3eb76cfc901976d5f2ae5e1d3bfc5 Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:19:51 +0100 Subject: [PATCH 03/15] Update E2E integration.yml --- .github/workflows/E2E integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/E2E integration.yml b/.github/workflows/E2E integration.yml index 72b608e..0451863 100644 --- a/.github/workflows/E2E integration.yml +++ b/.github/workflows/E2E integration.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - os: [ubuntu-18.04, windows-2019] + os: [ubuntu-latest, windows-latest] steps: - name: Setup Node.js environment From 571063b909fc8438f20cb4926358831e44dfe1c8 Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:33:58 +0100 Subject: [PATCH 04/15] Update E2E integration.yml --- .github/workflows/E2E integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/E2E integration.yml b/.github/workflows/E2E integration.yml index 0451863..4062f3f 100644 --- a/.github/workflows/E2E integration.yml +++ b/.github/workflows/E2E integration.yml @@ -10,8 +10,8 @@ on: branches: [main, release/vNext] env: - PROJECT: discordjs # The name of the project you want to clone. It must be on github - REPOSITORY: discord.js # The repository name + PROJECT: nodejs # The name of the project you want to clone. It must be on github + REPOSITORY: node # The repository name FOLDER_TO_SCAN: src # The folder under which the source code you have is contained. Relative to the repository TS_CONFIG_PATH: tsconfig.json # The tsconfig.json path relative to the repository From f651acb230a17a4f6693e39425b2a92fc2d5cb23 Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:37:44 +0100 Subject: [PATCH 05/15] Update E2E integration.yml --- .github/workflows/E2E integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/E2E integration.yml b/.github/workflows/E2E integration.yml index 4062f3f..27924c7 100644 --- a/.github/workflows/E2E integration.yml +++ b/.github/workflows/E2E integration.yml @@ -70,7 +70,7 @@ jobs: - name: Run eslint run: npx eslint - -c node_modules/@microsoft/eslint-plugin-sdl/config/recommended.js + -c node_modules/@microsoft/eslint-plugin-sdl/config/required.js ../${{env.PROJECT}}/${{env.FOLDER_TO_SCAN}}/ --ext .js --parser-options=project:../${{env.PROJECT}}/${{env.TS_CONFIG_PATH}} From 89d88772ba1f17d65d5dd8a585054e0614bbab3d Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:42:30 +0100 Subject: [PATCH 06/15] Update E2E integration.yml --- .github/workflows/E2E integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/E2E integration.yml b/.github/workflows/E2E integration.yml index 27924c7..79cda1b 100644 --- a/.github/workflows/E2E integration.yml +++ b/.github/workflows/E2E integration.yml @@ -12,7 +12,7 @@ on: env: PROJECT: nodejs # The name of the project you want to clone. It must be on github REPOSITORY: node # The repository name - FOLDER_TO_SCAN: src # The folder under which the source code you have is contained. Relative to the repository + FOLDER_TO_SCAN: lib # The folder under which the source code you have is contained. Relative to the repository TS_CONFIG_PATH: tsconfig.json # The tsconfig.json path relative to the repository jobs: From fe581a2c9a0d50dcdad18fa7a7f372380eaf399f Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:47:39 +0100 Subject: [PATCH 07/15] Updated workers and node versions --- .github/workflows/node-version-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/node-version-integration.yml b/.github/workflows/node-version-integration.yml index eeb2569..601c682 100644 --- a/.github/workflows/node-version-integration.yml +++ b/.github/workflows/node-version-integration.yml @@ -16,8 +16,8 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, ubuntu-18.04, windows-2019, macos-10.15] - node-version: [12.x, 14.x] + os: [ubuntu-latest, windows-latest, macos-latest] + node-version: [12.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 From e8f919030b99c64826cd94452de38843a1d4b812 Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:50:37 +0100 Subject: [PATCH 08/15] Update node-version-integration.yml --- .github/workflows/node-version-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/node-version-integration.yml b/.github/workflows/node-version-integration.yml index 601c682..0960d37 100644 --- a/.github/workflows/node-version-integration.yml +++ b/.github/workflows/node-version-integration.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest] node-version: [12.x, 14.x, 16.x] steps: From 0f319e551d841960a71c39043f3712fbb7119726 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Fri, 28 Jan 2022 11:15:17 +0100 Subject: [PATCH 09/15] fix: install ESLint rules that we depend on This ESLint plugin should work out of box, and not require adopters to install additional dependencies. We probably need to add more packages here, but our setup already had a few from before so I don't know what else is missing. --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 5f8f4e9..e95d970 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,10 @@ "scripts": { "test": "mocha tests --recursive" }, + "dependencies": { + "eslint-plugin-node": "11.1.0", + "eslint-plugin-security": "1.4.0" + }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/parser": "^3.7.0", From 330fc214c96e43663b3959c698e88ecd2456b4ba Mon Sep 17 00:00:00 2001 From: A-Katopodis Date: Wed, 2 Feb 2022 14:27:55 +0100 Subject: [PATCH 10/15] Updated gitgnore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fcd3dc0..69f4082 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ # Licensed under the MIT License. node_modules/ .vscode/ +package-lock.json \ No newline at end of file From 7a927ba159eaac220ebf2ddb55f5ec7dce7a508f Mon Sep 17 00:00:00 2001 From: A-Katopodis Date: Wed, 2 Feb 2022 15:16:55 +0100 Subject: [PATCH 11/15] Added jsx-no-target-blank --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e95d970..7b20b21 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ }, "dependencies": { "eslint-plugin-node": "11.1.0", - "eslint-plugin-security": "1.4.0" + "eslint-plugin-security": "1.4.0", + "eslint-plugin-react": "7.24.0" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^3.7.0", From 7fd0e8fc491b59ab6e34d79a533e9636858e5109 Mon Sep 17 00:00:00 2001 From: A-Katopodis Date: Wed, 2 Feb 2022 15:18:23 +0100 Subject: [PATCH 12/15] added jsx-no-target-blank --- config/react.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/react.js b/config/react.js index b8971ef..6ff9eb9 100644 --- a/config/react.js +++ b/config/react.js @@ -14,6 +14,7 @@ module.exports = { ], rules: { "react/no-danger": "error", - "@microsoft/sdl/react-iframe-missing-sandbox": "error" + "@microsoft/sdl/react-iframe-missing-sandbox": "error", + "react/jsx-no-target-blank": "error" } } \ No newline at end of file From 5466a048f9d3b3508bd41630d14184804c36353b Mon Sep 17 00:00:00 2001 From: A-Katopodis Date: Wed, 2 Feb 2022 15:26:44 +0100 Subject: [PATCH 13/15] Updated config options --- config/react.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config/react.js b/config/react.js index 6ff9eb9..510a60c 100644 --- a/config/react.js +++ b/config/react.js @@ -15,6 +15,14 @@ module.exports = { rules: { "react/no-danger": "error", "@microsoft/sdl/react-iframe-missing-sandbox": "error", - "react/jsx-no-target-blank": "error" + "react/jsx-no-target-blank": ["error", + { + allowReferrer: false, + enforceDynamicLinks: 'always', + warnOnSpreadAttributes: true, + links: true, + forms: true + } + ] } } \ No newline at end of file From fd050d6429816fb742f8ac66361923a45da8bcf0 Mon Sep 17 00:00:00 2001 From: Antonios Katopodis <23344572+A-Katopodis@users.noreply.github.com> Date: Mon, 14 Feb 2022 15:29:37 +0100 Subject: [PATCH 14/15] Updated package.json version 0.1.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b20b21..0d4da6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft/eslint-plugin-sdl", - "version": "0.1.8", + "version": "0.1.9", "description": "ESLint plugin focused on common security issues and misconfigurations discoverable during static testing as part of Microsoft Security Development Lifecycle (SDL)", "keywords": [ "eslint", From cea5c8e8351e3903e498945c1d70b2feda9ab3da Mon Sep 17 00:00:00 2001 From: Tobias Smolka Date: Tue, 1 Mar 2022 18:58:40 +0100 Subject: [PATCH 15/15] Fix: no-document-write should catch newWindow.document.write --- lib/ast-utils.js | 26 +++++++++++--------------- lib/rules/no-inner-html.js | 12 +----------- lib/rules/no-insecure-random.js | 4 ++-- tests/lib/rules/no-document-domain.js | 5 +++++ tests/lib/rules/no-document-write.js | 6 +++++- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/lib/ast-utils.js b/lib/ast-utils.js index df2e073..b0eaf22 100644 --- a/lib/ast-utils.js +++ b/lib/ast-utils.js @@ -28,23 +28,18 @@ module.exports = { getFullTypeChecker(context) { return this.hasFullTypeInformation(context) ? context.parserServices.program.getTypeChecker() : null; }, - getNodeType(node, context) { - const typeChecker = context.parserServices.program.getTypeChecker(); - const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node); - const tsType = typeChecker.getTypeAtLocation(tsNode); - return typeChecker.typeToString(tsType); - }, - getCallerType(fullTypeChecker, object, context){ - const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(object); - const tsType = fullTypeChecker.getTypeAtLocation(tsNode); - const type = fullTypeChecker.typeToString(tsType); - return type; + getNodeTypeAsString(fullTypeChecker, node, context) { + if (fullTypeChecker && node) { + const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node); + const tsType = fullTypeChecker.getTypeAtLocation(tsNode); + const type = fullTypeChecker.typeToString(tsType); + return type; + } + return "any"; }, isDocumentObject(node, context, fullTypeChecker) { if (fullTypeChecker) { - const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node); - const tsType = fullTypeChecker.getTypeAtLocation(tsNode); - const type = fullTypeChecker.typeToString(tsType); + const type = this.getNodeTypeAsString(fullTypeChecker, node, context); return (type === "Document"); } @@ -58,7 +53,8 @@ module.exports = { node.property != undefined && node.property.name == "document" && ( (node.object != undefined && - node.object.name == "window") || + typeof node.object.name === "string" && + node.object.name.toLowerCase().endsWith('window')) || ( node.object != undefined && node.object.property != undefined && diff --git a/lib/rules/no-inner-html.js b/lib/rules/no-inner-html.js index 476e180..24d6547 100644 --- a/lib/rules/no-inner-html.js +++ b/lib/rules/no-inner-html.js @@ -30,18 +30,8 @@ module.exports = { create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); - function getNodeTypeAsString(node) { - if (fullTypeChecker && node) { - const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node); - const tsType = fullTypeChecker.getTypeAtLocation(tsNode); - const type = fullTypeChecker.typeToString(tsType); - return type; - } - return "any"; - } - function mightBeHTMLElement(node) { - const type = getNodeTypeAsString(node); + const type = astUtils.getNodeTypeAsString(fullTypeChecker, node, context); return type.match(/HTML.*Element/) || type === "any"; } diff --git a/lib/rules/no-insecure-random.js b/lib/rules/no-insecure-random.js index 88b775f..644b5ff 100644 --- a/lib/rules/no-insecure-random.js +++ b/lib/rules/no-insecure-random.js @@ -47,7 +47,7 @@ module.exports = { var notFalsePositive = false; if (fullTypeChecker) { - const type = astUtils.getCallerType(fullTypeChecker, node.object, context); + const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); notFalsePositive = type === "any" || type === "Crypto"; }else{ notFalsePositive = node.object.name === 'crypto'; @@ -63,7 +63,7 @@ module.exports = { "CallExpression > MemberExpression[property.name='random']"(node) { var notFalsePositive = false; if (fullTypeChecker) { - const type = astUtils.getCallerType(fullTypeChecker, node.object, context); + const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); notFalsePositive = type === "any" || type === "Math"; }else{ notFalsePositive = node.object.name === 'Math'; diff --git a/tests/lib/rules/no-document-domain.js b/tests/lib/rules/no-document-domain.js index c832429..30a997d 100644 --- a/tests/lib/rules/no-document-domain.js +++ b/tests/lib/rules/no-document-domain.js @@ -47,6 +47,7 @@ function main() { var somevalue = 'somevalue'; document.domain = somevalue; window.document.domain = somevalue; +newWindow.document.domain = somevalue; `, errors: [ { @@ -56,6 +57,10 @@ window.document.domain = somevalue; { line: 4, messageId: "default" + }, + { + line: 5, + messageId: "default" } ] } diff --git a/tests/lib/rules/no-document-write.js b/tests/lib/rules/no-document-write.js index 4be2092..47f13c8 100644 --- a/tests/lib/rules/no-document-write.js +++ b/tests/lib/rules/no-document-write.js @@ -74,12 +74,16 @@ ruleTester.run(ruleId, rule, { document.writeln('...'); window.document.write('...'); window.document.writeln('...'); + newWindow.document.write('...'); + newWindow.document.writeln('...'); `, errors: [ { messageId: "default", line: 2 }, { messageId: "default", line: 3 }, { messageId: "default", line: 4 }, - { messageId: "default", line: 5 } + { messageId: "default", line: 5 }, + { messageId: "default", line: 6 }, + { messageId: "default", line: 7 } ] } ]