From 7a5a085e08e3dbc950389e67fb23cd629e4cfa34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ka=C4=8Dmar?= Date: Tue, 1 Oct 2024 12:43:46 +0200 Subject: [PATCH] Apply prettier for consistent code formatting --- .github/ISSUE_TEMPLATE/bug_report.md | 24 +- .github/ISSUE_TEMPLATE/feature_request.md | 7 +- .github/workflows/e2e-integration.yml | 26 +- .../workflows/node-version-integration.yml | 15 +- .prettierignore | 4 + .prettierrc | 3 + README.md | 68 ++-- config/angular.js | 12 +- config/angularjs.js | 12 +- config/common.js | 10 +- config/electron.js | 10 +- config/node.js | 14 +- config/react.js | 27 +- config/typescript.js | 20 +- docs/rules/no-angularjs-bypass-sce.md | 2 +- docs/rules/no-cookies.md | 4 +- docs/rules/no-document-domain.md | 2 +- docs/rules/no-document-write.md | 2 +- docs/rules/no-electron-node-integration.md | 2 +- docs/rules/no-html-method.md | 2 +- docs/rules/no-inner-html.md | 8 +- docs/rules/no-insecure-random.md | 14 +- docs/rules/no-insecure-url.md | 21 +- docs/rules/no-postmessage-star-origin.md | 4 +- docs/rules/no-unsafe-alloc.md | 6 +- docs/rules/no-winjs-html-unsafe.md | 2 +- docs/rules/react-iframe-missing-sandbox.md | 10 +- lib/ast-utils.js | 117 ++++--- lib/index.js | 79 +++-- lib/rules/no-angular-bypass-sanitizer.js | 53 ++-- .../no-angular-sanitization-trusted-urls.js | 59 ++-- lib/rules/no-angularjs-bypass-sce.js | 96 +++--- lib/rules/no-angularjs-enable-svg.js | 73 ++--- .../no-angularjs-sanitization-whitelist.js | 53 ++-- lib/rules/no-cookies.js | 20 +- lib/rules/no-document-domain.js | 25 +- lib/rules/no-document-write.js | 25 +- lib/rules/no-electron-node-integration.js | 53 ++-- lib/rules/no-html-method.js | 38 +-- lib/rules/no-inner-html.js | 46 +-- lib/rules/no-insecure-random.js | 70 ++-- lib/rules/no-insecure-url.js | 298 ++++++++++-------- lib/rules/no-msapp-exec-unsafe.js | 33 +- lib/rules/no-postmessage-star-origin.js | 31 +- lib/rules/no-unsafe-alloc.js | 28 +- lib/rules/no-winjs-html-unsafe.js | 31 +- lib/rules/react-iframe-missing-sandbox.js | 79 +++-- package-lock.json | 16 + package.json | 3 + tests/fixtures/ts/tsconfig.json | 6 +- tests/fixtures/tsx/tsconfig.json | 12 +- .../lib/rules/no-angular-bypass-sanitizer.js | 24 +- .../no-angular-sanitization-trusted-urls.js | 22 +- tests/lib/rules/no-angularjs-bypass-sce.js | 28 +- tests/lib/rules/no-angularjs-enable-svg.js | 14 +- .../no-angularjs-sanitization-whitelist.js | 22 +- tests/lib/rules/no-cookies.js | 24 +- tests/lib/rules/no-document-domain.js | 30 +- tests/lib/rules/no-document-write.js | 20 +- .../lib/rules/no-electron-node-integration.js | 28 +- tests/lib/rules/no-html-method.js | 18 +- tests/lib/rules/no-inner-html.js | 26 +- tests/lib/rules/no-insecure-random.js | 70 ++-- tests/lib/rules/no-insecure-url.js | 288 +++++++++-------- tests/lib/rules/no-msapp-exec-unsafe.js | 15 +- tests/lib/rules/no-postmessage-star-origin.js | 18 +- tests/lib/rules/no-unsafe-alloc.js | 18 +- tests/lib/rules/no-winjs-html-unsafe.js | 18 +- .../lib/rules/react-iframe-missing-sandbox.js | 50 +-- tests/lib/test-utils.js | 44 +-- 70 files changed, 1240 insertions(+), 1212 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..9b77ea7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..2bc5d5f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/workflows/e2e-integration.yml b/.github/workflows/e2e-integration.yml index 4b8fd64..a60274d 100644 --- a/.github/workflows/e2e-integration.yml +++ b/.github/workflows/e2e-integration.yml @@ -40,7 +40,7 @@ jobs: npm i typescript npm i @microsoft/eslint-formatter-sarif working-directory: ${{env.TEST_RUN_DIR}} - + - uses: actions/checkout@v4 with: repository: ${{env.GITHUB_REPO}} @@ -55,12 +55,12 @@ jobs: - name: Install plugin dependencies run: npm install --production working-directory: ${{env.PLUGIN_DIR}} - + - name: Link plugin run: sudo npm link ../${{env.PLUGIN_DIR}} working-directory: ${{env.TEST_RUN_DIR}} if: runner.os == 'Linux' - + - name: Link plugin run: npm link ../${{env.PLUGIN_DIR}} working-directory: ${{env.TEST_RUN_DIR}} @@ -69,18 +69,18 @@ jobs: - name: Create ESLint config file run: echo 'module.exports = [...require("@microsoft/eslint-plugin-sdl").configs.recommended];' > eslint.config.js working-directory: ${{env.TEST_RUN_DIR}} - + - name: Run ESLint - run: npx eslint - --config eslint.config.js - --no-config-lookup - ${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_ESLINT_GLOB}} - --parser-options=project:${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_TSCONFIG}} - --format @microsoft/eslint-formatter-sarif - --output-file eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif + run: npx eslint + --config eslint.config.js + --no-config-lookup + ${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_ESLINT_GLOB}} + --parser-options=project:${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_TSCONFIG}} + --format @microsoft/eslint-formatter-sarif + --output-file eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif working-directory: ${{env.TEST_RUN_DIR}} - continue-on-error: true - + continue-on-error: true + - name: Upload ESLint results as artifact uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/node-version-integration.yml b/.github/workflows/node-version-integration.yml index e93993e..8a563be 100644 --- a/.github/workflows/node-version-integration.yml +++ b/.github/workflows/node-version-integration.yml @@ -11,7 +11,6 @@ on: jobs: build: - runs-on: ${{ matrix.os }} strategy: @@ -20,10 +19,10 @@ jobs: node-version: [18.x, 20.x, 22.x] steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm test + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a8031c8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +SECURITY.md +CODE_OF_CONDUCT.md +package.json +package-lock.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..de753c5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} diff --git a/README.md b/README.md index da8a7d5..c39b2fb 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ Plugin is intended as a baseline for projects that follow [Microsoft Security De ```sh npm install microsoft/eslint-plugin-sdl ``` + or + ```sh yarn add microsoft/eslint-plugin-sdl ``` @@ -24,9 +26,7 @@ Including an ESLint configuration file in your project allows you to customize h ```js const pluginMicrosoftSdl = require("@microsoft/eslint-plugin-sdl"); -module.exports = [ - ...pluginMicrosoftSdl.configs.recommended -]; +module.exports = [...pluginMicrosoftSdl.configs.recommended]; ``` ESLint will then only enforce rules you specify in the rules section of your configuration file at the [severity level](https://eslint.org/docs/latest/use/configure/rules) you designate. For example: @@ -39,9 +39,9 @@ module.exports = [ { rules: { "no-eval": "error", - "@microsoft/sdl/no-inner-html": "error" - } - } + "@microsoft/sdl/no-inner-html": "error", + }, + }, ]; ``` @@ -65,36 +65,36 @@ Where possible, we leverage existing rules from [ESLint](https://eslint.org/docs We also implemented several [custom rules](./lib/rules) where we did not find sufficient alternative in the community. -| Name | Description | -| --- | --- | -| [no-caller](https://eslint.org/docs/rules/no-caller) | Bans usage of deprecated functions `arguments.caller()` and `arguments.callee` that could potentially allow access to call stack. | -| [no-delete-var](https://eslint.org/docs/rules/no-delete-var) | Bans usage of operator `delete` on variables as it can lead to unexpected behavior. | -| [no-eval](https://eslint.org/docs/rules/no-eval) | Bans usage of [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) that allows code execution from string argument. | -| [no-implied-eval](https://eslint.org/docs/rules/no-implied-eval) | Bans usage of `setTimeout()`, `setInterval()` and `execScript()`. These functions are similar to `eval()` and prone to code execution. | -| [no-new-func](https://eslint.org/docs/rules/no-new-func) | Bans calling `new Function()` as it's similar to `eval()` and prone to code execution. | -| [node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md) | Bans usage of deprecated APIs in Node. | -| [@microsoft/sdl/no-angular-bypass-sanitizer](./docs/rules/no-angular-bypass-sanitizer.md) | Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass [DomSanitizer](https://angular.io/api/platform-browser/DomSanitizer#security-risk) in Angular and need to be reviewed. | -| [@microsoft/sdl/no-angularjs-bypass-sce](./docs/rules/no-angularjs-bypass-sce.md) | Calls to `$sceProvider.enabled(false)`, `$sceDelegate.trustAs()`, `$sce.trustAs()` and relevant shorthand methods (e.g. `trustAsHtml` or `trustAsJs`) bypass [Strict Contextual Escaping (SCE)](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) in AngularJS and need to be reviewed. | -| [@microsoft/sdl/no-angularjs-enable-svg](./docs/rules/no-angularjs-enable-svg.md) | Calls to [`$sanitizeProvider.enableSvg(true)`](https://docs.angularjs.org/api/ngSanitize/provider/$sanitizeProvider#enableSvg) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed. | -| [@microsoft/sdl/no-angularjs-sanitization-whitelist](./docs/rules/no-angularjs-sanitization-whitelist.md) | Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed. | -| [@microsoft/sdl/no-cookies](./docs/rules/no-cookies.md) | HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other modern methods instead. | -| [@microsoft/sdl/no-document-domain](./docs/rules/no-document-domain.md) | Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited. | -| [@microsoft/sdl/no-document-write](./docs/rules/no-document-write.md) | Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | -| [@microsoft/sdl/no-electron-node-integration](./docs/rules/no-electron-node-integration.md) | [Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks. | -| [@microsoft/sdl/no-html-method](./docs/rules/no-html-method.md) | Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | -| [@microsoft/sdl/no-inner-html](./docs/rules/no-inner-html.md) | Assignments to innerHTML or outerHTML properties manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | -| [@microsoft/sdl/no-insecure-url](./docs/rules/no-insecure-url.md) | Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending potentially sensitive data over untrusted networks in plaintext. | -| [@microsoft/sdl/no-msapp-exec-unsafe](./docs/rules/no-msapp-exec-unsafe.md) | Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided. | -| [@microsoft/sdl/no-postmessage-star-origin](./docs/rules/no-postmessage-star-origin.md) | Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. | -| [@microsoft/sdl/no-unsafe-alloc](./docs/rules/no-unsafe-alloc.md) | When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data. | -| [@microsoft/sdl/no-winjs-html-unsafe](./docs/rules/no-winjs-html-unsafe.md) | Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)) and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead. | -| [@microsoft/sdl/react-iframe-missing-sandbox](./docs/rules/react-iframe-missing-sandbox.md) | The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified. | -| [react/no-danger](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md) | Bans usage of `dangerouslySetInnerHTML` property in React as it allows passing unsanitized HTML in DOM. | -| [@typescript-eslint/no-implied-eval](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implied-eval.md) | Similar to built-in ESLint rule `no-implied-eval`. Bans usage of `setTimeout()`, `setInterval()`, `setImmediate()`, `execScript()` or `new Function()` as they are similar to `eval()` and allow code execution from string arguments. | +| Name | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [no-caller](https://eslint.org/docs/rules/no-caller) | Bans usage of deprecated functions `arguments.caller()` and `arguments.callee` that could potentially allow access to call stack. | +| [no-delete-var](https://eslint.org/docs/rules/no-delete-var) | Bans usage of operator `delete` on variables as it can lead to unexpected behavior. | +| [no-eval](https://eslint.org/docs/rules/no-eval) | Bans usage of [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) that allows code execution from string argument. | +| [no-implied-eval](https://eslint.org/docs/rules/no-implied-eval) | Bans usage of `setTimeout()`, `setInterval()` and `execScript()`. These functions are similar to `eval()` and prone to code execution. | +| [no-new-func](https://eslint.org/docs/rules/no-new-func) | Bans calling `new Function()` as it's similar to `eval()` and prone to code execution. | +| [node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md) | Bans usage of deprecated APIs in Node. | +| [@microsoft/sdl/no-angular-bypass-sanitizer](./docs/rules/no-angular-bypass-sanitizer.md) | Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass [DomSanitizer](https://angular.io/api/platform-browser/DomSanitizer#security-risk) in Angular and need to be reviewed. | +| [@microsoft/sdl/no-angularjs-bypass-sce](./docs/rules/no-angularjs-bypass-sce.md) | Calls to `$sceProvider.enabled(false)`, `$sceDelegate.trustAs()`, `$sce.trustAs()` and relevant shorthand methods (e.g. `trustAsHtml` or `trustAsJs`) bypass [Strict Contextual Escaping (SCE)](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) in AngularJS and need to be reviewed. | +| [@microsoft/sdl/no-angularjs-enable-svg](./docs/rules/no-angularjs-enable-svg.md) | Calls to [`$sanitizeProvider.enableSvg(true)`](https://docs.angularjs.org/api/ngSanitize/provider/$sanitizeProvider#enableSvg) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed. | +| [@microsoft/sdl/no-angularjs-sanitization-whitelist](./docs/rules/no-angularjs-sanitization-whitelist.md) | Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed. | +| [@microsoft/sdl/no-cookies](./docs/rules/no-cookies.md) | HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other modern methods instead. | +| [@microsoft/sdl/no-document-domain](./docs/rules/no-document-domain.md) | Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited. | +| [@microsoft/sdl/no-document-write](./docs/rules/no-document-write.md) | Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | +| [@microsoft/sdl/no-electron-node-integration](./docs/rules/no-electron-node-integration.md) | [Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks. | +| [@microsoft/sdl/no-html-method](./docs/rules/no-html-method.md) | Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | +| [@microsoft/sdl/no-inner-html](./docs/rules/no-inner-html.md) | Assignments to innerHTML or outerHTML properties manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. | +| [@microsoft/sdl/no-insecure-url](./docs/rules/no-insecure-url.md) | Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending potentially sensitive data over untrusted networks in plaintext. | +| [@microsoft/sdl/no-msapp-exec-unsafe](./docs/rules/no-msapp-exec-unsafe.md) | Calls to [`MSApp.execUnsafeLocalFunction()`]() bypass script injection validation and should be avoided. | +| [@microsoft/sdl/no-postmessage-star-origin](./docs/rules/no-postmessage-star-origin.md) | Always provide specific target origin, not \* when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. | +| [@microsoft/sdl/no-unsafe-alloc](./docs/rules/no-unsafe-alloc.md) | When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data. | +| [@microsoft/sdl/no-winjs-html-unsafe](./docs/rules/no-winjs-html-unsafe.md) | Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`]() and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`]() instead. | +| [@microsoft/sdl/react-iframe-missing-sandbox](./docs/rules/react-iframe-missing-sandbox.md) | The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified. | +| [react/no-danger](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md) | Bans usage of `dangerouslySetInnerHTML` property in React as it allows passing unsanitized HTML in DOM. | +| [@typescript-eslint/no-implied-eval](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implied-eval.md) | Similar to built-in ESLint rule `no-implied-eval`. Bans usage of `setTimeout()`, `setInterval()`, `setImmediate()`, `execScript()` or `new Function()` as they are similar to `eval()` and allow code execution from string arguments. | ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. @@ -104,4 +104,4 @@ provided by the bot. You will only need to do this once across all repos using o This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. \ No newline at end of file +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/config/angular.js b/config/angular.js index cf2142f..9b6220c 100644 --- a/config/angular.js +++ b/config/angular.js @@ -3,16 +3,16 @@ "use strict"; -// Generates shareable config for modern Angular (https://angular.dev/) apps. +// Generates shareable config for modern Angular (https://angular.dev/) apps. module.exports = (pluginSdl) => { return [ { plugins: { - "@microsoft/sdl": pluginSdl + "@microsoft/sdl": pluginSdl, }, rules: { - "@microsoft/sdl/no-angular-bypass-sanitizer": "error" - } - } + "@microsoft/sdl/no-angular-bypass-sanitizer": "error", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/config/angularjs.js b/config/angularjs.js index f396c69..974a0b2 100644 --- a/config/angularjs.js +++ b/config/angularjs.js @@ -3,18 +3,18 @@ "use strict"; -// Generates shareable config for legacy AngularJS (https://angularjs.org/) apps. +// Generates shareable config for legacy AngularJS (https://angularjs.org/) apps. module.exports = (pluginSdl) => { return [ { plugins: { - "@microsoft/sdl": pluginSdl + "@microsoft/sdl": pluginSdl, }, rules: { "@microsoft/sdl/no-angularjs-enable-svg": "error", "@microsoft/sdl/no-angularjs-sanitization-whitelist": "error", - "@microsoft/sdl/no-angularjs-bypass-sce": "error" - } - } + "@microsoft/sdl/no-angularjs-bypass-sce": "error", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/config/common.js b/config/common.js index 4df3e69..1d3ccde 100644 --- a/config/common.js +++ b/config/common.js @@ -7,7 +7,7 @@ module.exports = (pluginSdl) => { return [ { plugins: { - "@microsoft/sdl": pluginSdl + "@microsoft/sdl": pluginSdl, }, rules: { "no-caller": "error", @@ -23,8 +23,8 @@ module.exports = (pluginSdl) => { "@microsoft/sdl/no-insecure-url": "error", "@microsoft/sdl/no-msapp-exec-unsafe": "error", "@microsoft/sdl/no-postmessage-star-origin": "error", - "@microsoft/sdl/no-winjs-html-unsafe": "error" - } - } + "@microsoft/sdl/no-winjs-html-unsafe": "error", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/config/electron.js b/config/electron.js index 0ad2f41..d9d788d 100644 --- a/config/electron.js +++ b/config/electron.js @@ -7,11 +7,11 @@ module.exports = (pluginSdl) => { return [ { plugins: { - "@microsoft/sdl": pluginSdl + "@microsoft/sdl": pluginSdl, }, rules: { - "@microsoft/sdl/no-electron-node-integration": "error" - } - } + "@microsoft/sdl/no-electron-node-integration": "error", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/config/node.js b/config/node.js index 32f7727..0feb108 100644 --- a/config/node.js +++ b/config/node.js @@ -9,19 +9,19 @@ module.exports = (pluginSdl) => { return [ { plugins: { - n: pluginN + n: pluginN, }, rules: { - "n/no-deprecated-api": "error" - } + "n/no-deprecated-api": "error", + }, }, { plugins: { - "@microsoft/sdl": pluginSdl + "@microsoft/sdl": pluginSdl, }, rules: { - "@microsoft/sdl/no-unsafe-alloc": "error" - } + "@microsoft/sdl/no-unsafe-alloc": "error", + }, }, ]; -}; \ No newline at end of file +}; diff --git a/config/react.js b/config/react.js index ece7068..80e7fa7 100644 --- a/config/react.js +++ b/config/react.js @@ -11,33 +11,34 @@ module.exports = (pluginSdl) => { languageOptions: { parserOptions: { ecmaFeatures: { - jsx: true - } - } - } + jsx: true, + }, + }, + }, }, { plugins: { - "react": pluginReact + react: pluginReact, }, rules: { "react/no-danger": "error", - "react/jsx-no-target-blank": ["error", + "react/jsx-no-target-blank": [ + "error", { allowReferrer: false, enforceDynamicLinks: "always", warnOnSpreadAttributes: true, - } - ] - } + }, + ], + }, }, { plugins: { "@microsoft/sdl": pluginSdl, }, rules: { - "@microsoft/sdl/react-iframe-missing-sandbox": "error" - } - } + "@microsoft/sdl/react-iframe-missing-sandbox": "error", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/config/typescript.js b/config/typescript.js index 5b55ad5..a8dc50b 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -13,28 +13,28 @@ module.exports = () => { ecmaVersion: 6, sourceType: "module", ecmaFeatures: { - jsx: true - } - } - } + jsx: true, + }, + }, + }, }, { files: ["**/*.{ts,tsx}"], languageOptions: { parserOptions: { parser: "@typescript-eslint/parser", - } + }, }, plugins: { - "@typescript-eslint": pluginTypescript + "@typescript-eslint": pluginTypescript, }, rules: { "@typescript-eslint/no-implied-eval": "error", // @typescript-eslint/no-implied-eval offers more accurate results for typescript. // thus we turn the more generic rule off for ts and tsx files. // This also avoids duplicate hits. - "no-implied-eval": "off" - } - } + "no-implied-eval": "off", + }, + }, ]; -}; \ No newline at end of file +}; diff --git a/docs/rules/no-angularjs-bypass-sce.md b/docs/rules/no-angularjs-bypass-sce.md index 0e73fdc..b43206b 100644 --- a/docs/rules/no-angularjs-bypass-sce.md +++ b/docs/rules/no-angularjs-bypass-sce.md @@ -4,4 +4,4 @@ Calls to `$sceProvider.enabled(false)`, `$sceDelegate.trustAs()`, `$sce.trustAs( SCE should be bypassed only in very rare and justifiable cases after careful review so that the risk of introducing Cross-Site-Scripting (XSS) vulnerability is minimized. -See [official documentation](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) for more details. \ No newline at end of file +See [official documentation](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) for more details. diff --git a/docs/rules/no-cookies.md b/docs/rules/no-cookies.md index 5949476..c395a8c 100644 --- a/docs/rules/no-cookies.md +++ b/docs/rules/no-cookies.md @@ -6,8 +6,8 @@ Cookies should be used only in rare and justifiable cases after thorough securit ## Further Reading -* [Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) +- [Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) ## Related Rules -* [tslint-microsoft-contrib/no-cookies](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noCookiesRule.ts) \ No newline at end of file +- [tslint-microsoft-contrib/no-cookies](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noCookiesRule.ts) diff --git a/docs/rules/no-document-domain.md b/docs/rules/no-document-domain.md index e16c2c1..3f06e8f 100644 --- a/docs/rules/no-document-domain.md +++ b/docs/rules/no-document-domain.md @@ -4,4 +4,4 @@ Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/D ## Related Rules -* [tslint-microsoft-contrib/no-document-domain](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentDomainRule.ts) +- [tslint-microsoft-contrib/no-document-domain](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentDomainRule.ts) diff --git a/docs/rules/no-document-write.md b/docs/rules/no-document-write.md index 3726090..1d182ae 100644 --- a/docs/rules/no-document-write.md +++ b/docs/rules/no-document-write.md @@ -4,4 +4,4 @@ Calls to document.write or document.writeln manipulate DOM directly without any ## Related Rules -* [tslint-microsoft-contrib/no-document-write](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentWriteRule.ts) +- [tslint-microsoft-contrib/no-document-write](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentWriteRule.ts) diff --git a/docs/rules/no-electron-node-integration.md b/docs/rules/no-electron-node-integration.md index 91b776e..6e7a34e 100644 --- a/docs/rules/no-electron-node-integration.md +++ b/docs/rules/no-electron-node-integration.md @@ -6,4 +6,4 @@ ## Related Rules -* [codeql/js/enabling-electron-renderer-node-integration](https://help.semmle.com/wiki/display/JS/Enabling+Node.js+integration+for+Electron+web+content+renderers) +- [codeql/js/enabling-electron-renderer-node-integration](https://help.semmle.com/wiki/display/JS/Enabling+Node.js+integration+for+Electron+web+content+renderers) diff --git a/docs/rules/no-html-method.md b/docs/rules/no-html-method.md index 4ee59a9..141f6cc 100644 --- a/docs/rules/no-html-method.md +++ b/docs/rules/no-html-method.md @@ -4,4 +4,4 @@ Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM ## Related Rules -* [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) +- [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) diff --git a/docs/rules/no-inner-html.md b/docs/rules/no-inner-html.md index 15facd9..e672e43 100644 --- a/docs/rules/no-inner-html.md +++ b/docs/rules/no-inner-html.md @@ -2,10 +2,10 @@ Assignments to [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)/[outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) properties or calls to [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. -* [Rule Source](../../lib/rules/no-inner-html.js) -* [Rule Test](../../tests/lib/rules/no-inner-html.js) +- [Rule Source](../../lib/rules/no-inner-html.js) +- [Rule Test](../../tests/lib/rules/no-inner-html.js) ## Related Rules -* [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) -* [eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/master/docs/rules/method.md) \ No newline at end of file +- [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) +- [eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/master/docs/rules/method.md) diff --git a/docs/rules/no-insecure-random.md b/docs/rules/no-insecure-random.md index 04bb94f..5b61c2d 100644 --- a/docs/rules/no-insecure-random.md +++ b/docs/rules/no-insecure-random.md @@ -6,12 +6,12 @@ Use crypto.randomBytes() or window.crypto.getRandomValues() instead. ## Related Rules -* [tslint-microsoft-contrib/no-insecure-random](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/insecureRandomRule.ts) -- https://help.semmle.com/wiki/display/JS/Insecure+randomness +- [tslint-microsoft-contrib/no-insecure-random](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/insecureRandomRule.ts) + +* https://help.semmle.com/wiki/display/JS/Insecure+randomness - [source](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll) -- https://vulncat.fortify.com/en/detail?id=desc.semantic.abap.insecure_randomness#JavaScript -- https://rules.sonarsource.com/javascript/RSPEC-2245 +* https://vulncat.fortify.com/en/detail?id=desc.semantic.abap.insecure_randomness#JavaScript +* https://rules.sonarsource.com/javascript/RSPEC-2245 - [source](https://github.com/SonarSource/SonarJS/blob/master/eslint-bridge/src/rules/pseudo-random.ts) -- https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-pseudoRandomBytes.js -- https://github.com/gkouziik/eslint-plugin-security-node/blob/master/lib/rules/detect-insecure-randomness.js - +* https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-pseudoRandomBytes.js +* https://github.com/gkouziik/eslint-plugin-security-node/blob/master/lib/rules/detect-insecure-randomness.js diff --git a/docs/rules/no-insecure-url.md b/docs/rules/no-insecure-url.md index 44de33d..6252ac5 100644 --- a/docs/rules/no-insecure-url.md +++ b/docs/rules/no-insecure-url.md @@ -6,7 +6,9 @@ Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transf - [Rule Test](../../tests/lib/rules/no-insecure-url.js) ## Options + This rule comes with three [default lists](../../lib/rules/no-insecure-url.js#L13): + - **blocklist** - a RegEx list of insecure URL patterns. - **exceptions** - a RegEx list of common false positive patterns. For example, HTTP URLs to XML schemas are usually allowed as they are used as identifiers, not for establishing actual network connections. - **varExceptions** - a RegEx list of false positive patterns which a derivated from the variable name. For example, a variable that is called "insecureURL" which is used to test HTTP explicitly. @@ -14,7 +16,9 @@ This rule comes with three [default lists](../../lib/rules/no-insecure-url.js#L1 These lists can be overrided by providing options. --- + For example, providing these options... : + ```javascript "@microsoft/sdl/no-insecure-url": ["error", { "blocklist": ["^(http|ftp):\\/\\/", "^https:\\/\\/www\\.disallow-example\\.com"], @@ -24,21 +28,25 @@ For example, providing these options... : ``` ... overrides the internal blocklist, blocking the following URL patterns... : + - `http://`... - `ftp://`... - `https://www.disallow-example.com` ... and also overrides the internal exceptions list, allowing the following URL patterns as exceptions.: + - `http://schemas.microsoft.com` - `http://schemas.microsoft.com/sharepoint` - `http://schemas.microsoft.com/path/subpath` ... and also overrides the internal variable exceptions list, allowing the following declaration name patterns as exceptions.: + - `var insecureURL = "http://..."` - `var insecureWebsite = "http://..."` - ... URLs in neither the blocklist nor the exceptions list, are allowed: + - `telnet://`... - `ws://`... - ... @@ -46,16 +54,19 @@ URLs in neither the blocklist nor the exceptions list, are allowed: --- **Note**: The RegEx for the lists is provided within a string in a JSON. It is without delimiting slashes `/ /` and thus users cannot pass RegEx parameters. We make it case-insensitive after user input. Do not forget to escape characters: + ```javascript let pureRegex = /^https:\/\/www\.disallow-example\.com/; let regexInString = "^https:\\/\\/www\\.disallow-example\\.com"; ``` ## Related Rules -* [tslint-microsoft-contrib/no-http-string](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noHttpStringRule.ts) -* [CodeQL/InsecureDownloadCustomizations.qll](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll#L62) -* [DevSkim/DS137138](https://github.com/microsoft/DevSkim/blob/main/guidance/DS137138.md) -* [Fortify/insecure_transport](https://vulncat.fortify.com/en/detail?id=desc.config.java.insecure_transport#JavaScript%2fTypeScript) + +- [tslint-microsoft-contrib/no-http-string](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noHttpStringRule.ts) +- [CodeQL/InsecureDownloadCustomizations.qll](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll#L62) +- [DevSkim/DS137138](https://github.com/microsoft/DevSkim/blob/main/guidance/DS137138.md) +- [Fortify/insecure_transport](https://vulncat.fortify.com/en/detail?id=desc.config.java.insecure_transport#JavaScript%2fTypeScript) ## Further Reading -* [HTTPS Everywhere](https://en.wikipedia.org/wiki/HTTPS_Everywhere) + +- [HTTPS Everywhere](https://en.wikipedia.org/wiki/HTTPS_Everywhere) diff --git a/docs/rules/no-postmessage-star-origin.md b/docs/rules/no-postmessage-star-origin.md index f756bd9..d500919 100644 --- a/docs/rules/no-postmessage-star-origin.md +++ b/docs/rules/no-postmessage-star-origin.md @@ -1,3 +1,3 @@ -# Do not use * as target origin when sending data to other windows (no-postmessage-star-origin) +# Do not use \* as target origin when sending data to other windows (no-postmessage-star-origin) -Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. +Always provide specific target origin, not \* when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. diff --git a/docs/rules/no-unsafe-alloc.md b/docs/rules/no-unsafe-alloc.md index 21892f6..1712fb1 100644 --- a/docs/rules/no-unsafe-alloc.md +++ b/docs/rules/no-unsafe-alloc.md @@ -4,9 +4,9 @@ When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_st These methods should be used only in justifiable cases (e.g. due to performance reasons) after thorough security review. -* [Rule Source](../../lib/rules/no-unsafe-alloc.js) -* [Rule Test](../../tests/lib/rules/no-unsafe-alloc.js) +- [Rule Source](../../lib/rules/no-unsafe-alloc.js) +- [Rule Test](../../tests/lib/rules/no-unsafe-alloc.js) ## Resources -* [Node.js - What makes Buffer.allocUnsafe() and Buffer.allocUnsafeSlow() "unsafe"?](https://nodejs.org/api/buffer.html#buffer_what_makes_buffer_allocunsafe_and_buffer_allocunsafeslow_unsafe) +- [Node.js - What makes Buffer.allocUnsafe() and Buffer.allocUnsafeSlow() "unsafe"?](https://nodejs.org/api/buffer.html#buffer_what_makes_buffer_allocunsafe_and_buffer_allocunsafeslow_unsafe) diff --git a/docs/rules/no-winjs-html-unsafe.md b/docs/rules/no-winjs-html-unsafe.md index 3e9af12..333623a 100644 --- a/docs/rules/no-winjs-html-unsafe.md +++ b/docs/rules/no-winjs-html-unsafe.md @@ -1,3 +1,3 @@ # Do not set HTML using unsafe methods from WinJS.Utilities (no-winjs-html-unsafe) -Calls to [`setInnerHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)), [`setOuterHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211698(v=win.10)) or [`insertAdjacentHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br229832(v=win.10)) methods from [Windows Library for JavaScript](https://docs.microsoft.com/en-us/previous-versions/windows/apps/mt502392(v=win.10)) do not perform input validation and should be avoided. Use alternate methods such as [`setInnerHTML`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead. \ No newline at end of file +Calls to [`setInnerHTMLUnsafe`](), [`setOuterHTMLUnsafe`]() or [`insertAdjacentHTMLUnsafe`]() methods from [Windows Library for JavaScript]() do not perform input validation and should be avoided. Use alternate methods such as [`setInnerHTML`]() instead. diff --git a/docs/rules/react-iframe-missing-sandbox.md b/docs/rules/react-iframe-missing-sandbox.md index 24dd407..9be6f53 100644 --- a/docs/rules/react-iframe-missing-sandbox.md +++ b/docs/rules/react-iframe-missing-sandbox.md @@ -4,14 +4,14 @@ The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute e Additional functionality such as the ability to run scripts should be enabled only in justifiable cases after thorough security review. -* [Rule Source](../../lib/rules/react-iframe-missing-sandbox.js) -* [Rule Test](../../tests/lib/rules/react-iframe-missing-sandbox.js) +- [Rule Source](../../lib/rules/react-iframe-missing-sandbox.js) +- [Rule Test](../../tests/lib/rules/react-iframe-missing-sandbox.js) ## Related Rules -* [tslint-microsoft-contrib/react-iframe-missing-sandbox](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/reactIframeMissingSandboxRule.ts) +- [tslint-microsoft-contrib/react-iframe-missing-sandbox](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/reactIframeMissingSandboxRule.ts) ## More Reading -* [How to safely inject HTML in React using an iframe](https://medium.com/the-thinkmill/how-to-safely-inject-html-in-react-using-an-iframe-adc775d458bc) -* [Play safely in sandboxed IFrames](https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/) \ No newline at end of file +- [How to safely inject HTML in React using an iframe](https://medium.com/the-thinkmill/how-to-safely-inject-html-in-react-using-an-iframe-adc775d458bc) +- [Play safely in sandboxed IFrames](https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/) diff --git a/lib/ast-utils.js b/lib/ast-utils.js index 734dd45..590d669 100644 --- a/lib/ast-utils.js +++ b/lib/ast-utils.js @@ -8,67 +8,62 @@ "use strict"; module.exports = { - isTypeScriptParserServices(parserServices) { - // Check properties specific to @typescript-eslint/parser + isTypeScriptParserServices(parserServices) { + // Check properties specific to @typescript-eslint/parser + return ( + parserServices && + parserServices.program && + parserServices.esTreeNodeToTSNodeMap && + parserServices.tsNodeToESTreeNodeMap + ); + }, + hasFullTypeInformation(context) { + var hasFullTypeInformation = + context && + context.sourceCode && + context.sourceCode.parserServices && + this.isTypeScriptParserServices(context.sourceCode.parserServices); + return hasFullTypeInformation; + }, + getFullTypeChecker(context) { + return this.hasFullTypeInformation(context) + ? context.sourceCode.parserServices.program.getTypeChecker() + : null; + }, + getNodeTypeAsString(fullTypeChecker, node, context) { + if (fullTypeChecker && node) { + const tsNode = context.sourceCode.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 type = this.getNodeTypeAsString(fullTypeChecker, node, context); + return type === "Document"; + } + + // Best-effort checking without Type information + switch (node.type) { + case "Identifier": + return node != undefined && node.name == "document"; + case "MemberExpression": return ( - parserServices && - parserServices.program && - parserServices.esTreeNodeToTSNodeMap && - parserServices.tsNodeToESTreeNodeMap + node != undefined && + node.property != undefined && + node.property.name == "document" && + ((node.object != undefined && + typeof node.object.name === "string" && + node.object.name.toLowerCase().endsWith("window")) || + (node.object != undefined && + node.object.property != undefined && + node.object.property.name == "window" && + ((node.object.object != undefined && node.object.object.type == "ThisExpression") || + (node.object.object != undefined && node.object.object.name == "globalThis")))) ); - }, - hasFullTypeInformation(context) { - var hasFullTypeInformation = ( - context && - context.sourceCode && - context.sourceCode.parserServices && - this.isTypeScriptParserServices(context.sourceCode.parserServices) - ); - return hasFullTypeInformation; - }, - getFullTypeChecker(context) { - return this.hasFullTypeInformation(context) ? context.sourceCode.parserServices.program.getTypeChecker() : null; - }, - getNodeTypeAsString(fullTypeChecker, node, context) { - if (fullTypeChecker && node) { - const tsNode = context.sourceCode.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 type = this.getNodeTypeAsString(fullTypeChecker, node, context); - return (type === "Document"); - } - - // Best-effort checking without Type information - switch (node.type) { - case "Identifier": - return node != undefined && node.name == "document"; - case "MemberExpression": - return ( - node != undefined && - node.property != undefined && - node.property.name == "document" && ( - (node.object != undefined && - typeof node.object.name === "string" && - node.object.name.toLowerCase().endsWith('window')) || - ( - node.object != undefined && - node.object.property != undefined && - node.object.property.name == "window" && - ( - - (node.object.object != undefined && node.object.object.type == "ThisExpression") || - (node.object.object != undefined && node.object.object.name == "globalThis") - ) - ) - ) - ); - }; - return false; } -}; \ No newline at end of file + return false; + }, +}; diff --git a/lib/index.js b/lib/index.js index 5c766f8..12f1f58 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,32 +9,32 @@ const pluginSecurity = require("eslint-plugin-security"); const pkg = require(path.join("..", "package.json")); const plugin = { - meta: { - name: pkg.name, - version: pkg.version - }, - rules: { - "no-angular-bypass-sanitizer": require("./rules/no-angular-bypass-sanitizer"), - "no-angular-sanitization-trusted-urls": require("./rules/no-angular-sanitization-trusted-urls"), - "no-angularjs-bypass-sce": require("./rules/no-angularjs-bypass-sce"), - "no-angularjs-enable-svg": require("./rules/no-angularjs-enable-svg"), - "no-angularjs-sanitization-whitelist": require("./rules/no-angularjs-sanitization-whitelist"), - "no-cookies": require("./rules/no-cookies"), - "no-document-domain": require("./rules/no-document-domain"), - "no-document-write": require("./rules/no-document-write"), - "no-electron-node-integration": require("./rules/no-electron-node-integration"), - "no-html-method": require("./rules/no-html-method"), - "no-inner-html": require("./rules/no-inner-html"), - "no-insecure-random": require("./rules/no-insecure-random"), - "no-insecure-url": require("./rules/no-insecure-url"), - "no-msapp-exec-unsafe": require("./rules/no-msapp-exec-unsafe"), - "no-postmessage-star-origin": require("./rules/no-postmessage-star-origin"), - "no-unsafe-alloc": require("./rules/no-unsafe-alloc"), - "no-winjs-html-unsafe": require("./rules/no-winjs-html-unsafe"), - "react-iframe-missing-sandbox": require("./rules/react-iframe-missing-sandbox"), - }, - // Filled in later in order to reference plugin itself. - configs: {} + meta: { + name: pkg.name, + version: pkg.version, + }, + rules: { + "no-angular-bypass-sanitizer": require("./rules/no-angular-bypass-sanitizer"), + "no-angular-sanitization-trusted-urls": require("./rules/no-angular-sanitization-trusted-urls"), + "no-angularjs-bypass-sce": require("./rules/no-angularjs-bypass-sce"), + "no-angularjs-enable-svg": require("./rules/no-angularjs-enable-svg"), + "no-angularjs-sanitization-whitelist": require("./rules/no-angularjs-sanitization-whitelist"), + "no-cookies": require("./rules/no-cookies"), + "no-document-domain": require("./rules/no-document-domain"), + "no-document-write": require("./rules/no-document-write"), + "no-electron-node-integration": require("./rules/no-electron-node-integration"), + "no-html-method": require("./rules/no-html-method"), + "no-inner-html": require("./rules/no-inner-html"), + "no-insecure-random": require("./rules/no-insecure-random"), + "no-insecure-url": require("./rules/no-insecure-url"), + "no-msapp-exec-unsafe": require("./rules/no-msapp-exec-unsafe"), + "no-postmessage-star-origin": require("./rules/no-postmessage-star-origin"), + "no-unsafe-alloc": require("./rules/no-unsafe-alloc"), + "no-winjs-html-unsafe": require("./rules/no-winjs-html-unsafe"), + "react-iframe-missing-sandbox": require("./rules/react-iframe-missing-sandbox"), + }, + // Filled in later in order to reference plugin itself. + configs: {}, }; plugin.configs["angular"] = require("../config/angular")(plugin); @@ -46,23 +46,22 @@ plugin.configs["react"] = require("../config/react")(plugin); plugin.configs["typescript"] = require("../config/react")(plugin); plugin.configs["required"] = [ - ...plugin.configs["angular"], - ...plugin.configs["angularjs"], - ...plugin.configs["common"], - ...plugin.configs["electron"], - ...plugin.configs["node"], - ...plugin.configs["react"] + ...plugin.configs["angular"], + ...plugin.configs["angularjs"], + ...plugin.configs["common"], + ...plugin.configs["electron"], + ...plugin.configs["node"], + ...plugin.configs["react"], ]; plugin.configs["recommended"] = [ - ...plugin.configs["required"], - ...plugin.configs["typescript"], - { - plugins: { - security: pluginSecurity - } - } + ...plugin.configs["required"], + ...plugin.configs["typescript"], + { + plugins: { + security: pluginSecurity, + }, + }, ]; - module.exports = plugin; diff --git a/lib/rules/no-angular-bypass-sanitizer.js b/lib/rules/no-angular-bypass-sanitizer.js index 3493ee9..36d1e3f 100644 --- a/lib/rules/no-angular-bypass-sanitizer.js +++ b/lib/rules/no-angular-bypass-sanitizer.js @@ -8,32 +8,31 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass DomSanitizer in Angular and need to be reviewed.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-bypass-sanitizer.md" - }, - messages: { - noBypass: "Do not bypass Angular's built-in sanitizer" - } + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass DomSanitizer in Angular and need to be reviewed.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-bypass-sanitizer.md", }, - create: function(context) { - return { - "CallExpression[arguments!=''][callee.property.name=/bypassSecurityTrust(Html|ResourceUrl|Script|Style|Url)/]"(node) { - context.report( - { - node: node, - messageId: "noBypass" - }); - } - }; - } -}; \ No newline at end of file + messages: { + noBypass: "Do not bypass Angular's built-in sanitizer", + }, + }, + create: function (context) { + return { + "CallExpression[arguments!=''][callee.property.name=/bypassSecurityTrust(Html|ResourceUrl|Script|Style|Url)/]"( + node, + ) { + context.report({ + node: node, + messageId: "noBypass", + }); + }, + }; + }, +}; diff --git a/lib/rules/no-angular-sanitization-trusted-urls.js b/lib/rules/no-angular-sanitization-trusted-urls.js index d2a7503..05493d4 100644 --- a/lib/rules/no-angular-sanitization-trusted-urls.js +++ b/lib/rules/no-angular-sanitization-trusted-urls.js @@ -6,34 +6,33 @@ * @author Vivien Flouirac */ - "use strict"; +"use strict"; - //------------------------------------------------------------------------------ - // Rule Definition - //------------------------------------------------------------------------------ - module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "Calls to [`$compileProvider.aHrefSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationTrustedUrlList) configure allowed Url list in AngularJS sanitizer and need to be reviewed.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-sanitization-trusted-urls.md" - }, - messages: { - noSanitizationTrustedUrls: "Do not modify the trusted Urls list in AngularJS" - } - }, - create: function(context) { - return { - "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationTrustedUrlList/]"(node) { - context.report( - { - node: node, - messageId: "noSanitizationTrustedUrls" - }); - } - }; - } - }; \ No newline at end of file +module.exports = { + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "Calls to [`$compileProvider.aHrefSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationTrustedUrlList) configure allowed Url list in AngularJS sanitizer and need to be reviewed.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-sanitization-trusted-urls.md", + }, + messages: { + noSanitizationTrustedUrls: "Do not modify the trusted Urls list in AngularJS", + }, + }, + create: function (context) { + return { + "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationTrustedUrlList/]"( + node, + ) { + context.report({ + node: node, + messageId: "noSanitizationTrustedUrls", + }); + }, + }; + }, +}; diff --git a/lib/rules/no-angularjs-bypass-sce.js b/lib/rules/no-angularjs-bypass-sce.js index a4e7019..25271bc 100644 --- a/lib/rules/no-angularjs-bypass-sce.js +++ b/lib/rules/no-angularjs-bypass-sce.js @@ -8,56 +8,62 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "Calls to $sceProvider.enabled(false), $sceDelegate.trustAs(), $sce.trustAs() and relevant shorthand methods (e.g. trustAsHtml or trustAsJs) bypass Strict Contextual Escaping (SCE) in AngularJS and need to be reviewed.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-bypass-sce.md" - }, - messages: { - doNotBypass: "Do not bypass Strict Contextual Escaping (SCE) in AngularJS" - } + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "Calls to $sceProvider.enabled(false), $sceDelegate.trustAs(), $sce.trustAs() and relevant shorthand methods (e.g. trustAsHtml or trustAsJs) bypass Strict Contextual Escaping (SCE) in AngularJS and need to be reviewed.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-bypass-sce.md", + }, + messages: { + doNotBypass: "Do not bypass Strict Contextual Escaping (SCE) in AngularJS", }, - create: function (context) { + }, + create: function (context) { + function reportIt(node) { + context.report({ + node: node, + messageId: "doNotBypass", + }); + } - function reportIt(node) { - context.report({ - node: node, - messageId: "doNotBypass" - }); + return { + "CallExpression[arguments!=''][callee.object.name='$sceProvider'][callee.property.name='enabled']"( + node, + ) { + // Known false positives + if ( + node.arguments == undefined || + node.arguments.length != 1 || + (node.arguments[0].type == "Literal" && /true|1/.test(node.arguments[0].value)) + ) { + return; + } + return reportIt(node); + }, + "CallExpression[arguments!=''][callee.object.name='$sceDelegate'][callee.property.name='trustAs']": + reportIt, + "CallExpression[arguments!=''][callee.object.name='$sce'][callee.property.name=/trustAs(Css|Html|Js|ResourceUrl|Url)?/]"( + node, + ) { + // Known false positives + if ( + node.arguments && + node.arguments.length === 1 && + node.arguments[0].type === "Literal" && + node.arguments[0].value === "" + ) { + return; } - return { - "CallExpression[arguments!=''][callee.object.name='$sceProvider'][callee.property.name='enabled']"(node) { - // Known false positives - if (node.arguments == undefined || node.arguments.length != 1 || (node.arguments[0].type == "Literal" && /true|1/.test(node.arguments[0].value))){ - return; - } - return reportIt(node) - }, - "CallExpression[arguments!=''][callee.object.name='$sceDelegate'][callee.property.name='trustAs']": reportIt, - "CallExpression[arguments!=''][callee.object.name='$sce'][callee.property.name=/trustAs(Css|Html|Js|ResourceUrl|Url)?/]"(node) { - // Known false positives - if ( - node.arguments - && node.arguments.length === 1 - && node.arguments[0].type === "Literal" - && node.arguments[0].value === "" - ) { - return; - } - - return reportIt(node); - } - }; - } + return reportIt(node); + }, + }; + }, }; // TODO: Review https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlWhitelist and https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlBlacklist diff --git a/lib/rules/no-angularjs-enable-svg.js b/lib/rules/no-angularjs-enable-svg.js index d2a680d..adaee84 100644 --- a/lib/rules/no-angularjs-enable-svg.js +++ b/lib/rules/no-angularjs-enable-svg.js @@ -8,46 +8,41 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "Calls to $sanitizeProvider.enableSvg(true) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-enable-svg.md" - }, - messages: { - doNotEnableSVG: "Do not enable SVG support in AngularJS" - } + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "Calls to $sanitizeProvider.enableSvg(true) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-enable-svg.md", + }, + messages: { + doNotEnableSVG: "Do not enable SVG support in AngularJS", }, - create: function (context) { - return { - "CallExpression[callee.object.name='$sanitizeProvider'][callee.property.name='enableSvg']"(node) { - // Known false positives - if ( - (node.arguments != undefined && - node.arguments.length != 1) || - ( - node.arguments[0].type == "Literal" && ( - node.arguments[0].value == "false" || node.arguments[0].value == "0" - ) - )) - { - return; - } - context.report( - { - node: node, - messageId: "doNotEnableSVG" - }); - } - }; - } + }, + create: function (context) { + return { + "CallExpression[callee.object.name='$sanitizeProvider'][callee.property.name='enableSvg']"( + node, + ) { + // Known false positives + if ( + (node.arguments != undefined && node.arguments.length != 1) || + (node.arguments[0].type == "Literal" && + (node.arguments[0].value == "false" || node.arguments[0].value == "0")) + ) { + return; + } + context.report({ + node: node, + messageId: "doNotEnableSVG", + }); + }, + }; + }, }; -// TODO: Add rules for $sanitizeProvider.addValidElements() and $sanitizeProvider.addValidAttrs() \ No newline at end of file +// TODO: Add rules for $sanitizeProvider.addValidElements() and $sanitizeProvider.addValidAttrs() diff --git a/lib/rules/no-angularjs-sanitization-whitelist.js b/lib/rules/no-angularjs-sanitization-whitelist.js index be2fd78..fa7e40b 100644 --- a/lib/rules/no-angularjs-sanitization-whitelist.js +++ b/lib/rules/no-angularjs-sanitization-whitelist.js @@ -8,32 +8,31 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-sanitization-whitelist.md" - }, - messages: { - noSanitizationWhitelist: "Do not modify sanitization whitelist in AngularJS" - } + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-sanitization-whitelist.md", }, - create: function (context) { - return { - "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationWhitelist/]"(node) { - context.report( - { - node: node, - messageId: "noSanitizationWhitelist" - }); - } - }; - } -}; \ No newline at end of file + messages: { + noSanitizationWhitelist: "Do not modify sanitization whitelist in AngularJS", + }, + }, + create: function (context) { + return { + "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationWhitelist/]"( + node, + ) { + context.report({ + node: node, + messageId: "noSanitizationWhitelist", + }); + }, + }; + }, +}; diff --git a/lib/rules/no-cookies.js b/lib/rules/no-cookies.js index 19fc215..04428e1 100644 --- a/lib/rules/no-cookies.js +++ b/lib/rules/no-cookies.js @@ -10,9 +10,6 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", @@ -20,12 +17,13 @@ module.exports = { schema: [], docs: { category: "Security", - description: "HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other more modern methods instead.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-cookies.md" + description: + "HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other more modern methods instead.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-cookies.md", }, messages: { - doNotUseCookies: "Do not use HTTP cookies in modern applications" - } + doNotUseCookies: "Do not use HTTP cookies in modern applications", + }, }, create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); @@ -34,10 +32,10 @@ module.exports = { if (astUtils.isDocumentObject(node.object, context, fullTypeChecker)) { context.report({ node: node, - messageId: "doNotUseCookies" + messageId: "doNotUseCookies", }); } - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-document-domain.js b/lib/rules/no-document-domain.js index 32af7ea..f49a75b 100644 --- a/lib/rules/no-document-domain.js +++ b/lib/rules/no-document-domain.js @@ -10,9 +10,6 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", @@ -20,25 +17,25 @@ module.exports = { schema: [], docs: { category: "Security", - description: "Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-domain.md" + description: + "Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-domain.md", }, messages: { - default: 'Do not write to document.domain property' - } + default: "Do not write to document.domain property", + }, }, - create: function(context) { + create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); return { "AssignmentExpression[operator='='][left.property.name='domain']"(node) { if (astUtils.isDocumentObject(node.left.object, context, fullTypeChecker)) { - context.report( - { + context.report({ node: node, - messageId: "default" + messageId: "default", }); } - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-document-write.js b/lib/rules/no-document-write.js index 20b36b1..b1b2f8d 100644 --- a/lib/rules/no-document-write.js +++ b/lib/rules/no-document-write.js @@ -10,9 +10,6 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", @@ -20,25 +17,25 @@ module.exports = { schema: [], docs: { category: "Security", - description: "Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-write.md" + description: + "Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-write.md", }, messages: { - default: 'Do not write to DOM directly using document.write or document.writeln methods' - } + default: "Do not write to DOM directly using document.write or document.writeln methods", + }, }, - create: function(context) { + create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); return { "CallExpression[arguments.length=1][callee.property.name=/write|writeln/]"(node) { if (astUtils.isDocumentObject(node.callee.object, context, fullTypeChecker)) { - context.report( - { + context.report({ node: node, - messageId: "default" + messageId: "default", }); } - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-electron-node-integration.js b/lib/rules/no-electron-node-integration.js index 5a3d1cb..8eda6bb 100644 --- a/lib/rules/no-electron-node-integration.js +++ b/lib/rules/no-electron-node-integration.js @@ -7,32 +7,31 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { - meta: { - type: "suggestion", - fixable: "code", - schema: [], - docs: { - category: "Security", - description: "[Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-electron-node-integration.md" - }, - messages: { - default: "Do not enable Node.js Integration for Remote Content" - } + meta: { + type: "suggestion", + fixable: "code", + schema: [], + docs: { + category: "Security", + description: + "[Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-electron-node-integration.md", }, - create: function(context) { - return { - "NewExpression[callee.name=/BrowserWindow|BrowserView/] > ObjectExpression.arguments > Property.properties[key.name=webPreferences] > ObjectExpression.value > Property.properties[key.name=/nodeIntegration|nodeIntegrationInWorker|nodeIntegrationInSubFrames/][value.value='true']"(node) { - context.report( - { - node: node, - messageId: "default" - }); - } - }; - } -}; \ No newline at end of file + messages: { + default: "Do not enable Node.js Integration for Remote Content", + }, + }, + create: function (context) { + return { + "NewExpression[callee.name=/BrowserWindow|BrowserView/] > ObjectExpression.arguments > Property.properties[key.name=webPreferences] > ObjectExpression.value > Property.properties[key.name=/nodeIntegration|nodeIntegrationInWorker|nodeIntegrationInSubFrames/][value.value='true']"( + node, + ) { + context.report({ + node: node, + messageId: "default", + }); + }, + }; + }, +}; diff --git a/lib/rules/no-html-method.js b/lib/rules/no-html-method.js index ef5e939..55aeed6 100644 --- a/lib/rules/no-html-method.js +++ b/lib/rules/no-html-method.js @@ -10,26 +10,24 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], - docs:{ - description: "Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-html-method.md" + docs: { + description: + "Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-html-method.md", }, messages: { - default: 'Do not write to DOM directly using jQuery html() method' - } + default: "Do not write to DOM directly using jQuery html() method", + }, }, - create: function(context) { + create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); return { - // TODO: + // TODO: // - Cover similar methods that can manipulate DOM such as append(string), jQuery(string) // - Improve rule with type information from TypeScript parser // - Consider ignoring all Literals? @@ -37,20 +35,16 @@ module.exports = { // Known false positives if ( // element.html("") - node.parent.arguments[0].type === "Literal" - && ( - node.parent.arguments[0].value === "" - || node.parent.arguments[0].value === null - ) + node.parent.arguments[0].type === "Literal" && + (node.parent.arguments[0].value === "" || node.parent.arguments[0].value === null) ) { return; } - context.report( - { + context.report({ node: node, - messageId: "default" - }); - } + messageId: "default", + }); + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-inner-html.js b/lib/rules/no-inner-html.js index 24d6547..99aa169 100644 --- a/lib/rules/no-inner-html.js +++ b/lib/rules/no-inner-html.js @@ -10,22 +10,20 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], docs: { - description: "Assignments to [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)/[outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) properties or calls to [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-inner-html.md" + description: + "Assignments to [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)/[outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) properties or calls to [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-inner-html.md", }, messages: { - noInnerHtml: 'Do not write to DOM directly using innerHTML/outerHTML property', - noInsertAdjacentHTML: 'Do not write to DOM using insertAdjacentHTML method' - } + noInnerHtml: "Do not write to DOM directly using innerHTML/outerHTML property", + noInsertAdjacentHTML: "Do not write to DOM using insertAdjacentHTML method", + }, }, create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); @@ -36,15 +34,18 @@ module.exports = { } return { - "CallExpression[arguments.length=2] > MemberExpression.callee[property.name='insertAdjacentHTML']"(node) { + "CallExpression[arguments.length=2] > MemberExpression.callee[property.name='insertAdjacentHTML']"( + node, + ) { // Ignore known false positives if ( - node.parent != undefined - && node.parent.arguments != undefined - && node.parent.arguments.length >= 1 - && node.parent.arguments[1] != undefined + node.parent != undefined && + node.parent.arguments != undefined && + node.parent.arguments.length >= 1 && + node.parent.arguments[1] != undefined && // element.insertAdjacentHTML('') - && node.parent.arguments[1].type === 'Literal' && node.parent.arguments[1].value === '' + node.parent.arguments[1].type === "Literal" && + node.parent.arguments[1].value === "" ) { return; } @@ -52,16 +53,19 @@ module.exports = { if (mightBeHTMLElement(node.object)) { context.report({ node: node, - messageId: "noInsertAdjacentHTML" + messageId: "noInsertAdjacentHTML", }); } }, - "AssignmentExpression[left.type='MemberExpression'][left.property.name=/innerHTML|outerHTML/]"(node) { + "AssignmentExpression[left.type='MemberExpression'][left.property.name=/innerHTML|outerHTML/]"( + node, + ) { // Ignore known false positives if ( - node.right != undefined + node.right != undefined && // element.innerHTML = '' - && node.right.type === 'Literal' && node.right.value === '' + node.right.type === "Literal" && + node.right.value === "" ) { return; } @@ -69,10 +73,10 @@ module.exports = { if (mightBeHTMLElement(node.left.object)) { context.report({ node: node, - messageId: "noInnerHtml" + messageId: "noInnerHtml", }); } - } + }, }; - } + }, }; diff --git a/lib/rules/no-insecure-random.js b/lib/rules/no-insecure-random.js index 644b5ff..2c77d31 100644 --- a/lib/rules/no-insecure-random.js +++ b/lib/rules/no-insecure-random.js @@ -9,38 +9,36 @@ "use strict"; const astUtils = require("../ast-utils"); -const path = require('path'); +const path = require("path"); const bannedRandomLibraries = [ - 'chance', - 'random-number', - 'random-int', - 'random-float', - 'random-seed', - 'unique-random' -] + "chance", + "random-number", + "random-int", + "random-float", + "random-seed", + "unique-random", +]; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], - docs:{ + docs: { description: `Methods such as Math.random or crypto.pseudoRandomBytes do not produce cryptographically-secure random numbers and must not be used for security purposes such as generating tokens, passwords or keys. Use crypto.randomBytes() or window.crypto.getRandomValues() instead. `, - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-random.md" + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-random.md", }, messages: { - default: 'Do not use pseudo-random number generators for generating secret values such as tokens, passwords or keys.' - } + default: + "Do not use pseudo-random number generators for generating secret values such as tokens, passwords or keys.", + }, }, - create: function(context) { + create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); return { "CallExpression > MemberExpression[property.name='pseudoRandomBytes']"(node) { @@ -49,50 +47,50 @@ module.exports = { if (fullTypeChecker) { const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); notFalsePositive = type === "any" || type === "Crypto"; - }else{ - notFalsePositive = node.object.name === 'crypto'; + } else { + notFalsePositive = node.object.name === "crypto"; } - if(notFalsePositive){ + if (notFalsePositive) { context.report({ node: node, - messageId: "default" + messageId: "default", }); - } + } }, "CallExpression > MemberExpression[property.name='random']"(node) { var notFalsePositive = false; if (fullTypeChecker) { const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); notFalsePositive = type === "any" || type === "Math"; - }else{ - notFalsePositive = node.object.name === 'Math'; + } else { + notFalsePositive = node.object.name === "Math"; } - if(notFalsePositive){ + if (notFalsePositive) { context.report({ node: node, - messageId: "default" + messageId: "default", }); } }, - ImportDeclaration(node){ - if(bannedRandomLibraries.includes(path.basename(node.source.value))){ + ImportDeclaration(node) { + if (bannedRandomLibraries.includes(path.basename(node.source.value))) { context.report({ node: node, - messageId: "default" - }); + messageId: "default", + }); } }, - "CallExpression[callee.name='require'][arguments.length=1]"(node){ + "CallExpression[callee.name='require'][arguments.length=1]"(node) { var requireName = path.parse(path.basename(node.arguments[0].value)).name; - if(bannedRandomLibraries.includes(requireName)){ + if (bannedRandomLibraries.includes(requireName)) { context.report({ node: node, - messageId: "default" - }); + messageId: "default", + }); } - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-insecure-url.js b/lib/rules/no-insecure-url.js index a5b9057..49209d8 100644 --- a/lib/rules/no-insecure-url.js +++ b/lib/rules/no-insecure-url.js @@ -1,139 +1,159 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/** - * @fileoverview Disallows usage of insecure protocols in URL strings - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ -const DEFAULT_BLOCKLIST = [ - /^(ftp|http|telnet|ws):\/\//i -]; - -const DEFAULT_EXCEPTIONS = [ // TODO: add more typical false positives such as XML schemas after more testing - /^http:(\/\/|\\u002f\\u002f)schemas\.microsoft\.com(\/\/|\\u002f\\u002f)?.*/i, - /^http:(\/\/|\\u002f\\u002f)schemas\.openxmlformats\.org(\/\/|\\u002f\\u002f)?.*/i, - /^http:(\/|\\u002f){2}localhost(:|\/|\\u002f)*/i, - /^http:(\/\/)www\.w3\.org\/1999\/xhtml/i, - /^http:(\/\/)www\.w3\.org\/2000\/svg/i -]; - -const DEFAULT_VARIABLES_EXECEPTIONS = []; - -module.exports = { - defaultBlocklist: DEFAULT_BLOCKLIST, - defaultExceptions: DEFAULT_EXCEPTIONS, - defaultVarExecptions: DEFAULT_VARIABLES_EXECEPTIONS, - meta: { - type: "suggestion", - fixable: "code", - schema: [ - { - type: "object", - properties: { - blocklist: { - type: "array", - items: { - type: "string" - } - }, - exceptions: { - type: "array", - items: { - type: "string" - } - }, - varExceptions: { - type: "array", - items: { - type: "string" - } - }, - }, - additionalProperties: false - } - ], - docs: { - category: "Security", - description: "Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending (potentially sensitive) data over untrusted network in plaintext.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-url.md" - }, - messages: { - doNotUseInsecureUrl: "Do not use insecure URLs" - } - }, - create: function (context) { - const options = context.options[0] || {}; - const blocklist = (options.blocklist || DEFAULT_BLOCKLIST).map((pattern) => { return new RegExp(pattern, "i"); }); - const exceptions = (options.exceptions || DEFAULT_EXCEPTIONS).map((pattern) => { return new RegExp(pattern, "i"); }); - const varExceptions = (options.varExceptions || DEFAULT_VARIABLES_EXECEPTIONS).map((pattern) => { return new RegExp(pattern, "i"); }); - - function matches(patterns, value) { - return patterns.find((re) => { return re.test(value) }) !== undefined; - } - - function shouldFix( varExceptions,context, node) { - // check variable for unfixable pattern e.g. `var insecureURL = "http://..."` - let text = node.parent - ? context.sourceCode.getText(node.parent) - : context.sourceCode.getText(node); - // if no match, fix the line - return !matches(varExceptions,text); - } - - return { - "Literal"(node) { - if (typeof node.value === "string") { - // Add an exception for xmlns attributes - if(node.parent && node.parent.type === "JSXAttribute" && node.parent.name && node.parent.name.name === "xmlns") - { - // Do nothing - } - else if (matches(blocklist, node.value) && !matches(exceptions, node.value) && shouldFix(varExceptions,context, node)) { - context.report({ - node: node, - messageId: "doNotUseInsecureUrl", - fix(fixer) { - // Only fix if it contains an http url - if (node.value.toLowerCase().includes("http")) { - let fixedString = node.value.replace(/http:/i, "https:"); - //insert an "s" before ":/" to change http:/ to https:/ - return fixer.replaceText(node, JSON.stringify(fixedString)); - } - }, - }); - } - } - }, - "TemplateElement"(node) { - if (typeof node.value.raw === "string" && typeof node.value.cooked === "string") { - const rawStringText = node.value.raw; - const cookedStringText = node.value.cooked; - - if (shouldFix(varExceptions,context, node) && (matches(blocklist, rawStringText) && !matches(exceptions, rawStringText)) || - (matches(blocklist, cookedStringText) && !matches(exceptions, cookedStringText))) { - context.report({ - node: node, - messageId: "doNotUseInsecureUrl", - fix(fixer) { - // Only fix if it contains an http url - if (node.value.raw.toLowerCase().includes("http")) { - let escapedString = JSON.stringify(context.sourceCode.getText(node)); - // delete "" that JSON.stringify created and convert to `` string - escapedString = ``+ escapedString.substring(1, escapedString.length-1); - let fixedString = escapedString.replace(/http:/i, "https:"); - //insert an "s" before ":/" to change http:/ to https:/ - return fixer.replaceText(node, fixedString); - } - } - }); - } - } - }, - }; - }, -}; \ No newline at end of file +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @fileoverview Disallows usage of insecure protocols in URL strings + */ + +"use strict"; + +const DEFAULT_BLOCKLIST = [/^(ftp|http|telnet|ws):\/\//i]; + +const DEFAULT_EXCEPTIONS = [ + // TODO: add more typical false positives such as XML schemas after more testing + /^http:(\/\/|\\u002f\\u002f)schemas\.microsoft\.com(\/\/|\\u002f\\u002f)?.*/i, + /^http:(\/\/|\\u002f\\u002f)schemas\.openxmlformats\.org(\/\/|\\u002f\\u002f)?.*/i, + /^http:(\/|\\u002f){2}localhost(:|\/|\\u002f)*/i, + /^http:(\/\/)www\.w3\.org\/1999\/xhtml/i, + /^http:(\/\/)www\.w3\.org\/2000\/svg/i, +]; + +const DEFAULT_VARIABLES_EXECEPTIONS = []; + +module.exports = { + defaultBlocklist: DEFAULT_BLOCKLIST, + defaultExceptions: DEFAULT_EXCEPTIONS, + defaultVarExecptions: DEFAULT_VARIABLES_EXECEPTIONS, + meta: { + type: "suggestion", + fixable: "code", + schema: [ + { + type: "object", + properties: { + blocklist: { + type: "array", + items: { + type: "string", + }, + }, + exceptions: { + type: "array", + items: { + type: "string", + }, + }, + varExceptions: { + type: "array", + items: { + type: "string", + }, + }, + }, + additionalProperties: false, + }, + ], + docs: { + category: "Security", + description: + "Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending (potentially sensitive) data over untrusted network in plaintext.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-url.md", + }, + messages: { + doNotUseInsecureUrl: "Do not use insecure URLs", + }, + }, + create: function (context) { + const options = context.options[0] || {}; + const blocklist = (options.blocklist || DEFAULT_BLOCKLIST).map((pattern) => { + return new RegExp(pattern, "i"); + }); + const exceptions = (options.exceptions || DEFAULT_EXCEPTIONS).map((pattern) => { + return new RegExp(pattern, "i"); + }); + const varExceptions = (options.varExceptions || DEFAULT_VARIABLES_EXECEPTIONS).map( + (pattern) => { + return new RegExp(pattern, "i"); + }, + ); + + function matches(patterns, value) { + return ( + patterns.find((re) => { + return re.test(value); + }) !== undefined + ); + } + + function shouldFix(varExceptions, context, node) { + // check variable for unfixable pattern e.g. `var insecureURL = "http://..."` + let text = node.parent + ? context.sourceCode.getText(node.parent) + : context.sourceCode.getText(node); + // if no match, fix the line + return !matches(varExceptions, text); + } + + return { + Literal(node) { + if (typeof node.value === "string") { + // Add an exception for xmlns attributes + if ( + node.parent && + node.parent.type === "JSXAttribute" && + node.parent.name && + node.parent.name.name === "xmlns" + ) { + // Do nothing + } else if ( + matches(blocklist, node.value) && + !matches(exceptions, node.value) && + shouldFix(varExceptions, context, node) + ) { + context.report({ + node: node, + messageId: "doNotUseInsecureUrl", + fix(fixer) { + // Only fix if it contains an http url + if (node.value.toLowerCase().includes("http")) { + let fixedString = node.value.replace(/http:/i, "https:"); + //insert an "s" before ":/" to change http:/ to https:/ + return fixer.replaceText(node, JSON.stringify(fixedString)); + } + }, + }); + } + } + }, + TemplateElement(node) { + if (typeof node.value.raw === "string" && typeof node.value.cooked === "string") { + const rawStringText = node.value.raw; + const cookedStringText = node.value.cooked; + + if ( + (shouldFix(varExceptions, context, node) && + matches(blocklist, rawStringText) && + !matches(exceptions, rawStringText)) || + (matches(blocklist, cookedStringText) && !matches(exceptions, cookedStringText)) + ) { + context.report({ + node: node, + messageId: "doNotUseInsecureUrl", + fix(fixer) { + // Only fix if it contains an http url + if (node.value.raw.toLowerCase().includes("http")) { + let escapedString = JSON.stringify(context.sourceCode.getText(node)); + // delete "" that JSON.stringify created and convert to `` string + escapedString = `` + escapedString.substring(1, escapedString.length - 1); + let fixedString = escapedString.replace(/http:/i, "https:"); + //insert an "s" before ":/" to change http:/ to https:/ + return fixer.replaceText(node, fixedString); + } + }, + }); + } + } + }, + }; + }, +}; diff --git a/lib/rules/no-msapp-exec-unsafe.js b/lib/rules/no-msapp-exec-unsafe.js index a6d2189..bb1203d 100644 --- a/lib/rules/no-msapp-exec-unsafe.js +++ b/lib/rules/no-msapp-exec-unsafe.js @@ -8,31 +8,30 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], - docs:{ - description: "Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-msapp-exec-unsafe.md" + docs: { + description: + "Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-msapp-exec-unsafe.md", }, messages: { - default: 'Do not bypass script injection validation' - } + default: "Do not bypass script injection validation", + }, }, - create: function(context) { + create: function (context) { return { - "CallExpression[arguments.length=1][callee.object.name='MSApp'][callee.property.name='execUnsafeLocalFunction']"(node) { - context.report( - { - node: node, - messageId: "default" + "CallExpression[arguments.length=1][callee.object.name='MSApp'][callee.property.name='execUnsafeLocalFunction']"( + node, + ) { + context.report({ + node: node, + messageId: "default", }); - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-postmessage-star-origin.js b/lib/rules/no-postmessage-star-origin.js index d8c5682..5fdda1a 100644 --- a/lib/rules/no-postmessage-star-origin.js +++ b/lib/rules/no-postmessage-star-origin.js @@ -9,35 +9,36 @@ const astUtils = require("../ast-utils"); -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], docs: { - description: "Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-postmessage-star-origin.md" + description: + "Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-postmessage-star-origin.md", }, messages: { - default: 'Do not use * as target origin when sending data to other windows' - } + default: "Do not use * as target origin when sending data to other windows", + }, }, create: function (context) { const fullTypeChecker = astUtils.getFullTypeChecker(context); return { - "CallExpression[arguments.length>=2][arguments.length<=3][callee.property.name=postMessage]"(node) { - + "CallExpression[arguments.length>=2][arguments.length<=3][callee.property.name=postMessage]"( + node, + ) { // Check that second argument (target origin) is Literal "*" - if (!(node.arguments[1].type === 'Literal' && node.arguments[1].value == '*')) { + if (!(node.arguments[1].type === "Literal" && node.arguments[1].value == "*")) { return; } // Check that object type is Window when full type information is available if (fullTypeChecker) { - const tsNode = context.sourceCode.parserServices.esTreeNodeToTSNodeMap.get(node.callee.object); + const tsNode = context.sourceCode.parserServices.esTreeNodeToTSNodeMap.get( + node.callee.object, + ); const tsType = fullTypeChecker.getTypeAtLocation(tsNode); const type = fullTypeChecker.typeToString(tsType); if (type !== "any" && type !== "Window") { @@ -47,9 +48,9 @@ module.exports = { context.report({ node: node, - messageId: "default" + messageId: "default", }); - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-unsafe-alloc.js b/lib/rules/no-unsafe-alloc.js index 51e8a7c..bd0b7c7 100644 --- a/lib/rules/no-unsafe-alloc.js +++ b/lib/rules/no-unsafe-alloc.js @@ -3,21 +3,19 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], docs: { - description: "When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-unsafe-alloc.md" + description: + "When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-unsafe-alloc.md", }, messages: { - default: 'Do not allocate uninitialized buffers in Node.js' - } + default: "Do not allocate uninitialized buffers in Node.js", + }, }, create: function (context) { return { @@ -28,19 +26,19 @@ module.exports = { node.parent.arguments != undefined && node.parent.arguments.length != undefined && // Buffer.allocUnsafe(0); - node.parent.type === 'CallExpression' && - node.parent.arguments.length == 1 && + node.parent.type === "CallExpression" && + node.parent.arguments.length == 1 && node.parent.arguments[0] != undefined && - node.parent.arguments[0].type === 'Literal' && - node.parent.arguments[0].value == '0' + node.parent.arguments[0].type === "Literal" && + node.parent.arguments[0].value == "0" ) { return; } context.report({ node: node, - messageId: "default" + messageId: "default", }); - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/no-winjs-html-unsafe.js b/lib/rules/no-winjs-html-unsafe.js index 4884469..fa74734 100644 --- a/lib/rules/no-winjs-html-unsafe.js +++ b/lib/rules/no-winjs-html-unsafe.js @@ -8,31 +8,30 @@ "use strict"; -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", fixable: "code", schema: [], - docs:{ - description: "Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)) and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-winjs-html-unsafe.md" + docs: { + description: + "Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)) and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-winjs-html-unsafe.md", }, messages: { - default: 'Do not set HTML using unsafe methods from WinJS.Utilities' - } + default: "Do not set HTML using unsafe methods from WinJS.Utilities", + }, }, - create: function(context) { + create: function (context) { return { - "CallExpression[callee.object.object.name='WinJS'][callee.object.property.name='Utilities'][callee.property.name=/(insertAdjacent|setInner|setOuter)HTMLUnsafe/]"(node) { - context.report( - { + "CallExpression[callee.object.object.name='WinJS'][callee.object.property.name='Utilities'][callee.property.name=/(insertAdjacent|setInner|setOuter)HTMLUnsafe/]"( + node, + ) { + context.report({ node: node, - messageId: "default" + messageId: "default", }); - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/lib/rules/react-iframe-missing-sandbox.js b/lib/rules/react-iframe-missing-sandbox.js index a62655b..02ef7fe 100644 --- a/lib/rules/react-iframe-missing-sandbox.js +++ b/lib/rules/react-iframe-missing-sandbox.js @@ -9,9 +9,6 @@ // TODO: Follow-up on https://github.com/yannickcr/eslint-plugin-react/issues/2754 and try to merge rule into eslint-plugin-react -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ module.exports = { meta: { type: "suggestion", @@ -19,35 +16,38 @@ module.exports = { schema: [], docs: { category: "Security", - description: "The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified.", - url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/react-iframe-missing-sandbox.md" + description: + "The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified.", + url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/react-iframe-missing-sandbox.md", }, messages: { - attributeMissing: 'An iframe element is missing a sandbox attribute', - invalidValue: 'An iframe element defines a sandbox attribute with invalid value "{{ value }}"', - invalidCombination: 'An iframe element defines a sandbox attribute with both allow-scripts and allow-same-origin which is invalid' - } + attributeMissing: "An iframe element is missing a sandbox attribute", + invalidValue: + 'An iframe element defines a sandbox attribute with invalid value "{{ value }}"', + invalidCombination: + "An iframe element defines a sandbox attribute with both allow-scripts and allow-same-origin which is invalid", + }, }, create(context) { const ALLOWED_VALUES = [ // From https://www.w3schools.com/tags/att_iframe_sandbox.asp - '', - 'allow-forms', - 'allow-modals', - 'allow-orientation-lock', - 'allow-pointer-lock', - 'allow-popups', - 'allow-popups-to-escape-sandbox', - 'allow-presentation', - 'allow-same-origin', - 'allow-scripts', - 'allow-top-navigation', - 'allow-top-navigation-by-user-activation' + "", + "allow-forms", + "allow-modals", + "allow-orientation-lock", + "allow-pointer-lock", + "allow-popups", + "allow-popups-to-escape-sandbox", + "allow-presentation", + "allow-same-origin", + "allow-scripts", + "allow-top-navigation", + "allow-top-navigation-by-user-activation", ]; function validateSandboxAttribute(node, attribute) { - const values = attribute.value.value.split(' '); + const values = attribute.value.value.split(" "); let allowScripts = false; let allowSameOrigin = false; values.forEach((attributeValue) => { @@ -55,23 +55,23 @@ module.exports = { if (ALLOWED_VALUES.indexOf(trimmedAttributeValue) === -1) { context.report({ node, - messageId: 'invalidValue', + messageId: "invalidValue", data: { - value: trimmedAttributeValue - } + value: trimmedAttributeValue, + }, }); } - if (trimmedAttributeValue === 'allow-scripts') { + if (trimmedAttributeValue === "allow-scripts") { allowScripts = true; } - if (trimmedAttributeValue === 'allow-same-origin') { + if (trimmedAttributeValue === "allow-same-origin") { allowSameOrigin = true; } }); if (allowScripts && allowSameOrigin) { context.report({ node, - messageId: 'invalidCombination' + messageId: "invalidCombination", }); } } @@ -80,17 +80,14 @@ module.exports = { 'JSXOpeningElement[name.name="iframe"]'(node) { let sandboxAttributeFound = false; node.attributes.forEach((attribute) => { - if (attribute.type === 'JSXAttribute' - && attribute.name - && attribute.name.type === 'JSXIdentifier' - && attribute.name.name === 'sandbox' + if ( + attribute.type === "JSXAttribute" && + attribute.name && + attribute.name.type === "JSXIdentifier" && + attribute.name.name === "sandbox" ) { sandboxAttributeFound = true; - if ( - attribute.value - && attribute.value.type === 'Literal' - && attribute.value.value - ) { + if (attribute.value && attribute.value.type === "Literal" && attribute.value.value) { // Only string literals are supported for now validateSandboxAttribute(node, attribute); } @@ -99,10 +96,10 @@ module.exports = { if (!sandboxAttributeFound) { context.report({ node, - messageId: 'attributeMissing' + messageId: "attributeMissing", }); } - } + }, }; - } -}; \ No newline at end of file + }, +}; diff --git a/package-lock.json b/package-lock.json index 9ea344c..0129500 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@typescript-eslint/parser": "~8.6.0", "eslint": "~9.11.0", "mocha": "~10.7.0", + "prettier": "~3.3.0", "typescript": "~5.5.0" }, "engines": { @@ -2728,6 +2729,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index 384cd54..41aa73c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "bugs": "https://github.com/microsoft/eslint-plugin-sdl/issues", "main": "lib/index.js", "scripts": { + "check-fmt": "prettier . --check", + "fmt": "prettier . --write", "test": "mocha tests --recursive" }, "dependencies": { @@ -29,6 +31,7 @@ "@typescript-eslint/parser": "~8.6.0", "eslint": "~9.11.0", "mocha": "~10.7.0", + "prettier": "~3.3.0", "typescript": "~5.5.0" }, "peerDependencies": { diff --git a/tests/fixtures/ts/tsconfig.json b/tests/fixtures/ts/tsconfig.json index 8e9b11d..ed4dc2f 100644 --- a/tests/fixtures/ts/tsconfig.json +++ b/tests/fixtures/ts/tsconfig.json @@ -1,5 +1,3 @@ { - "include": [ - "estree.ts" - ] -} \ No newline at end of file + "include": ["estree.ts"] +} diff --git a/tests/fixtures/tsx/tsconfig.json b/tests/fixtures/tsx/tsconfig.json index 9bbd92f..8aa1c5b 100644 --- a/tests/fixtures/tsx/tsconfig.json +++ b/tests/fixtures/tsx/tsconfig.json @@ -1,8 +1,6 @@ { - "compilerOptions": { - "jsx": "preserve" - }, - "include": [ - "estree.tsx" - ] -} \ No newline at end of file + "compilerOptions": { + "jsx": "preserve" + }, + "include": ["estree.tsx"] +} diff --git a/tests/lib/rules/no-angular-bypass-sanitizer.js b/tests/lib/rules/no-angular-bypass-sanitizer.js index 58bfb53..90a3387 100644 --- a/tests/lib/rules/no-angular-bypass-sanitizer.js +++ b/tests/lib/rules/no-angular-bypass-sanitizer.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -11,7 +11,7 @@ ruleTester.run(ruleId, rule, { valid: [ "bypassSecurityTrustHtml('XSS')", "x.bypassSecurityTrustHtml()", - "x.BypassSecurityTrustHtml('XSS')" + "x.BypassSecurityTrustHtml('XSS')", ], invalid: [ { @@ -22,25 +22,25 @@ ruleTester.run(ruleId, rule, { line: 1, endLine: 1, column: 1, - endColumn: 38 - } - ] + endColumn: 38, + }, + ], }, { code: "$('p').bypassSecurityTrustResourceUrl('XSS')", - errors: [{ messageId: "noBypass"}] + errors: [{ messageId: "noBypass" }], }, { code: "$('p').bypassSecurityTrustScript('XSS')", - errors: [{ messageId: "noBypass"}] + errors: [{ messageId: "noBypass" }], }, { code: "$('p').bypassSecurityTrustStyle('XSS')", - errors: [{ messageId: "noBypass"}] + errors: [{ messageId: "noBypass" }], }, { code: "$('p').bypassSecurityTrustUrl('XSS')", - errors: [{ messageId: "noBypass"}] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "noBypass" }], + }, + ], +}); diff --git a/tests/lib/rules/no-angular-sanitization-trusted-urls.js b/tests/lib/rules/no-angular-sanitization-trusted-urls.js index 7d3dddc..f3b5c97 100644 --- a/tests/lib/rules/no-angular-sanitization-trusted-urls.js +++ b/tests/lib/rules/no-angular-sanitization-trusted-urls.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -12,7 +12,7 @@ ruleTester.run(ruleId, rule, { "aHrefSanitizationTrustedUrlList ('.*')", "x.aHrefSanitizationTrustedUrlList ('.*')", "$compileProvider.aHrefSanitizationTrustedUrlList ()", - "$compileProvider.AHrefSanitizationTrustedUrlList ('.*')" + "$compileProvider.AHrefSanitizationTrustedUrlList ('.*')", ], invalid: [ { @@ -23,9 +23,9 @@ ruleTester.run(ruleId, rule, { line: 1, endLine: 1, column: 1, - endColumn: 56 - } - ] + endColumn: 56, + }, + ], }, { code: "$compileProvider.imgSrcSanitizationTrustedUrlList('.*');", @@ -35,9 +35,9 @@ ruleTester.run(ruleId, rule, { line: 1, endLine: 1, column: 1, - endColumn: 56 - } - ] - } - ] -}); \ No newline at end of file + endColumn: 56, + }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-angularjs-bypass-sce.js b/tests/lib/rules/no-angularjs-bypass-sce.js index dbb1eab..f85e9fb 100644 --- a/tests/lib/rules/no-angularjs-bypass-sce.js +++ b/tests/lib/rules/no-angularjs-bypass-sce.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -21,43 +21,43 @@ ruleTester.run(ruleId, rule, { invalid: [ { code: "$sceDelegate.trustAs($sce.HTML, 'XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAs($sce.HTML, 'XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAsCss('XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAsHtml('XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAsJs('XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAsResourceUrl('XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sce.trustAsUrl('XSS')", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sceProvider.enabled(false)", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sceProvider.enabled(0)", - errors: [{ messageId: "doNotBypass" }] + errors: [{ messageId: "doNotBypass" }], }, { code: "$sceProvider.enabled(true != true)", - errors: [{ messageId: "doNotBypass" }] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "doNotBypass" }], + }, + ], +}); diff --git a/tests/lib/rules/no-angularjs-enable-svg.js b/tests/lib/rules/no-angularjs-enable-svg.js index e41d3ac..3bde880 100644 --- a/tests/lib/rules/no-angularjs-enable-svg.js +++ b/tests/lib/rules/no-angularjs-enable-svg.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -14,16 +14,16 @@ ruleTester.run(ruleId, rule, { "$sanitizeProvider.enableSvg()", "$sanitizeProvider.enableSvg(false)", "$sanitizeProvider.enableSvg(0)", - "$sanitizeProvider.EnableSvg(0)" + "$sanitizeProvider.EnableSvg(0)", ], invalid: [ { code: "$sanitizeProvider.enableSvg(true)", - errors: [{ messageId: "doNotEnableSVG" }] + errors: [{ messageId: "doNotEnableSVG" }], }, { code: "$sanitizeProvider.enableSvg(1)", - errors: [{ messageId: "doNotEnableSVG" }] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "doNotEnableSVG" }], + }, + ], +}); diff --git a/tests/lib/rules/no-angularjs-sanitization-whitelist.js b/tests/lib/rules/no-angularjs-sanitization-whitelist.js index b391eeb..0e4cd6e 100644 --- a/tests/lib/rules/no-angularjs-sanitization-whitelist.js +++ b/tests/lib/rules/no-angularjs-sanitization-whitelist.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -12,7 +12,7 @@ ruleTester.run(ruleId, rule, { "aHrefSanitizationWhitelist('.*')", "x.aHrefSanitizationWhitelist('.*')", "$compileProvider.aHrefSanitizationWhitelist()", - "$compileProvider.AHrefSanitizationWhitelist('.*')" + "$compileProvider.AHrefSanitizationWhitelist('.*')", ], invalid: [ { @@ -23,9 +23,9 @@ ruleTester.run(ruleId, rule, { line: 1, endLine: 1, column: 1, - endColumn: 50 - } - ] + endColumn: 50, + }, + ], }, { code: "$compileProvider.imgSrcSanitizationWhitelist('.*');", @@ -35,9 +35,9 @@ ruleTester.run(ruleId, rule, { line: 1, endLine: 1, column: 1, - endColumn: 51 - } - ] - } - ] -}); \ No newline at end of file + endColumn: 51, + }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-cookies.js b/tests/lib/rules/no-cookies.js index f591c32..0a7c5c8 100644 --- a/tests/lib/rules/no-cookies.js +++ b/tests/lib/rules/no-cookies.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; const testUtils = require("../test-utils"); @@ -39,25 +39,25 @@ function X() { } documentLikeAPIFunction().cookie = '...'; -` - } +`, + }, ], invalid: [ { code: "document.cookie = '...'", - errors: [{ messageId: "doNotUseCookies" }] + errors: [{ messageId: "doNotUseCookies" }], }, { code: "window.document.cookie = '...'", - errors: [{ messageId: "doNotUseCookies" }] + errors: [{ messageId: "doNotUseCookies" }], }, { code: "this.window.document.cookie = '...'", - errors: [{ messageId: "doNotUseCookies" }] + errors: [{ messageId: "doNotUseCookies" }], }, { code: "globalThis.window.document.cookie = '...'", - errors: [{ messageId: "doNotUseCookies" }] + errors: [{ messageId: "doNotUseCookies" }], }, { languageOptions: testUtils.tsLanguageOptions, @@ -67,7 +67,7 @@ function documentFunction(): Document { } documentFunction().cookie = '...'; `, - errors: [{ messageId: "doNotUseCookies" }] + errors: [{ messageId: "doNotUseCookies" }], }, { languageOptions: testUtils.tsLanguageOptions, @@ -78,7 +78,7 @@ namespace Sample { } } `, - errors: [{ messageId: "doNotUseCookies" }] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "doNotUseCookies" }], + }, + ], +}); diff --git a/tests/lib/rules/no-document-domain.js b/tests/lib/rules/no-document-domain.js index 0a4704a..8fe8747 100644 --- a/tests/lib/rules/no-document-domain.js +++ b/tests/lib/rules/no-document-domain.js @@ -4,7 +4,7 @@ const path = require("path"); const testUtils = require("../test-utils"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -23,22 +23,22 @@ function main() { var document: DocumentLikeAPI = documentLikeAPIFunction(); document.domain = 'somevalue'; } - ` - } + `, + }, ], invalid: [ { languageOptions: testUtils.tsLanguageOptions, code: "var doc = window.document; doc.domain = 'somevalue';", - errors: [{ messageId: "default" }] + errors: [{ messageId: "default" }], }, { code: "document.domain = 'somevalue'", - errors: [{ messageId: "default" }] + errors: [{ messageId: "default" }], }, { code: "window.document.domain = 'somevalue'", - errors: [{ messageId: "default" }] + errors: [{ messageId: "default" }], }, { code: ` @@ -48,19 +48,19 @@ window.document.domain = somevalue; newWindow.document.domain = somevalue; `, errors: [ - { + { line: 3, - messageId: "default" + messageId: "default", }, { line: 4, - messageId: "default" + messageId: "default", }, { line: 5, - messageId: "default" - } - ] - } - ] -}); \ No newline at end of file + messageId: "default", + }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-document-write.js b/tests/lib/rules/no-document-write.js index fa8d6cf..03ed9a4 100644 --- a/tests/lib/rules/no-document-write.js +++ b/tests/lib/rules/no-document-write.js @@ -4,7 +4,7 @@ const path = require("path"); const testUtils = require("../test-utils"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -23,7 +23,7 @@ ruleTester.run(ruleId, rule, { writeln: () => {}, }; } - ` + `, }, { code: ` @@ -43,8 +43,8 @@ ruleTester.run(ruleId, rule, { document.write('', ''); document.writeln(); document.writeln('', ''); - ` - } + `, + }, ], invalid: [ { @@ -63,8 +63,8 @@ ruleTester.run(ruleId, rule, { { messageId: "default", line: 3 }, { messageId: "default", line: 4 }, { messageId: "default", line: 8 }, - { messageId: "default", line: 9 } - ] + { messageId: "default", line: 9 }, + ], }, { code: ` @@ -81,8 +81,8 @@ ruleTester.run(ruleId, rule, { { messageId: "default", line: 4 }, { messageId: "default", line: 5 }, { messageId: "default", line: 6 }, - { messageId: "default", line: 7 } - ] - } - ] + { messageId: "default", line: 7 }, + ], + }, + ], }); diff --git a/tests/lib/rules/no-electron-node-integration.js b/tests/lib/rules/no-electron-node-integration.js index a17fd01..5e006c2 100644 --- a/tests/lib/rules/no-electron-node-integration.js +++ b/tests/lib/rules/no-electron-node-integration.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -23,8 +23,8 @@ ruleTester.run(ruleId, rule, { nodeIntegration: false } }); - ` - } + `, + }, ], invalid: [ { @@ -38,10 +38,10 @@ ruleTester.run(ruleId, rule, { }); `, errors: [ - { messageId: "default", line: 4}, - { messageId: "default", line: 5}, - { messageId: "default", line: 6} - ] + { messageId: "default", line: 4 }, + { messageId: "default", line: 5 }, + { messageId: "default", line: 6 }, + ], }, { code: ` @@ -54,10 +54,10 @@ ruleTester.run(ruleId, rule, { }); `, errors: [ - { messageId: "default", line: 4}, - { messageId: "default", line: 5}, - { messageId: "default", line: 6} - ] - } - ] -}); \ No newline at end of file + { messageId: "default", line: 4 }, + { messageId: "default", line: 5 }, + { messageId: "default", line: 6 }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-html-method.js b/tests/lib/rules/no-html-method.js index d4a22c5..4a5523e 100644 --- a/tests/lib/rules/no-html-method.js +++ b/tests/lib/rules/no-html-method.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); const testUtils = require("../test-utils"); @@ -14,16 +14,16 @@ ruleTester.run(ruleId, rule, { "test.html()", "test.html('','')", "element.html('');", - "element.html(null);" + "element.html(null);", ], invalid: [ { code: "$('p').html('XSS')", - errors: [{ messageId: "default", line: 1 }] + errors: [{ messageId: "default", line: 1 }], }, { code: "$(selector).html(sample_function())", - errors: [{ messageId: "default", line: 1 }] + errors: [{ messageId: "default", line: 1 }], }, { languageOptions: testUtils.es6LanguageOptions, @@ -31,9 +31,7 @@ ruleTester.run(ruleId, rule, { import $ from "jquery"; test.html('XSS'); `, - errors: [ - { messageId: "default", line: 3 } - ] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "default", line: 3 }], + }, + ], +}); diff --git a/tests/lib/rules/no-inner-html.js b/tests/lib/rules/no-inner-html.js index 268825f..188e3fd 100644 --- a/tests/lib/rules/no-inner-html.js +++ b/tests/lib/rules/no-inner-html.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); const testUtils = require("../test-utils"); @@ -29,8 +29,8 @@ ruleTester.run(ruleId, rule, { let test = new Test("test"); test.innerHTML = test; test.outerHTML = test; - ` - } + `, + }, ], invalid: [ // TypeScript with full type information @@ -45,8 +45,8 @@ ruleTester.run(ruleId, rule, { errors: [ { messageId: "noInnerHtml", line: 3 }, { messageId: "noInnerHtml", line: 4 }, - { messageId: "noInsertAdjacentHTML", line: 5 } - ] + { messageId: "noInsertAdjacentHTML", line: 5 }, + ], }, { code: ` @@ -55,8 +55,8 @@ ruleTester.run(ruleId, rule, { `, errors: [ { messageId: "noInnerHtml", line: 2 }, - { messageId: "noInnerHtml", line: 3 } - ] + { messageId: "noInnerHtml", line: 3 }, + ], }, { code: ` @@ -65,12 +65,12 @@ ruleTester.run(ruleId, rule, { `, errors: [ { messageId: "noInnerHtml", line: 2 }, - { messageId: "noInnerHtml", line: 3 } - ] + { messageId: "noInnerHtml", line: 3 }, + ], }, { code: "element.insertAdjacentHTML('beforebegin', 'foo')", - errors: [{ messageId: "noInsertAdjacentHTML", line: 1 }] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "noInsertAdjacentHTML", line: 1 }], + }, + ], +}); diff --git a/tests/lib/rules/no-insecure-random.js b/tests/lib/rules/no-insecure-random.js index 5e44276..07535e6 100644 --- a/tests/lib/rules/no-insecure-random.js +++ b/tests/lib/rules/no-insecure-random.js @@ -3,7 +3,7 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); const testUtils = require("../test-utils"); @@ -20,38 +20,36 @@ ruleTester.run(ruleId, rule, { Math.random; math.random(); random(); - ` + `, }, { - - code:` + code: ` require('./node_modules/not-unsafe-random'); require('eslint'); require('test'); require('random-package'); require('random-float2'); require('random2-seed'); - ` + `, }, { languageOptions: testUtils.es6LanguageOptions, - code:` + code: ` import './node_modules/untest'; import 'random'; import 'random-3'; import 'eslint'; import 'eslint-plugin-sdl'; import 'testing'; - ` + `, }, { - - code:` + code: ` cryptos.pseudoRandomBytes(); pseudoRandomBytes(); pseudoRandomByte(); cryptos.pseudoRondomBytes(); - ` + `, }, { languageOptions: testUtils.tsLanguageOptions, @@ -62,11 +60,11 @@ ruleTester.run(ruleId, rule, { Math.Random; Math.random; - ` + `, }, { languageOptions: testUtils.tsLanguageOptions, - code:` + code: ` function pseudoRandomBytes(){} function pseudoRandomByte(){} @@ -74,8 +72,8 @@ ruleTester.run(ruleId, rule, { pseudoRandomByte(); cryptos.pseudoRondomBytes(); cryptos.pseudoRondomBytes(); - ` - } + `, + }, ], invalid: [ { @@ -85,8 +83,8 @@ ruleTester.run(ruleId, rule, { `, errors: [ { messageId: "default", line: 2 }, - { messageId: "default", line: 3 } - ] + { messageId: "default", line: 3 }, + ], }, { languageOptions: testUtils.tsLanguageOptions, @@ -96,8 +94,8 @@ ruleTester.run(ruleId, rule, { `, errors: [ { messageId: "default", line: 2 }, - { messageId: "default", line: 3 } - ] + { messageId: "default", line: 3 }, + ], }, { languageOptions: testUtils.tsLanguageOptions, @@ -108,18 +106,14 @@ ruleTester.run(ruleId, rule, { notMath().random(); `, - errors: [ - { messageId: "default", line: 6 } - ] + errors: [{ messageId: "default", line: 6 }], }, { languageOptions: testUtils.tsLanguageOptions, code: ` crypto.pseudoRandomBytes(); `, - errors: [ - { messageId: "default", line: 2 } - ] + errors: [{ messageId: "default", line: 2 }], }, { languageOptions: testUtils.tsLanguageOptions, @@ -130,13 +124,11 @@ ruleTester.run(ruleId, rule, { notCrypto().pseudoRandomBytes(); `, - errors: [ - { messageId: "default", line: 6 } - ] + errors: [{ messageId: "default", line: 6 }], }, { languageOptions: testUtils.es6LanguageOptions, - code:` + code: ` import './node_modules/unique-random'; import 'chance'; import 'random-number'; @@ -150,12 +142,12 @@ ruleTester.run(ruleId, rule, { { messageId: "default", line: 4 }, { messageId: "default", line: 5 }, { messageId: "default", line: 6 }, - { messageId: "default", line: 7 } - ] + { messageId: "default", line: 7 }, + ], }, { languageOptions: testUtils.es6LanguageOptions, - code:` + code: ` import * as chance1 from 'chance'; import defaultExport from 'chance'; import { chance } from 'chance'; @@ -167,11 +159,11 @@ ruleTester.run(ruleId, rule, { { messageId: "default", line: 3 }, { messageId: "default", line: 4 }, { messageId: "default", line: 5 }, - { messageId: "default", line: 6 } - ] + { messageId: "default", line: 6 }, + ], }, { - code:` + code: ` require('./node_modules/unique-random'); require('**/chance.js'); require('random-number'); @@ -185,8 +177,8 @@ ruleTester.run(ruleId, rule, { { messageId: "default", line: 4 }, { messageId: "default", line: 5 }, { messageId: "default", line: 6 }, - { messageId: "default", line: 7 } - ] - } - ] -}); \ No newline at end of file + { messageId: "default", line: 7 }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-insecure-url.js b/tests/lib/rules/no-insecure-url.js index dc47fb9..f8ac24e 100644 --- a/tests/lib/rules/no-insecure-url.js +++ b/tests/lib/rules/no-insecure-url.js @@ -15,60 +15,75 @@ const testUtils = require("../test-utils"); let ruleTester = new RuleTester(); ruleTester.run(ruleId, rule, { - valid: [ - { // should allow https,ftps strings in variables - code: ` + valid: [ + { + // should allow https,ftps strings in variables + code: ` var x = 'https://www.example.com' var y = 'ftps://www.example.com' - ` - }, - { // should allow https,ftps template strings in variables - code: ` + `, + }, + { + // should allow https,ftps template strings in variables + code: ` var x = \`https://www.template-examples.com\` var y = \`ftps://www.template-file-examples.com\` `, - languageOptions: testUtils.es6LanguageOptions - }, - { // should allow https,ftps multipart template strings in variables - code: ` + languageOptions: testUtils.es6LanguageOptions, + }, + { + // should allow https,ftps multipart template strings in variables + code: ` var x = \`https://www.\${multipartExample}.com\` var y = \`ftps://www.\${multipartExample}.com\` `, - languageOptions: testUtils.es6LanguageOptions - }, - { // should allow http,ftp in middle of string - code: "var x = 'The protocol may be http://, https://, ftp:// or ftps://'" - }, - { // should allow https,ftps strings in default values - code: ` + languageOptions: testUtils.es6LanguageOptions, + }, + { + // should allow http,ftp in middle of string + code: "var x = 'The protocol may be http://, https://, ftp:// or ftps://'", + }, + { + // should allow https,ftps strings in default values + code: ` function f(x : string = 'https://www.example.com') {} function f(y : string = 'ftps://www.example.com') {} `, - languageOptions: testUtils.tsLanguageOptions, - }, - { // should allow user-provided exceptions matches, regardless of upper/lower-case - code: ` + languageOptions: testUtils.tsLanguageOptions, + }, + { + // should allow user-provided exceptions matches, regardless of upper/lower-case + code: ` var a1 = 'http://www.allow-example.com' var a2 = 'HtTp://www.allow-example.com/path' var b1 = 'FTP://www.allow-file-example.com' var c1 = 'LDaP://www.allow-ldap-example.com' `, - options: [{ - exceptions: ["HTTP:\/\/www\.allow-example\.com\/?.*", "FtP:\/\/www\.allow-file-example\.com", "LdaP:\/\/www\.allow-ldap-example\.com"] - }] + options: [ + { + exceptions: [ + "HTTP://www.allow-example.com/?.*", + "FtP://www.allow-file-example.com", + "LdaP://www.allow-ldap-example.com", + ], }, - { // should allow user-provided exceptions for variable name matches, regardless of upper/lower-case - code: ` + ], + }, + { + // should allow user-provided exceptions for variable name matches, regardless of upper/lower-case + code: ` var insecureURL = 'http://www.allow-example.com' var InSeCuReURL = 'ftp://www.allow-example.com/path' `, - options: [{ - varExceptions: ["insecure?.*"] - }] - }, + options: [ { - // should allow xml namespaces, as they are not accessed by the browser - code: ` + varExceptions: ["insecure?.*"], + }, + ], + }, + { + // should allow xml namespaces, as they are not accessed by the browser + code: ` const someSvg: React.FC = () => { return ( @@ -76,121 +91,132 @@ ruleTester.run(ruleId, rule, { ); }; `, - languageOptions: testUtils.tsReactLanguageOptions, - }, - { - // should allow localhost - code: ` + languageOptions: testUtils.tsReactLanguageOptions, + }, + { + // should allow localhost + code: ` var x = "http://localhost/test"; var y = "http://localhost"; - ` - }, - { - // should allow xml namespaces for XHTML and SVG even if outside of jsx xmlns attribute - code: ` + `, + }, + { + // should allow xml namespaces for XHTML and SVG even if outside of jsx xmlns attribute + code: ` var x = "http://www.w3.org/1999/xhtml"; var y = "http://www.w3.org/2000/svg"; - ` - }, - ], - invalid: [ - { // should ban http,ftp strings in variables - code: ` + `, + }, + ], + invalid: [ + { + // should ban http,ftp strings in variables + code: ` var x1 = 'http://www.examples.com' var x2 = 'HTTP://www.examples.com' var y1 = 'ftp://www.file-examples.com' var y2 = 'FTP://www.file-examples.com' `, - output: ` + output: ` var x1 = "https://www.examples.com" var x2 = "https://www.examples.com" var y1 = 'ftp://www.file-examples.com' var y2 = 'FTP://www.file-examples.com' `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 2}, - { messageId: "doNotUseInsecureUrl", line: 3}, - { messageId: "doNotUseInsecureUrl", line: 4}, - { messageId: "doNotUseInsecureUrl", line: 5} - ], - }, - { // should ban http,ftp template strings in variables - code: ` + errors: [ + { messageId: "doNotUseInsecureUrl", line: 2 }, + { messageId: "doNotUseInsecureUrl", line: 3 }, + { messageId: "doNotUseInsecureUrl", line: 4 }, + { messageId: "doNotUseInsecureUrl", line: 5 }, + ], + }, + { + // should ban http,ftp template strings in variables + code: ` var x1 = \`http://www.template-examples.com\` var x2 = \`HTTP://www.template-examples.com\` var y1 = \`ftp://www.file-examples.com\` var y2 = \`FTP://www.file-examples.com\` `, - output: ` + output: ` var x1 = \`https://www.template-examples.com\` var x2 = \`https://www.template-examples.com\` var y1 = \`ftp://www.file-examples.com\` var y2 = \`FTP://www.file-examples.com\` `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 2}, - { messageId: "doNotUseInsecureUrl", line: 3}, - { messageId: "doNotUseInsecureUrl", line: 4}, - { messageId: "doNotUseInsecureUrl", line: 5} - ], - languageOptions: testUtils.es6LanguageOptions - }, - { // should ban http,ftp multipart template strings in variables - code: ` + errors: [ + { messageId: "doNotUseInsecureUrl", line: 2 }, + { messageId: "doNotUseInsecureUrl", line: 3 }, + { messageId: "doNotUseInsecureUrl", line: 4 }, + { messageId: "doNotUseInsecureUrl", line: 5 }, + ], + languageOptions: testUtils.es6LanguageOptions, + }, + { + // should ban http,ftp multipart template strings in variables + code: ` var x1 = \`http://www.\${multipartExample}.com\`; var y1 = \`ftp://www.\${multipartExample}.com\`; `, - output: ` + output: ` var x1 = \`https://www.\${multipartExample}.com\`; var y1 = \`ftp://www.\${multipartExample}.com\`; `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 2}, - { messageId: "doNotUseInsecureUrl", line: 3}, - ], - languageOptions: testUtils.es6LanguageOptions - }, - { // should ban http,ftp strings in default values - code: ` + errors: [ + { messageId: "doNotUseInsecureUrl", line: 2 }, + { messageId: "doNotUseInsecureUrl", line: 3 }, + ], + languageOptions: testUtils.es6LanguageOptions, + }, + { + // should ban http,ftp strings in default values + code: ` function f(x : string = 'http://www.example.com') {} function f(y : string = 'ftp://www.example.com') {} `, - output: ` + output: ` function f(x : string = "https://www.example.com") {} function f(y : string = 'ftp://www.example.com') {} `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 2}, - { messageId: "doNotUseInsecureUrl", line: 3}, - ], - languageOptions: testUtils.tsLanguageOptions, - }, - { // should ban user-provided blacklist matches, regardless of upper/lower-case - code: ` + errors: [ + { messageId: "doNotUseInsecureUrl", line: 2 }, + { messageId: "doNotUseInsecureUrl", line: 3 }, + ], + languageOptions: testUtils.tsLanguageOptions, + }, + { + // should ban user-provided blacklist matches, regardless of upper/lower-case + code: ` var a1 = 'http://www.ban-example.com' var a2 = 'HTTP://www.ban-example.com/path' var b1 = 'FtP://www.ban-file-example.com' var c1 = 'LDAp://www.ban-ldap-example.com' `, - output: ` + output: ` var a1 = "https://www.ban-example.com" var a2 = "https://www.ban-example.com/path" var b1 = 'FtP://www.ban-file-example.com' var c1 = 'LDAp://www.ban-ldap-example.com' `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 2}, - { messageId: "doNotUseInsecureUrl", line: 3}, - { messageId: "doNotUseInsecureUrl", line: 4}, - { messageId: "doNotUseInsecureUrl", line: 5} - ], - options: [{ - blocklist: ["htTp:\/\/www\.ban-example\.com\/?.*", "fTp:\/\/www\.ban-file-example\.com\/?.*", "lDAp:\/\/www\.ban-ldap-example\.com\/?.*"] - }] - }, + errors: [ + { messageId: "doNotUseInsecureUrl", line: 2 }, + { messageId: "doNotUseInsecureUrl", line: 3 }, + { messageId: "doNotUseInsecureUrl", line: 4 }, + { messageId: "doNotUseInsecureUrl", line: 5 }, + ], + options: [ { - // should ban any other xml attribute with urls in them - code: ` + blocklist: [ + "htTp://www.ban-example.com/?.*", + "fTp://www.ban-file-example.com/?.*", + "lDAp://www.ban-ldap-example.com/?.*", + ], + }, + ], + }, + { + // should ban any other xml attribute with urls in them + code: ` const someSvg: React.FC = () => { return ( @@ -198,7 +224,7 @@ ruleTester.run(ruleId, rule, { ); }; `, - output: ` + output: ` const someSvg: React.FC = () => { return ( @@ -206,38 +232,30 @@ ruleTester.run(ruleId, rule, { ); }; `, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 4}, - ], - languageOptions: testUtils.tsReactLanguageOptions, - }, - { - // should escape the url string correctly - code: `var a1 = "http://moz\ti\tlla.org";`, - output: `var a1 = "https://moz\\ti\\tlla.org";`, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 1}, - ], - }, - { - // should fix url in `` correctly - code: "var x1 = `http://foo${multipartExample} http://${multipartExample}.com`;", - output: "var x1 = `https://foo${multipartExample} http://${multipartExample}.com`;", - errors: [ - { messageId: "doNotUseInsecureUrl", line: 1}, - ], + errors: [{ messageId: "doNotUseInsecureUrl", line: 4 }], + languageOptions: testUtils.tsReactLanguageOptions, + }, + { + // should escape the url string correctly + code: `var a1 = "http://moz\ti\tlla.org";`, + output: `var a1 = "https://moz\\ti\\tlla.org";`, + errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }], + }, + { + // should fix url in `` correctly + code: "var x1 = `http://foo${multipartExample} http://${multipartExample}.com`;", + output: "var x1 = `https://foo${multipartExample} http://${multipartExample}.com`;", + errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }], - languageOptions: testUtils.es6LanguageOptions - }, - { - // should escape the string and fix it properly in `` - code: `var a1 = \`http://moz\ti\tlla.org\`;`, - output: `var a1 = \`https://moz\\ti\\tlla.org\`;`, - errors: [ - { messageId: "doNotUseInsecureUrl", line: 1}, - ], + languageOptions: testUtils.es6LanguageOptions, + }, + { + // should escape the string and fix it properly in `` + code: `var a1 = \`http://moz\ti\tlla.org\`;`, + output: `var a1 = \`https://moz\\ti\\tlla.org\`;`, + errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }], - languageOptions: testUtils.es6LanguageOptions - }, - ] -}); \ No newline at end of file + languageOptions: testUtils.es6LanguageOptions, + }, + ], +}); diff --git a/tests/lib/rules/no-msapp-exec-unsafe.js b/tests/lib/rules/no-msapp-exec-unsafe.js index 4aa4d90..5389c9a 100644 --- a/tests/lib/rules/no-msapp-exec-unsafe.js +++ b/tests/lib/rules/no-msapp-exec-unsafe.js @@ -3,22 +3,17 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); const testUtils = require("../test-utils"); ruleTester.run(ruleId, rule, { - valid: [ - "test.execUnsafeLocalFunction = 'test'", - "MSApp.execUnsafeLocalFunction()" - ], + valid: ["test.execUnsafeLocalFunction = 'test'", "MSApp.execUnsafeLocalFunction()"], invalid: [ { code: "MSApp.execUnsafeLocalFunction(testfunc)", - errors: [ - { messageId: "default", line: 1, type: 'CallExpression' } - ] - } - ] + errors: [{ messageId: "default", line: 1, type: "CallExpression" }], + }, + ], }); diff --git a/tests/lib/rules/no-postmessage-star-origin.js b/tests/lib/rules/no-postmessage-star-origin.js index bffc412..0794681 100644 --- a/tests/lib/rules/no-postmessage-star-origin.js +++ b/tests/lib/rules/no-postmessage-star-origin.js @@ -4,7 +4,7 @@ const path = require("path"); const testUtils = require("../test-utils"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); @@ -27,8 +27,8 @@ function main() { var w: WindowLike = new WindowLike(); w.postMessage('test', '*'); } - ` - } + `, + }, ], invalid: [ { @@ -38,8 +38,8 @@ function main() { `, errors: [ { messageId: "default", line: 2 }, - { messageId: "default", line: 3 } - ] + { messageId: "default", line: 3 }, + ], }, { languageOptions: testUtils.tsLanguageOptions, @@ -50,8 +50,8 @@ function main() { `, errors: [ { messageId: "default", line: 2 }, - { messageId: "default", line: 4 } - ] + { messageId: "default", line: 4 }, + ], }, - ] -}); \ No newline at end of file + ], +}); diff --git a/tests/lib/rules/no-unsafe-alloc.js b/tests/lib/rules/no-unsafe-alloc.js index bd99f37..4384c5e 100644 --- a/tests/lib/rules/no-unsafe-alloc.js +++ b/tests/lib/rules/no-unsafe-alloc.js @@ -4,16 +4,12 @@ "use strict"; const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); ruleTester.run(ruleId, rule, { - valid: [ - "foo.allocUnsafe", - "Buffer.allocUnsafe(0)", - "Buffer.allocUnsafeSlow(0)" - ], + valid: ["foo.allocUnsafe", "Buffer.allocUnsafe(0)", "Buffer.allocUnsafeSlow(0)"], invalid: [ { code: ` @@ -22,8 +18,8 @@ ruleTester.run(ruleId, rule, { `, errors: [ { messageId: "default", line: 2 }, - { messageId: "default", line: 3 } - ] - } - ] -}); \ No newline at end of file + { messageId: "default", line: 3 }, + ], + }, + ], +}); diff --git a/tests/lib/rules/no-winjs-html-unsafe.js b/tests/lib/rules/no-winjs-html-unsafe.js index bbcf3dc..62e1608 100644 --- a/tests/lib/rules/no-winjs-html-unsafe.js +++ b/tests/lib/rules/no-winjs-html-unsafe.js @@ -3,15 +3,13 @@ const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester(); const testUtils = require("../test-utils"); ruleTester.run(ruleId, rule, { - valid: [ - 'element.insertAdjacentHTMLUnsafe = "test";' - ], + valid: ['element.insertAdjacentHTMLUnsafe = "test";'], invalid: [ { code: ` @@ -20,10 +18,10 @@ ruleTester.run(ruleId, rule, { WinJS.Utilities.setOuterHTMLUnsafe(element, text); `, errors: [ - { messageId: "default", line: 2, type: 'CallExpression' }, - { messageId: "default", line: 3, type: 'CallExpression' }, - { messageId: "default", line: 4, type: 'CallExpression' } - ] - } - ] + { messageId: "default", line: 2, type: "CallExpression" }, + { messageId: "default", line: 3, type: "CallExpression" }, + { messageId: "default", line: 4, type: "CallExpression" }, + ], + }, + ], }); diff --git a/tests/lib/rules/react-iframe-missing-sandbox.js b/tests/lib/rules/react-iframe-missing-sandbox.js index 9006ded..2c07cde 100644 --- a/tests/lib/rules/react-iframe-missing-sandbox.js +++ b/tests/lib/rules/react-iframe-missing-sandbox.js @@ -1,23 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -'use strict'; +"use strict"; const path = require("path"); const ruleId = path.parse(__filename).name; -const rule = require(path.join('../../../lib/rules/', ruleId)); +const rule = require(path.join("../../../lib/rules/", ruleId)); const RuleTester = require("eslint").RuleTester; var ruleTester = new RuleTester({ languageOptions: { parserOptions: { ecmaVersion: 2018, - sourceType: 'module', + sourceType: "module", ecmaFeatures: { - jsx: true - } + jsx: true, + }, }, - } + }, }); ruleTester.run(ruleId, rule, { @@ -37,45 +37,49 @@ ruleTester.run(ruleId, rule, { { code: '' }, { code: '' }, { code: '' }, - { code: '' }, + { + code: '', + }, { code: '' }, - { code: '' } + { + code: '', + }, ], invalid: [ { - code: ';', - errors: [{ messageId: 'attributeMissing' }] + code: ";", + errors: [{ messageId: "attributeMissing" }], }, { - code: ';', - errors: [{ messageId: 'attributeMissing' }] + code: ";", + errors: [{ messageId: "attributeMissing" }], }, { code: '', - errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }] + errors: [{ messageId: "invalidValue", data: { value: "__unknown__" } }], }, { code: '', - errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }] + errors: [{ messageId: "invalidValue", data: { value: "__unknown__" } }], }, { code: '', - errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }] + errors: [{ messageId: "invalidValue", data: { value: "__unknown__" } }], }, { code: '', errors: [ - { messageId: 'invalidValue', data: { value: '__unknown__' } }, - { messageId: 'invalidValue', data: { value: '__unknown__' } } - ] + { messageId: "invalidValue", data: { value: "__unknown__" } }, + { messageId: "invalidValue", data: { value: "__unknown__" } }, + ], }, { code: ';', - errors: [{ messageId: 'invalidCombination' }] + errors: [{ messageId: "invalidCombination" }], }, { code: ';', - errors: [{ messageId: 'invalidCombination' }] - } - ] -}); \ No newline at end of file + errors: [{ messageId: "invalidCombination" }], + }, + ], +}); diff --git a/tests/lib/test-utils.js b/tests/lib/test-utils.js index 0176bd4..591eb3a 100644 --- a/tests/lib/test-utils.js +++ b/tests/lib/test-utils.js @@ -11,27 +11,27 @@ const path = require("path"); const tsParser = require("@typescript-eslint/parser"); module.exports = { - es6LanguageOptions: { - parserOptions: { - ecmaVersion: 6, - sourceType: "module" - } + es6LanguageOptions: { + parserOptions: { + ecmaVersion: 6, + sourceType: "module", }, - tsLanguageOptions: { - parser: tsParser, - parserOptions: { - tsconfigRootDir: path.join(__dirname, "..", "fixtures", "ts"), - projectService: true, - } + }, + tsLanguageOptions: { + parser: tsParser, + parserOptions: { + tsconfigRootDir: path.join(__dirname, "..", "fixtures", "ts"), + projectService: true, }, - tsReactLanguageOptions: { - parser: tsParser, - parserOptions: { - tsconfigRootDir: path.join(__dirname, "..", "fixtures", "tsx"), - projectService: true, - ecmaFeatures: { - jsx: true - } - } - } -}; \ No newline at end of file + }, + tsReactLanguageOptions: { + parser: tsParser, + parserOptions: { + tsconfigRootDir: path.join(__dirname, "..", "fixtures", "tsx"), + projectService: true, + ecmaFeatures: { + jsx: true, + }, + }, + }, +};