├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── code-formatting.yml │ ├── e2e-integration.yml │ ├── node-version-integration.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── config ├── angular.js ├── angularjs.js ├── common.js ├── electron.js ├── node.js ├── react.js └── typescript.js ├── contrib ├── eslint.config.recommended.js └── eslint.config.required.js ├── docs └── rules │ ├── no-angular-bypass-sanitizer.md │ ├── no-angularjs-bypass-sce.md │ ├── no-angularjs-enable-svg.md │ ├── no-angularjs-sanitization-whitelist.md │ ├── no-cookies.md │ ├── no-document-domain.md │ ├── no-document-write.md │ ├── no-electron-node-integration.md │ ├── no-html-method.md │ ├── no-inner-html.md │ ├── no-insecure-random.md │ ├── no-insecure-url.md │ ├── no-msapp-exec-unsafe.md │ ├── no-postmessage-star-origin.md │ ├── no-unsafe-alloc.md │ └── no-winjs-html-unsafe.md ├── lib ├── ast-utils.js ├── index.js └── rules │ ├── no-angular-bypass-sanitizer.js │ ├── no-angular-sanitization-trusted-urls.js │ ├── no-angularjs-bypass-sce.js │ ├── no-angularjs-enable-svg.js │ ├── no-angularjs-sanitization-whitelist.js │ ├── no-cookies.js │ ├── no-document-domain.js │ ├── no-document-write.js │ ├── no-electron-node-integration.js │ ├── no-html-method.js │ ├── no-inner-html.js │ ├── no-insecure-random.js │ ├── no-insecure-url.js │ ├── no-msapp-exec-unsafe.js │ ├── no-postmessage-star-origin.js │ ├── no-unsafe-alloc.js │ └── no-winjs-html-unsafe.js ├── package-lock.json ├── package.json └── tests ├── fixtures ├── ts │ ├── estree.ts │ └── tsconfig.json └── tsx │ ├── estree.tsx │ └── tsconfig.json └── lib ├── rules ├── no-angular-bypass-sanitizer.js ├── no-angular-sanitization-trusted-urls.js ├── no-angularjs-bypass-sce.js ├── no-angularjs-enable-svg.js ├── no-angularjs-sanitization-whitelist.js ├── no-cookies.js ├── no-document-domain.js ├── no-document-write.js ├── no-electron-node-integration.js ├── no-html-method.js ├── no-inner-html.js ├── no-insecure-random.js ├── no-insecure-url.js ├── no-msapp-exec-unsafe.js ├── no-postmessage-star-origin.js ├── no-unsafe-alloc.js └── no-winjs-html-unsafe.js └── test-utils.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owners file. 2 | # This file controls who is tagged for review for any given pull request. 3 | 4 | # For anything not explicitly taken by someone else: 5 | * @microsoft/1es-sdl-engineering 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/workflows/code-formatting.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | name: Code formatting 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-node@v4 18 | - run: npm install 19 | - run: npm run check-fmt 20 | -------------------------------------------------------------------------------- /.github/workflows/e2e-integration.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | name: E2E integration 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request_target: 10 | branches: [main] 11 | 12 | env: 13 | GITHUB_REPO: nodejs/node 14 | GITHUB_REPO_ESLINT_GLOB: lib 15 | GITHUB_REPO_TSCONFIG: tsconfig.json 16 | TEST_TARGET_DIR: test-target 17 | TEST_RUN_DIR: test-run 18 | PLUGIN_DIR: eslint-plugin-sdl 19 | 20 | # This job runs the plugin with latest ESLint on the target repository in GITHUB_REPO env variable. 21 | jobs: 22 | build: 23 | name: E2E run with SARIF 24 | runs-on: ${{ matrix.os }} 25 | 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, windows-latest] 29 | 30 | steps: 31 | - name: Setup Node.js environment 32 | uses: actions/setup-node@v4 33 | 34 | - name: Create test run directory 35 | run: mkdir ${{env.TEST_RUN_DIR}} 36 | 37 | - name: Install ESLint and dependencies required for test 38 | run: | 39 | npm i eslint 40 | npm i typescript 41 | npm i @microsoft/eslint-formatter-sarif 42 | working-directory: ${{env.TEST_RUN_DIR}} 43 | 44 | - uses: actions/checkout@v4 45 | with: 46 | repository: ${{env.GITHUB_REPO}} 47 | path: ${{env.TEST_RUN_DIR}}/${{env.TEST_TARGET_DIR}} 48 | clean: true 49 | 50 | - uses: actions/checkout@v4 51 | with: 52 | path: ${{env.PLUGIN_DIR}} 53 | clean: true 54 | 55 | - name: Install plugin dependencies 56 | run: npm install --production 57 | working-directory: ${{env.PLUGIN_DIR}} 58 | 59 | - name: Link plugin 60 | run: sudo npm link ../${{env.PLUGIN_DIR}} 61 | working-directory: ${{env.TEST_RUN_DIR}} 62 | if: runner.os == 'Linux' 63 | 64 | - name: Link plugin 65 | run: npm link ../${{env.PLUGIN_DIR}} 66 | working-directory: ${{env.TEST_RUN_DIR}} 67 | if: runner.os == 'Windows' 68 | 69 | - name: Create ESLint config file 70 | run: echo 'module.exports = [...require("@microsoft/eslint-plugin-sdl").configs.recommended];' > eslint.config.js 71 | working-directory: ${{env.TEST_RUN_DIR}} 72 | 73 | - name: Run ESLint 74 | run: npx eslint 75 | --config eslint.config.js 76 | --no-config-lookup 77 | ${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_ESLINT_GLOB}} 78 | --parser-options=project:${{env.TEST_TARGET_DIR}}/${{env.GITHUB_REPO_TSCONFIG}} 79 | --format @microsoft/eslint-formatter-sarif 80 | --output-file eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif 81 | working-directory: ${{env.TEST_RUN_DIR}} 82 | continue-on-error: true 83 | 84 | - name: Upload ESLint results as artifact 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: eslint-result-${{ matrix.os }} 88 | path: ${{env.TEST_RUN_DIR}}/eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif 89 | if-no-files-found: error 90 | -------------------------------------------------------------------------------- /.github/workflows/node-version-integration.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | name: Node.js integration 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest] 19 | node-version: [18.x, 20.x, 22.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | name: Publish Package 5 | 6 | on: 7 | release: 8 | types: [published] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | - run: npm i 17 | - run: npm test 18 | 19 | publish-npm: 20 | needs: build 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: actions/setup-node@v4 25 | with: 26 | registry-url: https://registry.npmjs.org/ 27 | - run: npm i 28 | - run: npm publish 29 | env: 30 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | node_modules/ 4 | .vscode/ 5 | .package-lock.json 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | tests/ 4 | .github/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | SECURITY.md 2 | CODE_OF_CONDUCT.md 3 | package.json 4 | package-lock.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "none" 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Microsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-sdl 2 | 3 | [![Node.js integration](https://github.com/microsoft/eslint-plugin-sdl/actions/workflows/node-version-integration.yml/badge.svg)](https://github.com/microsoft/eslint-plugin-sdl/actions/workflows/node-version-integration.yml) 4 | [![E2E integration](https://github.com/microsoft/eslint-plugin-sdl/actions/workflows/e2e-integration.yml/badge.svg)](https://github.com/microsoft/eslint-plugin-sdl/actions/workflows/e2e-integration.yml) 5 | 6 | [ESLint Plugin](https://eslint.org/docs/developer-guide/working-with-plugins) focused on common security issues and misconfigurations. 7 | 8 | Plugin is intended as a baseline for projects that follow [Microsoft Security Development Lifecycle (SDL)](https://www.microsoft.com/securityengineering/sdl) and use ESLint to perform [Static Analysis Security Testing (SAST)](https://www.microsoft.com/securityengineering/sdl/practices#practice9). 9 | 10 | ## Installation 11 | 12 | ```sh 13 | npm install microsoft/eslint-plugin-sdl 14 | ``` 15 | 16 | or 17 | 18 | ```sh 19 | yarn add microsoft/eslint-plugin-sdl 20 | ``` 21 | 22 | ## Configs 23 | 24 | Including an ESLint configuration file in your project allows you to customize how ESLint applies rules to your project. You can include the plugin in your [configuration file](https://eslint.org/docs/latest/use/configure/configuration-files) as described in examples for [`recommended`](contrib/eslint.config.recommended.js) and [`required`](contrib/eslint.config.required.js) configurations. 25 | 26 | 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: 27 | 28 | ```js 29 | const pluginMicrosoftSdl = require("@microsoft/eslint-plugin-sdl"); 30 | 31 | module.exports = [ 32 | ...pluginMicrosoftSdl.configs.recommended, 33 | { 34 | rules: { 35 | "no-eval": "error", 36 | "@microsoft/sdl/no-inner-html": "error" 37 | } 38 | } 39 | ]; 40 | ``` 41 | 42 | You can also used the below Shareable Config files using flat config model as guidelines depending on the type of project. 43 | 44 | Plugin is shipped with following [Shareable Configs](http://eslint.org/docs/developer-guide/shareable-configs): 45 | 46 | - [angular](config/angular.js) - Set of rules for modern [Angular](https://angular.io) applications 47 | - [angularjs](config/angularjs.js) - Set of rules for legacy [AngularJS](https://docs.angularjs.org) applications 48 | - [common](config/common.js) - Set of rules for common JavaScript applications 49 | - [electron](config/electron.js) - Set of rules for Electron applications 50 | - [node](config/node.js) - Set of rules for Node.js applications 51 | - [react](config/react.js) - Set of rules for [ReactJS](https://reactjs.org) applications 52 | - [**recommended**](lib/index.js) - SDL Recommended rules for all applications 53 | - [**required**](lib/index.js) - SDL Required rules for all applications 54 | - [typescript](config/typescript.js) - Set of rules for TypeScript applications 55 | 56 | ## Rules 57 | 58 | Where possible, we leverage existing rules from [ESLint](https://eslint.org/docs/rules/) and community plugins such as [react](https://github.com/jsx-eslint/eslint-plugin-react), [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/eslint-plugin#supported-rules) or [security](https://github.com/nodesecurity/eslint-plugin-security#rules). 59 | 60 | We also implemented several [custom rules](./lib/rules) where we did not find sufficient alternative in the community. 61 | 62 | | Name | Description | 63 | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 64 | | [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. | 65 | | [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. | 66 | | [no-eval](https://eslint.org/docs/rules/no-eval) | Bans usage of [`eval()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/eval) that allows code execution from string argument. | 67 | | [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. | 68 | | [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. | 69 | | [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. | 70 | | [@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. | 71 | | [@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. | 72 | | [@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. | 73 | | [@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. | 74 | | [@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. | 75 | | [@microsoft/sdl/no-document-domain](./docs/rules/no-document-domain.md) | Writes to [`document.domain`](https://developer.mozilla.org/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited. | 76 | | [@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. | 77 | | [@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. | 78 | | [@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. | 79 | | [@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. | 80 | | [@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. | 81 | | [@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. | 82 | | [@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/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. | 83 | | [@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. | 84 | | [@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. | 85 | | [react/iframe-missing-sandbox](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/iframe-missing-sandbox.md) | The [sandbox](https://developer.mozilla.org/docs/Web/HTML/Element/iframe#sandbox) attribute enables an extra set of restrictions for the content in the iframe and should always be specified. | 86 | | [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. | 87 | | [@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. | 88 | 89 | ## Contributing 90 | 91 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 92 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 93 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 94 | 95 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 96 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 97 | provided by the bot. You will only need to do this once across all repos using our CLA. 98 | 99 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 100 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 101 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 102 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /config/angular.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | // Generates shareable config for modern Angular (https://angular.dev/) apps. 7 | module.exports = (pluginSdl) => { 8 | return [ 9 | { 10 | plugins: { 11 | "@microsoft/sdl": pluginSdl 12 | }, 13 | rules: { 14 | "@microsoft/sdl/no-angular-bypass-sanitizer": "error" 15 | } 16 | } 17 | ]; 18 | }; 19 | -------------------------------------------------------------------------------- /config/angularjs.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | // Generates shareable config for legacy AngularJS (https://angularjs.org/) apps. 7 | module.exports = (pluginSdl) => { 8 | return [ 9 | { 10 | plugins: { 11 | "@microsoft/sdl": pluginSdl 12 | }, 13 | rules: { 14 | "@microsoft/sdl/no-angularjs-enable-svg": "error", 15 | "@microsoft/sdl/no-angularjs-sanitization-whitelist": "error", 16 | "@microsoft/sdl/no-angularjs-bypass-sce": "error" 17 | } 18 | } 19 | ]; 20 | }; 21 | -------------------------------------------------------------------------------- /config/common.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | module.exports = (pluginSdl) => { 7 | return [ 8 | { 9 | plugins: { 10 | "@microsoft/sdl": pluginSdl 11 | }, 12 | rules: { 13 | "no-caller": "error", 14 | "no-delete-var": "error", 15 | "no-eval": "error", 16 | "no-implied-eval": "error", 17 | "no-new-func": "error", 18 | "@microsoft/sdl/no-cookies": "error", 19 | "@microsoft/sdl/no-document-domain": "error", 20 | "@microsoft/sdl/no-document-write": "error", 21 | "@microsoft/sdl/no-html-method": "error", 22 | "@microsoft/sdl/no-inner-html": "error", 23 | "@microsoft/sdl/no-insecure-url": "error", 24 | "@microsoft/sdl/no-msapp-exec-unsafe": "error", 25 | "@microsoft/sdl/no-postmessage-star-origin": "error", 26 | "@microsoft/sdl/no-winjs-html-unsafe": "error" 27 | } 28 | } 29 | ]; 30 | }; 31 | -------------------------------------------------------------------------------- /config/electron.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | module.exports = (pluginSdl) => { 7 | return [ 8 | { 9 | plugins: { 10 | "@microsoft/sdl": pluginSdl 11 | }, 12 | rules: { 13 | "@microsoft/sdl/no-electron-node-integration": "error" 14 | } 15 | } 16 | ]; 17 | }; 18 | -------------------------------------------------------------------------------- /config/node.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const pluginN = require("eslint-plugin-n"); 7 | 8 | module.exports = (pluginSdl) => { 9 | return [ 10 | { 11 | plugins: { 12 | n: pluginN 13 | }, 14 | rules: { 15 | "n/no-deprecated-api": "error" 16 | } 17 | }, 18 | { 19 | plugins: { 20 | "@microsoft/sdl": pluginSdl 21 | }, 22 | rules: { 23 | "@microsoft/sdl/no-unsafe-alloc": "error" 24 | } 25 | } 26 | ]; 27 | }; 28 | -------------------------------------------------------------------------------- /config/react.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const pluginReact = require("eslint-plugin-react"); 7 | 8 | module.exports = (pluginSdl) => { 9 | return [ 10 | { 11 | languageOptions: { 12 | parserOptions: { 13 | ecmaFeatures: { 14 | jsx: true 15 | } 16 | } 17 | } 18 | }, 19 | { 20 | plugins: { 21 | react: pluginReact 22 | }, 23 | rules: { 24 | "react/no-danger": "error", 25 | "react/jsx-no-target-blank": [ 26 | "error", 27 | { 28 | allowReferrer: false, 29 | enforceDynamicLinks: "always", 30 | warnOnSpreadAttributes: true 31 | } 32 | ], 33 | "react/iframe-missing-sandbox": "error" 34 | } 35 | }, 36 | { 37 | plugins: { 38 | "@microsoft/sdl": pluginSdl 39 | } 40 | } 41 | ]; 42 | }; 43 | -------------------------------------------------------------------------------- /config/typescript.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const pluginTypescript = require("@typescript-eslint/eslint-plugin"); 7 | 8 | module.exports = () => { 9 | return [ 10 | { 11 | languageOptions: { 12 | parserOptions: { 13 | ecmaVersion: 6, 14 | sourceType: "module", 15 | ecmaFeatures: { 16 | jsx: true 17 | } 18 | } 19 | } 20 | }, 21 | { 22 | files: ["**/*.{ts,tsx}"], 23 | languageOptions: { 24 | parserOptions: { 25 | parser: "@typescript-eslint/parser" 26 | } 27 | }, 28 | plugins: { 29 | "@typescript-eslint": pluginTypescript 30 | }, 31 | rules: { 32 | "@typescript-eslint/no-implied-eval": "error", 33 | // @typescript-eslint/no-implied-eval offers more accurate results for typescript. 34 | // thus we turn the more generic rule off for ts and tsx files. 35 | // This also avoids duplicate hits. 36 | "no-implied-eval": "off" 37 | } 38 | } 39 | ]; 40 | }; 41 | -------------------------------------------------------------------------------- /contrib/eslint.config.recommended.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const pluginMicrosoftSdl = require("@microsoft/eslint-plugin-sdl"); 7 | 8 | module.exports = [...pluginMicrosoftSdl.configs.recommended]; 9 | -------------------------------------------------------------------------------- /contrib/eslint.config.required.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const pluginMicrosoftSdl = require("@microsoft/eslint-plugin-sdl"); 7 | 8 | module.exports = [...pluginMicrosoftSdl.configs.required]; 9 | -------------------------------------------------------------------------------- /docs/rules/no-angular-bypass-sanitizer.md: -------------------------------------------------------------------------------- 1 | # Do not bypass Angular's built-in sanitization (no-angular-bypass-sanitizer) 2 | 3 | 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. 4 | 5 | Sanitization should be disabled only in very rare and justifiable cases after careful review so that the risk of introducing Cross-Site-Scripting (XSS) vulnerability is minimized. 6 | 7 | The issue is well described in official [DomSanitizer](https://angular.io/api/platform-browser/DomSanitizer#security-risk) documentation. Also see [Angular Security Guide](https://angular.io/guide/security) for more details. 8 | -------------------------------------------------------------------------------- /docs/rules/no-angularjs-bypass-sce.md: -------------------------------------------------------------------------------- 1 | # Do not bypass Strict Contextual Escaping (SCE) in AngularJS (no-angularjs-bypass-sce) 2 | 3 | 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. 4 | 5 | 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. 6 | 7 | See [official documentation](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) for more details. 8 | -------------------------------------------------------------------------------- /docs/rules/no-angularjs-enable-svg.md: -------------------------------------------------------------------------------- 1 | # Do not enable SVG support in AngularJS (no-angularjs-enable-svg) 2 | 3 | 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. 4 | 5 | SVG support should be enabled only in very rare and justifiable cases after careful review so that the risk of introducing Clickjacking vulnerability is minimized. 6 | 7 | See [official documentation](https://docs.angularjs.org/api/ngSanitize/provider/$sanitizeProvider#enableSvg) for more details about the issue. 8 | -------------------------------------------------------------------------------- /docs/rules/no-angularjs-sanitization-whitelist.md: -------------------------------------------------------------------------------- 1 | # Do not bypass Angular's built-in sanitization (no-angularjs-sanitization-whitelist) 2 | 3 | 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. 4 | 5 | Sanitization should be disabled only in very rare and justifiable cases after careful review so that the risk of introducing Cross-Site-Scripting (XSS) vulnerability is minimized. 6 | 7 | See [official documentation](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) for more details about the issue. 8 | -------------------------------------------------------------------------------- /docs/rules/no-cookies.md: -------------------------------------------------------------------------------- 1 | # Do not use HTTP cookies in modern applications (no-cookies) 2 | 3 | HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other modern methods instead. 4 | 5 | Cookies should be used only in rare and justifiable cases after thorough security review. 6 | 7 | ## Further Reading 8 | 9 | - [Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) 10 | 11 | ## Related Rules 12 | 13 | - [tslint-microsoft-contrib/no-cookies](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noCookiesRule.ts) 14 | -------------------------------------------------------------------------------- /docs/rules/no-document-domain.md: -------------------------------------------------------------------------------- 1 | # Do not write to document.domain property (no-document-domain) 2 | 3 | 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. 4 | 5 | ## Related Rules 6 | 7 | - [tslint-microsoft-contrib/no-document-domain](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentDomainRule.ts) 8 | -------------------------------------------------------------------------------- /docs/rules/no-document-write.md: -------------------------------------------------------------------------------- 1 | # Do not write to DOM directly using document.write or document.writeln methods (no-document-write) 2 | 3 | Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. 4 | 5 | ## Related Rules 6 | 7 | - [tslint-microsoft-contrib/no-document-write](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentWriteRule.ts) 8 | -------------------------------------------------------------------------------- /docs/rules/no-electron-node-integration.md: -------------------------------------------------------------------------------- 1 | # Do not enable Node.js Integration for Remote Content (no-electron-node-integration) 2 | 3 | [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. 4 | 5 | [Rule Source Code](../../lib/rules/no-electron-node-integration.js) 6 | 7 | ## Related Rules 8 | 9 | - [codeql/js/enabling-electron-renderer-node-integration](https://help.semmle.com/wiki/display/JS/Enabling+Node.js+integration+for+Electron+web+content+renderers) 10 | -------------------------------------------------------------------------------- /docs/rules/no-html-method.md: -------------------------------------------------------------------------------- 1 | # Do not write to DOM directly using jQuery html() method (no-html-method) 2 | 3 | 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. 4 | 5 | ## Related Rules 6 | 7 | - [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) 8 | -------------------------------------------------------------------------------- /docs/rules/no-inner-html.md: -------------------------------------------------------------------------------- 1 | # Do not write to DOM directly using innerHTML/outerHTML property (no-inner-html) 2 | 3 | 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. 4 | 5 | - [Rule Source](../../lib/rules/no-inner-html.js) 6 | - [Rule Test](../../tests/lib/rules/no-inner-html.js) 7 | 8 | ## Related Rules 9 | 10 | - [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts) 11 | - [eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/master/docs/rules/method.md) 12 | -------------------------------------------------------------------------------- /docs/rules/no-insecure-random.md: -------------------------------------------------------------------------------- 1 | # Do not use insecure random functions 2 | 3 | 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. 4 | 5 | Use crypto.randomBytes() or window.crypto.getRandomValues() instead. 6 | 7 | ## Related Rules 8 | 9 | - [tslint-microsoft-contrib/no-insecure-random](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/insecureRandomRule.ts) 10 | 11 | * https://help.semmle.com/wiki/display/JS/Insecure+randomness 12 | - [source](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll) 13 | * https://vulncat.fortify.com/en/detail?id=desc.semantic.abap.insecure_randomness#JavaScript 14 | * https://rules.sonarsource.com/javascript/RSPEC-2245 15 | - [source](https://github.com/SonarSource/SonarJS/blob/master/eslint-bridge/src/rules/pseudo-random.ts) 16 | * https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-pseudoRandomBytes.js 17 | * https://github.com/gkouziik/eslint-plugin-security-node/blob/master/lib/rules/detect-insecure-randomness.js 18 | -------------------------------------------------------------------------------- /docs/rules/no-insecure-url.md: -------------------------------------------------------------------------------- 1 | # Do not use insecure URLs (no-insecure-url) 2 | 3 | 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. 4 | 5 | - [Rule Source](../../lib/rules/no-insecure-url.js) 6 | - [Rule Test](../../tests/lib/rules/no-insecure-url.js) 7 | 8 | ## Options 9 | 10 | This rule comes with three [default lists](../../lib/rules/no-insecure-url.js#L13): 11 | 12 | - **blocklist** - a RegEx list of insecure URL patterns. 13 | - **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. 14 | - **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. 15 | 16 | These lists can be overrided by providing options. 17 | 18 | --- 19 | 20 | For example, providing these options... : 21 | 22 | ```javascript 23 | "@microsoft/sdl/no-insecure-url": ["error", { 24 | "blocklist": ["^(http|ftp):\\/\\/", "^https:\\/\\/www\\.disallow-example\\.com"], 25 | "exceptions": ["^http:\\/\\/schemas\\.microsoft\\.com\\/\\/?.*"], 26 | "varExceptions": ["insecure?.*"] 27 | }] 28 | ``` 29 | 30 | ... overrides the internal blocklist, blocking the following URL patterns... : 31 | 32 | - `http://`... 33 | - `ftp://`... 34 | - `https://www.disallow-example.com` 35 | 36 | ... and also overrides the internal exceptions list, allowing the following URL patterns as exceptions.: 37 | 38 | - `http://schemas.microsoft.com` 39 | - `http://schemas.microsoft.com/sharepoint` 40 | - `http://schemas.microsoft.com/path/subpath` 41 | 42 | ... and also overrides the internal variable exceptions list, allowing the following declaration name patterns as exceptions.: 43 | 44 | - `var insecureURL = "http://..."` 45 | - `var insecureWebsite = "http://..."` 46 | - ... 47 | 48 | URLs in neither the blocklist nor the exceptions list, are allowed: 49 | 50 | - `telnet://`... 51 | - `ws://`... 52 | - ... 53 | 54 | --- 55 | 56 | **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: 57 | 58 | ```javascript 59 | let pureRegex = /^https:\/\/www\.disallow-example\.com/; 60 | let regexInString = "^https:\\/\\/www\\.disallow-example\\.com"; 61 | ``` 62 | 63 | ## Related Rules 64 | 65 | - [tslint-microsoft-contrib/no-http-string](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noHttpStringRule.ts) 66 | - [CodeQL/InsecureDownloadCustomizations.qll](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll#L62) 67 | - [DevSkim/DS137138](https://github.com/microsoft/DevSkim/blob/main/guidance/DS137138.md) 68 | - [Fortify/insecure_transport](https://vulncat.fortify.com/en/detail?id=desc.config.java.insecure_transport#JavaScript%2fTypeScript) 69 | 70 | ## Further Reading 71 | 72 | - [HTTPS Everywhere](https://en.wikipedia.org/wiki/HTTPS_Everywhere) 73 | -------------------------------------------------------------------------------- /docs/rules/no-msapp-exec-unsafe.md: -------------------------------------------------------------------------------- 1 | # Do not bypass script injection validation (no-msapp-exec-unsafe) 2 | 3 | Calls to `MSApp.execUnsafeLocalFunction()` bypass script injection validation and should be avoided. 4 | -------------------------------------------------------------------------------- /docs/rules/no-postmessage-star-origin.md: -------------------------------------------------------------------------------- 1 | # Do not use \* as target origin when sending data to other windows (no-postmessage-star-origin) 2 | 3 | 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. 4 | -------------------------------------------------------------------------------- /docs/rules/no-unsafe-alloc.md: -------------------------------------------------------------------------------- 1 | # Do not allocate uninitialized buffers in Node.js (no-unsafe-alloc) 2 | 3 | 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. 4 | 5 | These methods should be used only in justifiable cases (e.g. due to performance reasons) after thorough security review. 6 | 7 | - [Rule Source](../../lib/rules/no-unsafe-alloc.js) 8 | - [Rule Test](../../tests/lib/rules/no-unsafe-alloc.js) 9 | 10 | ## Resources 11 | 12 | - [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) 13 | -------------------------------------------------------------------------------- /docs/rules/no-winjs-html-unsafe.md: -------------------------------------------------------------------------------- 1 | # Do not set HTML using unsafe methods from WinJS.Utilities (no-winjs-html-unsafe) 2 | 3 | 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. 4 | -------------------------------------------------------------------------------- /lib/ast-utils.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Common utils for AST. 6 | */ 7 | 8 | "use strict"; 9 | 10 | module.exports = { 11 | isTypeScriptParserServices(parserServices) { 12 | // Check properties specific to @typescript-eslint/parser 13 | return ( 14 | parserServices && 15 | parserServices.program && 16 | parserServices.esTreeNodeToTSNodeMap && 17 | parserServices.tsNodeToESTreeNodeMap 18 | ); 19 | }, 20 | hasFullTypeInformation(context) { 21 | var hasFullTypeInformation = 22 | context && 23 | context.sourceCode && 24 | context.sourceCode.parserServices && 25 | this.isTypeScriptParserServices(context.sourceCode.parserServices); 26 | return hasFullTypeInformation; 27 | }, 28 | getFullTypeChecker(context) { 29 | return this.hasFullTypeInformation(context) 30 | ? context.sourceCode.parserServices.program.getTypeChecker() 31 | : null; 32 | }, 33 | getNodeTypeAsString(fullTypeChecker, node, context) { 34 | if (fullTypeChecker && node) { 35 | const tsNode = context.sourceCode.parserServices.esTreeNodeToTSNodeMap.get(node); 36 | const tsType = fullTypeChecker.getTypeAtLocation(tsNode); 37 | const type = fullTypeChecker.typeToString(tsType); 38 | return type; 39 | } 40 | return "any"; 41 | }, 42 | isDocumentObject(node, context, fullTypeChecker) { 43 | if (fullTypeChecker) { 44 | const type = this.getNodeTypeAsString(fullTypeChecker, node, context); 45 | return type === "Document"; 46 | } 47 | 48 | // Best-effort checking without Type information 49 | switch (node.type) { 50 | case "Identifier": 51 | return node != undefined && node.name == "document"; 52 | case "MemberExpression": 53 | return ( 54 | node != undefined && 55 | node.property != undefined && 56 | node.property.name == "document" && 57 | ((node.object != undefined && 58 | typeof node.object.name === "string" && 59 | node.object.name.toLowerCase().endsWith("window")) || 60 | (node.object != undefined && 61 | node.object.property != undefined && 62 | node.object.property.name == "window" && 63 | ((node.object.object != undefined && node.object.object.type == "ThisExpression") || 64 | (node.object.object != undefined && node.object.object.name == "globalThis")))) 65 | ); 66 | } 67 | return false; 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | const path = require("path"); 7 | const pluginSecurity = require("eslint-plugin-security"); 8 | 9 | const pkg = require(path.join("..", "package.json")); 10 | 11 | const plugin = { 12 | meta: { 13 | name: pkg.name, 14 | version: pkg.version 15 | }, 16 | rules: { 17 | "no-angular-bypass-sanitizer": require("./rules/no-angular-bypass-sanitizer"), 18 | "no-angular-sanitization-trusted-urls": require("./rules/no-angular-sanitization-trusted-urls"), 19 | "no-angularjs-bypass-sce": require("./rules/no-angularjs-bypass-sce"), 20 | "no-angularjs-enable-svg": require("./rules/no-angularjs-enable-svg"), 21 | "no-angularjs-sanitization-whitelist": require("./rules/no-angularjs-sanitization-whitelist"), 22 | "no-cookies": require("./rules/no-cookies"), 23 | "no-document-domain": require("./rules/no-document-domain"), 24 | "no-document-write": require("./rules/no-document-write"), 25 | "no-electron-node-integration": require("./rules/no-electron-node-integration"), 26 | "no-html-method": require("./rules/no-html-method"), 27 | "no-inner-html": require("./rules/no-inner-html"), 28 | "no-insecure-random": require("./rules/no-insecure-random"), 29 | "no-insecure-url": require("./rules/no-insecure-url"), 30 | "no-msapp-exec-unsafe": require("./rules/no-msapp-exec-unsafe"), 31 | "no-postmessage-star-origin": require("./rules/no-postmessage-star-origin"), 32 | "no-unsafe-alloc": require("./rules/no-unsafe-alloc"), 33 | "no-winjs-html-unsafe": require("./rules/no-winjs-html-unsafe") 34 | }, 35 | // Filled in later in order to reference plugin itself. 36 | configs: {} 37 | }; 38 | 39 | plugin.configs["angular"] = require("../config/angular")(plugin); 40 | plugin.configs["angularjs"] = require("../config/angularjs")(plugin); 41 | plugin.configs["common"] = require("../config/common")(plugin); 42 | plugin.configs["electron"] = require("../config/electron")(plugin); 43 | plugin.configs["node"] = require("../config/node")(plugin); 44 | plugin.configs["react"] = require("../config/react")(plugin); 45 | plugin.configs["typescript"] = require("../config/typescript")(plugin); 46 | 47 | plugin.configs["required"] = [ 48 | ...plugin.configs["angular"], 49 | ...plugin.configs["angularjs"], 50 | ...plugin.configs["common"], 51 | ...plugin.configs["electron"], 52 | ...plugin.configs["node"], 53 | ...plugin.configs["react"] 54 | ]; 55 | 56 | plugin.configs["recommended"] = [ 57 | ...plugin.configs["required"], 58 | ...plugin.configs["typescript"], 59 | { 60 | plugins: { 61 | security: pluginSecurity 62 | } 63 | } 64 | ]; 65 | 66 | module.exports = plugin; 67 | -------------------------------------------------------------------------------- /lib/rules/no-angular-bypass-sanitizer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow bypassing Angular's built-in sanitizer 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | category: "Security", 18 | description: 19 | "Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass DomSanitizer in Angular and need to be reviewed.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-bypass-sanitizer.md" 21 | }, 22 | messages: { 23 | noBypass: "Do not bypass Angular's built-in sanitizer" 24 | } 25 | }, 26 | create: function (context) { 27 | return { 28 | "CallExpression[arguments!=''][callee.property.name=/bypassSecurityTrust(Html|ResourceUrl|Script|Style|Url)/]"( 29 | node 30 | ) { 31 | context.report({ 32 | node: node, 33 | messageId: "noBypass" 34 | }); 35 | } 36 | }; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/rules/no-angular-sanitization-trusted-urls.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow modifying sanitization allowed url list in AngularJS. Update fron the deprecate SanitizationWhitelist 6 | * @author Vivien Flouirac 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | category: "Security", 18 | description: 19 | "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.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-sanitization-trusted-urls.md" 21 | }, 22 | messages: { 23 | noSanitizationTrustedUrls: "Do not modify the trusted Urls list in AngularJS" 24 | } 25 | }, 26 | create: function (context) { 27 | return { 28 | "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationTrustedUrlList/]"( 29 | node 30 | ) { 31 | context.report({ 32 | node: node, 33 | messageId: "noSanitizationTrustedUrls" 34 | }); 35 | } 36 | }; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/rules/no-angularjs-bypass-sce.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow bypassing Strict Contextual Escaping (SCE) in AngularJS 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | category: "Security", 18 | description: 19 | "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.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-bypass-sce.md" 21 | }, 22 | messages: { 23 | doNotBypass: "Do not bypass Strict Contextual Escaping (SCE) in AngularJS" 24 | } 25 | }, 26 | create: function (context) { 27 | function reportIt(node) { 28 | context.report({ 29 | node: node, 30 | messageId: "doNotBypass" 31 | }); 32 | } 33 | 34 | return { 35 | "CallExpression[arguments!=''][callee.object.name='$sceProvider'][callee.property.name='enabled']"( 36 | node 37 | ) { 38 | // Known false positives 39 | if ( 40 | node.arguments == undefined || 41 | node.arguments.length != 1 || 42 | (node.arguments[0].type == "Literal" && /true|1/.test(node.arguments[0].value)) 43 | ) { 44 | return; 45 | } 46 | return reportIt(node); 47 | }, 48 | "CallExpression[arguments!=''][callee.object.name='$sceDelegate'][callee.property.name='trustAs']": 49 | reportIt, 50 | "CallExpression[arguments!=''][callee.object.name='$sce'][callee.property.name=/trustAs(Css|Html|Js|ResourceUrl|Url)?/]"( 51 | node 52 | ) { 53 | // Known false positives 54 | if ( 55 | node.arguments && 56 | node.arguments.length === 1 && 57 | node.arguments[0].type === "Literal" && 58 | node.arguments[0].value === "" 59 | ) { 60 | return; 61 | } 62 | 63 | return reportIt(node); 64 | } 65 | }; 66 | } 67 | }; 68 | 69 | // TODO: Review https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlWhitelist and https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlBlacklist 70 | -------------------------------------------------------------------------------- /lib/rules/no-angularjs-enable-svg.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow enabling SVG in AngularJS apps 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | category: "Security", 18 | description: 19 | "Calls to $sanitizeProvider.enableSvg(true) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-enable-svg.md" 21 | }, 22 | messages: { 23 | doNotEnableSVG: "Do not enable SVG support in AngularJS" 24 | } 25 | }, 26 | create: function (context) { 27 | return { 28 | "CallExpression[callee.object.name='$sanitizeProvider'][callee.property.name='enableSvg']"( 29 | node 30 | ) { 31 | // Known false positives 32 | if ( 33 | (node.arguments != undefined && node.arguments.length != 1) || 34 | (node.arguments[0].type == "Literal" && 35 | (node.arguments[0].value == "false" || node.arguments[0].value == "0")) 36 | ) { 37 | return; 38 | } 39 | context.report({ 40 | node: node, 41 | messageId: "doNotEnableSVG" 42 | }); 43 | } 44 | }; 45 | } 46 | }; 47 | 48 | // TODO: Add rules for $sanitizeProvider.addValidElements() and $sanitizeProvider.addValidAttrs() 49 | -------------------------------------------------------------------------------- /lib/rules/no-angularjs-sanitization-whitelist.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow modifying sanitization whitelist in AngularJS 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | category: "Security", 18 | description: 19 | "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.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-sanitization-whitelist.md" 21 | }, 22 | messages: { 23 | noSanitizationWhitelist: "Do not modify sanitization whitelist in AngularJS" 24 | } 25 | }, 26 | create: function (context) { 27 | return { 28 | "CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationWhitelist/]"( 29 | node 30 | ) { 31 | context.report({ 32 | node: node, 33 | messageId: "noSanitizationWhitelist" 34 | }); 35 | } 36 | }; 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /lib/rules/no-cookies.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow usage of HTTP cookies 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | 13 | module.exports = { 14 | meta: { 15 | type: "suggestion", 16 | fixable: "code", 17 | schema: [], 18 | docs: { 19 | category: "Security", 20 | description: 21 | "HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other more modern methods instead.", 22 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-cookies.md" 23 | }, 24 | messages: { 25 | doNotUseCookies: "Do not use HTTP cookies in modern applications" 26 | } 27 | }, 28 | create: function (context) { 29 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 30 | return { 31 | "MemberExpression[property.name='cookie']"(node) { 32 | if (astUtils.isDocumentObject(node.object, context, fullTypeChecker)) { 33 | context.report({ 34 | node: node, 35 | messageId: "doNotUseCookies" 36 | }); 37 | } 38 | } 39 | }; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /lib/rules/no-document-domain.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow document.domain property 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | 13 | module.exports = { 14 | meta: { 15 | type: "suggestion", 16 | fixable: "code", 17 | schema: [], 18 | docs: { 19 | category: "Security", 20 | description: 21 | "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.", 22 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-domain.md" 23 | }, 24 | messages: { 25 | default: "Do not write to document.domain property" 26 | } 27 | }, 28 | create: function (context) { 29 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 30 | return { 31 | "AssignmentExpression[operator='='][left.property.name='domain']"(node) { 32 | if (astUtils.isDocumentObject(node.left.object, context, fullTypeChecker)) { 33 | context.report({ 34 | node: node, 35 | messageId: "default" 36 | }); 37 | } 38 | } 39 | }; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /lib/rules/no-document-write.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow document.write or document.writeln method call 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | 13 | module.exports = { 14 | meta: { 15 | type: "suggestion", 16 | fixable: "code", 17 | schema: [], 18 | docs: { 19 | category: "Security", 20 | description: 21 | "Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.", 22 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-write.md" 23 | }, 24 | messages: { 25 | default: "Do not write to DOM directly using document.write or document.writeln methods" 26 | } 27 | }, 28 | create: function (context) { 29 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 30 | return { 31 | "CallExpression[arguments.length=1][callee.property.name=/write|writeln/]"(node) { 32 | if (astUtils.isDocumentObject(node.callee.object, context, fullTypeChecker)) { 33 | context.report({ 34 | node: node, 35 | messageId: "default" 36 | }); 37 | } 38 | } 39 | }; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /lib/rules/no-electron-node-integration.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow enabling Node.js integration in Electron apps 6 | */ 7 | 8 | "use strict"; 9 | 10 | module.exports = { 11 | meta: { 12 | type: "suggestion", 13 | fixable: "code", 14 | schema: [], 15 | docs: { 16 | category: "Security", 17 | description: 18 | "[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.", 19 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-electron-node-integration.md" 20 | }, 21 | messages: { 22 | default: "Do not enable Node.js Integration for Remote Content" 23 | } 24 | }, 25 | create: function (context) { 26 | return { 27 | "NewExpression[callee.name=/BrowserWindow|BrowserView/] > ObjectExpression.arguments > Property.properties[key.name=webPreferences] > ObjectExpression.value > Property.properties[key.name=/nodeIntegration|nodeIntegrationInWorker|nodeIntegrationInSubFrames/][value.value='true']"( 28 | node 29 | ) { 30 | context.report({ 31 | node: node, 32 | messageId: "default" 33 | }); 34 | } 35 | }; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /lib/rules/no-html-method.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow call to html() method 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | 13 | module.exports = { 14 | meta: { 15 | type: "suggestion", 16 | fixable: "code", 17 | schema: [], 18 | docs: { 19 | description: 20 | "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.", 21 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-html-method.md" 22 | }, 23 | messages: { 24 | default: "Do not write to DOM directly using jQuery html() method" 25 | } 26 | }, 27 | create: function (context) { 28 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 29 | return { 30 | // TODO: 31 | // - Cover similar methods that can manipulate DOM such as append(string), jQuery(string) 32 | // - Improve rule with type information from TypeScript parser 33 | // - Consider ignoring all Literals? 34 | "CallExpression[arguments.length=1] > MemberExpression.callee[property.name='html']"(node) { 35 | // Known false positives 36 | if ( 37 | // element.html("") 38 | node.parent.arguments[0].type === "Literal" && 39 | (node.parent.arguments[0].value === "" || node.parent.arguments[0].value === null) 40 | ) { 41 | return; 42 | } 43 | context.report({ 44 | node: node, 45 | messageId: "default" 46 | }); 47 | } 48 | }; 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /lib/rules/no-inner-html.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow assignment to innerHTML or outerHTML properties 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | 13 | module.exports = { 14 | meta: { 15 | type: "suggestion", 16 | fixable: "code", 17 | schema: [], 18 | docs: { 19 | description: 20 | "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.", 21 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-inner-html.md" 22 | }, 23 | messages: { 24 | noInnerHtml: "Do not write to DOM directly using innerHTML/outerHTML property", 25 | noInsertAdjacentHTML: "Do not write to DOM using insertAdjacentHTML method" 26 | } 27 | }, 28 | create: function (context) { 29 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 30 | 31 | function mightBeHTMLElement(node) { 32 | const type = astUtils.getNodeTypeAsString(fullTypeChecker, node, context); 33 | return type.match(/HTML.*Element/) || type === "any"; 34 | } 35 | 36 | return { 37 | "CallExpression[arguments.length=2] > MemberExpression.callee[property.name='insertAdjacentHTML']"( 38 | node 39 | ) { 40 | // Ignore known false positives 41 | if ( 42 | node.parent != undefined && 43 | node.parent.arguments != undefined && 44 | node.parent.arguments.length >= 1 && 45 | node.parent.arguments[1] != undefined && 46 | // element.insertAdjacentHTML('') 47 | node.parent.arguments[1].type === "Literal" && 48 | node.parent.arguments[1].value === "" 49 | ) { 50 | return; 51 | } 52 | 53 | if (mightBeHTMLElement(node.object)) { 54 | context.report({ 55 | node: node, 56 | messageId: "noInsertAdjacentHTML" 57 | }); 58 | } 59 | }, 60 | "AssignmentExpression[left.type='MemberExpression'][left.property.name=/innerHTML|outerHTML/]"( 61 | node 62 | ) { 63 | // Ignore known false positives 64 | if ( 65 | node.right != undefined && 66 | // element.innerHTML = '' 67 | node.right.type === "Literal" && 68 | node.right.value === "" 69 | ) { 70 | return; 71 | } 72 | 73 | if (mightBeHTMLElement(node.left.object)) { 74 | context.report({ 75 | node: node, 76 | messageId: "noInnerHtml" 77 | }); 78 | } 79 | } 80 | }; 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /lib/rules/no-insecure-random.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow call to Math.random and crypto.pseudoRandomBytes functions 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | const astUtils = require("../ast-utils"); 12 | const path = require("path"); 13 | 14 | const bannedRandomLibraries = [ 15 | "chance", 16 | "random-number", 17 | "random-int", 18 | "random-float", 19 | "random-seed", 20 | "unique-random" 21 | ]; 22 | 23 | module.exports = { 24 | meta: { 25 | type: "suggestion", 26 | fixable: "code", 27 | schema: [], 28 | docs: { 29 | 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. 30 | 31 | Use crypto.randomBytes() or window.crypto.getRandomValues() instead. 32 | 33 | `, 34 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-random.md" 35 | }, 36 | messages: { 37 | default: 38 | "Do not use pseudo-random number generators for generating secret values such as tokens, passwords or keys." 39 | } 40 | }, 41 | create: function (context) { 42 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 43 | return { 44 | "CallExpression > MemberExpression[property.name='pseudoRandomBytes']"(node) { 45 | var notFalsePositive = false; 46 | 47 | if (fullTypeChecker) { 48 | const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); 49 | notFalsePositive = type === "any" || type === "Crypto"; 50 | } else { 51 | notFalsePositive = node.object.name === "crypto"; 52 | } 53 | 54 | if (notFalsePositive) { 55 | context.report({ 56 | node: node, 57 | messageId: "default" 58 | }); 59 | } 60 | }, 61 | "CallExpression > MemberExpression[property.name='random']"(node) { 62 | var notFalsePositive = false; 63 | if (fullTypeChecker) { 64 | const type = astUtils.getNodeTypeAsString(fullTypeChecker, node.object, context); 65 | notFalsePositive = type === "any" || type === "Math"; 66 | } else { 67 | notFalsePositive = node.object.name === "Math"; 68 | } 69 | 70 | if (notFalsePositive) { 71 | context.report({ 72 | node: node, 73 | messageId: "default" 74 | }); 75 | } 76 | }, 77 | ImportDeclaration(node) { 78 | if (bannedRandomLibraries.includes(path.basename(node.source.value))) { 79 | context.report({ 80 | node: node, 81 | messageId: "default" 82 | }); 83 | } 84 | }, 85 | "CallExpression[callee.name='require'][arguments.length=1]"(node) { 86 | var requireName = path.parse(path.basename(node.arguments[0].value)).name; 87 | if (bannedRandomLibraries.includes(requireName)) { 88 | context.report({ 89 | node: node, 90 | messageId: "default" 91 | }); 92 | } 93 | } 94 | }; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /lib/rules/no-insecure-url.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Disallows usage of insecure protocols in URL strings 6 | */ 7 | 8 | "use strict"; 9 | 10 | const DEFAULT_BLOCKLIST = [/^(ftp|http|telnet|ws):\/\//i]; 11 | 12 | const DEFAULT_EXCEPTIONS = [ 13 | // TODO: add more typical false positives such as XML schemas after more testing 14 | /^http:(\/\/|\\u002f\\u002f)schemas\.microsoft\.com(\/\/|\\u002f\\u002f)?.*/i, 15 | /^http:(\/\/|\\u002f\\u002f)schemas\.openxmlformats\.org(\/\/|\\u002f\\u002f)?.*/i, 16 | /^http:(\/|\\u002f){2}localhost(:|\/|\\u002f)*/i, 17 | /^http:(\/\/)www\.w3\.org\/1999\/xhtml/i, 18 | /^http:(\/\/)www\.w3\.org\/2000\/svg/i 19 | ]; 20 | 21 | const DEFAULT_VARIABLES_EXECEPTIONS = []; 22 | 23 | module.exports = { 24 | defaultBlocklist: DEFAULT_BLOCKLIST, 25 | defaultExceptions: DEFAULT_EXCEPTIONS, 26 | defaultVarExecptions: DEFAULT_VARIABLES_EXECEPTIONS, 27 | meta: { 28 | type: "suggestion", 29 | fixable: "code", 30 | schema: [ 31 | { 32 | type: "object", 33 | properties: { 34 | blocklist: { 35 | type: "array", 36 | items: { 37 | type: "string" 38 | } 39 | }, 40 | exceptions: { 41 | type: "array", 42 | items: { 43 | type: "string" 44 | } 45 | }, 46 | varExceptions: { 47 | type: "array", 48 | items: { 49 | type: "string" 50 | } 51 | } 52 | }, 53 | additionalProperties: false 54 | } 55 | ], 56 | docs: { 57 | category: "Security", 58 | description: 59 | "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.", 60 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-url.md" 61 | }, 62 | messages: { 63 | doNotUseInsecureUrl: "Do not use insecure URLs" 64 | } 65 | }, 66 | create: function (context) { 67 | const options = context.options[0] || {}; 68 | const blocklist = (options.blocklist || DEFAULT_BLOCKLIST).map((pattern) => { 69 | return new RegExp(pattern, "i"); 70 | }); 71 | const exceptions = (options.exceptions || DEFAULT_EXCEPTIONS).map((pattern) => { 72 | return new RegExp(pattern, "i"); 73 | }); 74 | const varExceptions = (options.varExceptions || DEFAULT_VARIABLES_EXECEPTIONS).map( 75 | (pattern) => { 76 | return new RegExp(pattern, "i"); 77 | } 78 | ); 79 | 80 | function matches(patterns, value) { 81 | return ( 82 | patterns.find((re) => { 83 | return re.test(value); 84 | }) !== undefined 85 | ); 86 | } 87 | 88 | function shouldFix(varExceptions, context, node) { 89 | // check variable for unfixable pattern e.g. `var insecureURL = "http://..."` 90 | let text = node.parent 91 | ? context.sourceCode.getText(node.parent) 92 | : context.sourceCode.getText(node); 93 | // if no match, fix the line 94 | return !matches(varExceptions, text); 95 | } 96 | 97 | return { 98 | Literal(node) { 99 | if (typeof node.value === "string") { 100 | // Add an exception for xmlns attributes 101 | if ( 102 | node.parent && 103 | node.parent.type === "JSXAttribute" && 104 | node.parent.name && 105 | node.parent.name.name === "xmlns" 106 | ) { 107 | // Do nothing 108 | } else if ( 109 | matches(blocklist, node.value) && 110 | !matches(exceptions, node.value) && 111 | shouldFix(varExceptions, context, node) 112 | ) { 113 | context.report({ 114 | node: node, 115 | messageId: "doNotUseInsecureUrl", 116 | fix(fixer) { 117 | // Only fix if it contains an http url 118 | if (node.value.toLowerCase().includes("http")) { 119 | let fixedString = node.value.replace(/http:/i, "https:"); 120 | //insert an "s" before ":/" to change http:/ to https:/ 121 | return fixer.replaceText(node, JSON.stringify(fixedString)); 122 | } 123 | } 124 | }); 125 | } 126 | } 127 | }, 128 | TemplateElement(node) { 129 | if (typeof node.value.raw === "string" && typeof node.value.cooked === "string") { 130 | const rawStringText = node.value.raw; 131 | const cookedStringText = node.value.cooked; 132 | 133 | if ( 134 | (shouldFix(varExceptions, context, node) && 135 | matches(blocklist, rawStringText) && 136 | !matches(exceptions, rawStringText)) || 137 | (matches(blocklist, cookedStringText) && !matches(exceptions, cookedStringText)) 138 | ) { 139 | context.report({ 140 | node: node, 141 | messageId: "doNotUseInsecureUrl", 142 | fix(fixer) { 143 | // Only fix if it contains an http url 144 | if (node.value.raw.toLowerCase().includes("http")) { 145 | let escapedString = JSON.stringify(context.sourceCode.getText(node)); 146 | // delete "" that JSON.stringify created and convert to `` string 147 | escapedString = `` + escapedString.substring(1, escapedString.length - 1); 148 | let fixedString = escapedString.replace(/http:/i, "https:"); 149 | //insert an "s" before ":/" to change http:/ to https:/ 150 | return fixer.replaceText(node, fixedString); 151 | } 152 | } 153 | }); 154 | } 155 | } 156 | } 157 | }; 158 | } 159 | }; 160 | -------------------------------------------------------------------------------- /lib/rules/no-msapp-exec-unsafe.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow MSApp.execUnsafeLocalFunction method call 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | description: 18 | "Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided.", 19 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-msapp-exec-unsafe.md" 20 | }, 21 | messages: { 22 | default: "Do not bypass script injection validation" 23 | } 24 | }, 25 | create: function (context) { 26 | return { 27 | "CallExpression[arguments.length=1][callee.object.name='MSApp'][callee.property.name='execUnsafeLocalFunction']"( 28 | node 29 | ) { 30 | context.report({ 31 | node: node, 32 | messageId: "default" 33 | }); 34 | } 35 | }; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /lib/rules/no-postmessage-star-origin.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow * as target origin in window.postMessage 6 | */ 7 | 8 | "use strict"; 9 | 10 | const astUtils = require("../ast-utils"); 11 | 12 | module.exports = { 13 | meta: { 14 | type: "suggestion", 15 | fixable: "code", 16 | schema: [], 17 | docs: { 18 | description: 19 | "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.", 20 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-postmessage-star-origin.md" 21 | }, 22 | messages: { 23 | default: "Do not use * as target origin when sending data to other windows" 24 | } 25 | }, 26 | create: function (context) { 27 | const fullTypeChecker = astUtils.getFullTypeChecker(context); 28 | return { 29 | "CallExpression[arguments.length>=2][arguments.length<=3][callee.property.name=postMessage]"( 30 | node 31 | ) { 32 | // Check that second argument (target origin) is Literal "*" 33 | if (!(node.arguments[1].type === "Literal" && node.arguments[1].value == "*")) { 34 | return; 35 | } 36 | 37 | // Check that object type is Window when full type information is available 38 | if (fullTypeChecker) { 39 | const tsNode = context.sourceCode.parserServices.esTreeNodeToTSNodeMap.get( 40 | node.callee.object 41 | ); 42 | const tsType = fullTypeChecker.getTypeAtLocation(tsNode); 43 | const type = fullTypeChecker.typeToString(tsType); 44 | if (type !== "any" && type !== "Window") { 45 | return; 46 | } 47 | } 48 | 49 | context.report({ 50 | node: node, 51 | messageId: "default" 52 | }); 53 | } 54 | }; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /lib/rules/no-unsafe-alloc.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | 6 | module.exports = { 7 | meta: { 8 | type: "suggestion", 9 | fixable: "code", 10 | schema: [], 11 | docs: { 12 | description: 13 | "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.", 14 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-unsafe-alloc.md" 15 | }, 16 | messages: { 17 | default: "Do not allocate uninitialized buffers in Node.js" 18 | } 19 | }, 20 | create: function (context) { 21 | return { 22 | "MemberExpression[object.name='Buffer'][property.name=/allocUnsafe|allocUnsafeSlow/]"(node) { 23 | // Known false positives 24 | if ( 25 | node.parent != undefined && 26 | node.parent.arguments != undefined && 27 | node.parent.arguments.length != undefined && 28 | // Buffer.allocUnsafe(0); 29 | node.parent.type === "CallExpression" && 30 | node.parent.arguments.length == 1 && 31 | node.parent.arguments[0] != undefined && 32 | node.parent.arguments[0].type === "Literal" && 33 | node.parent.arguments[0].value == "0" 34 | ) { 35 | return; 36 | } 37 | context.report({ 38 | node: node, 39 | messageId: "default" 40 | }); 41 | } 42 | }; 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /lib/rules/no-winjs-html-unsafe.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Rule to disallow WinJS.Utilities.setInnerHTMLUnsafe or WinJS.Utilities.setOuterHTMLUnsafe method call 6 | * @author Antonios Katopodis 7 | */ 8 | 9 | "use strict"; 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | fixable: "code", 15 | schema: [], 16 | docs: { 17 | description: 18 | "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.", 19 | url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-winjs-html-unsafe.md" 20 | }, 21 | messages: { 22 | default: "Do not set HTML using unsafe methods from WinJS.Utilities" 23 | } 24 | }, 25 | create: function (context) { 26 | return { 27 | "CallExpression[callee.object.object.name='WinJS'][callee.object.property.name='Utilities'][callee.property.name=/(insertAdjacent|setInner|setOuter)HTMLUnsafe/]"( 28 | node 29 | ) { 30 | context.report({ 31 | node: node, 32 | messageId: "default" 33 | }); 34 | } 35 | }; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@microsoft/eslint-plugin-sdl", 3 | "version": "1.1.0", 4 | "description": "ESLint plugin focused on common security issues and misconfigurations discoverable during static testing as part of Microsoft Security Development Lifecycle (SDL)", 5 | "keywords": [ 6 | "eslint", 7 | "eslintplugin", 8 | "eslint-plugin", 9 | "sdl" 10 | ], 11 | "author": "Microsoft", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/microsoft/eslint-plugin-sdl" 15 | }, 16 | "homepage": "https://github.com/microsoft/eslint-plugin-sdl", 17 | "bugs": "https://github.com/microsoft/eslint-plugin-sdl/issues", 18 | "main": "lib/index.js", 19 | "scripts": { 20 | "check-fmt": "prettier . --check", 21 | "fmt": "prettier . --write", 22 | "test": "mocha tests --recursive" 23 | }, 24 | "dependencies": { 25 | "eslint-plugin-n": "17.10.3", 26 | "eslint-plugin-react": "7.37.3", 27 | "eslint-plugin-security": "1.4.0" 28 | }, 29 | "devDependencies": { 30 | "@typescript-eslint/eslint-plugin": "~8.14.0", 31 | "@typescript-eslint/parser": "~8.14.0", 32 | "eslint": "~9.15.0", 33 | "mocha": "~10.8.0", 34 | "prettier": "~3.3.0", 35 | "typescript": "~5.6.0" 36 | }, 37 | "peerDependencies": { 38 | "eslint": "^9" 39 | }, 40 | "engines": { 41 | "node": ">=18.0.0" 42 | }, 43 | "license": "MIT" 44 | } 45 | -------------------------------------------------------------------------------- /tests/fixtures/ts/estree.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/eslint-plugin-sdl/3196a29da75f029c44e5b89d03601415597064ef/tests/fixtures/ts/estree.ts -------------------------------------------------------------------------------- /tests/fixtures/ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["estree.ts"] 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/tsx/estree.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/eslint-plugin-sdl/3196a29da75f029c44e5b89d03601415597064ef/tests/fixtures/tsx/estree.tsx -------------------------------------------------------------------------------- /tests/fixtures/tsx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "preserve" 4 | }, 5 | "include": ["estree.tsx"] 6 | } 7 | -------------------------------------------------------------------------------- /tests/lib/rules/no-angular-bypass-sanitizer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | "bypassSecurityTrustHtml('XSS')", 13 | "x.bypassSecurityTrustHtml()", 14 | "x.BypassSecurityTrustHtml('XSS')" 15 | ], 16 | invalid: [ 17 | { 18 | code: "$('p').bypassSecurityTrustHtml('XSS');", 19 | errors: [ 20 | { 21 | messageId: "noBypass", 22 | line: 1, 23 | endLine: 1, 24 | column: 1, 25 | endColumn: 38 26 | } 27 | ] 28 | }, 29 | { 30 | code: "$('p').bypassSecurityTrustResourceUrl('XSS')", 31 | errors: [{ messageId: "noBypass" }] 32 | }, 33 | { 34 | code: "$('p').bypassSecurityTrustScript('XSS')", 35 | errors: [{ messageId: "noBypass" }] 36 | }, 37 | { 38 | code: "$('p').bypassSecurityTrustStyle('XSS')", 39 | errors: [{ messageId: "noBypass" }] 40 | }, 41 | { 42 | code: "$('p').bypassSecurityTrustUrl('XSS')", 43 | errors: [{ messageId: "noBypass" }] 44 | } 45 | ] 46 | }); 47 | -------------------------------------------------------------------------------- /tests/lib/rules/no-angular-sanitization-trusted-urls.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | "aHrefSanitizationTrustedUrlList ('.*')", 13 | "x.aHrefSanitizationTrustedUrlList ('.*')", 14 | "$compileProvider.aHrefSanitizationTrustedUrlList ()", 15 | "$compileProvider.AHrefSanitizationTrustedUrlList ('.*')" 16 | ], 17 | invalid: [ 18 | { 19 | code: "$compileProvider.aHrefSanitizationTrustedUrlList ('.*');", 20 | errors: [ 21 | { 22 | messageId: "noSanitizationTrustedUrls", 23 | line: 1, 24 | endLine: 1, 25 | column: 1, 26 | endColumn: 56 27 | } 28 | ] 29 | }, 30 | { 31 | code: "$compileProvider.imgSrcSanitizationTrustedUrlList('.*');", 32 | errors: [ 33 | { 34 | messageId: "noSanitizationTrustedUrls", 35 | line: 1, 36 | endLine: 1, 37 | column: 1, 38 | endColumn: 56 39 | } 40 | ] 41 | } 42 | ] 43 | }); 44 | -------------------------------------------------------------------------------- /tests/lib/rules/no-angularjs-bypass-sce.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | "trustAsHtml()", 13 | "$sce.trustAsHtml()", 14 | "$sce.trustAsHtml('')", 15 | "$sce.TrustAsHtml('XSS')", 16 | "x.trustAsHtml('XSS')", 17 | "$sceProvider.enabled()", 18 | "$sceProvider.enabled(true)", 19 | "$sceProvider.enabled(1)" 20 | ], 21 | invalid: [ 22 | { 23 | code: "$sceDelegate.trustAs($sce.HTML, 'XSS')", 24 | errors: [{ messageId: "doNotBypass" }] 25 | }, 26 | { 27 | code: "$sce.trustAs($sce.HTML, 'XSS')", 28 | errors: [{ messageId: "doNotBypass" }] 29 | }, 30 | { 31 | code: "$sce.trustAsCss('XSS')", 32 | errors: [{ messageId: "doNotBypass" }] 33 | }, 34 | { 35 | code: "$sce.trustAsHtml('XSS')", 36 | errors: [{ messageId: "doNotBypass" }] 37 | }, 38 | { 39 | code: "$sce.trustAsJs('XSS')", 40 | errors: [{ messageId: "doNotBypass" }] 41 | }, 42 | { 43 | code: "$sce.trustAsResourceUrl('XSS')", 44 | errors: [{ messageId: "doNotBypass" }] 45 | }, 46 | { 47 | code: "$sce.trustAsUrl('XSS')", 48 | errors: [{ messageId: "doNotBypass" }] 49 | }, 50 | { 51 | code: "$sceProvider.enabled(false)", 52 | errors: [{ messageId: "doNotBypass" }] 53 | }, 54 | { 55 | code: "$sceProvider.enabled(0)", 56 | errors: [{ messageId: "doNotBypass" }] 57 | }, 58 | { 59 | code: "$sceProvider.enabled(true != true)", 60 | errors: [{ messageId: "doNotBypass" }] 61 | } 62 | ] 63 | }); 64 | -------------------------------------------------------------------------------- /tests/lib/rules/no-angularjs-enable-svg.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | "enableSvg()", 13 | "enableSvg(true)", 14 | "$sanitizeProvider.enableSvg()", 15 | "$sanitizeProvider.enableSvg(false)", 16 | "$sanitizeProvider.enableSvg(0)", 17 | "$sanitizeProvider.EnableSvg(0)" 18 | ], 19 | invalid: [ 20 | { 21 | code: "$sanitizeProvider.enableSvg(true)", 22 | errors: [{ messageId: "doNotEnableSVG" }] 23 | }, 24 | { 25 | code: "$sanitizeProvider.enableSvg(1)", 26 | errors: [{ messageId: "doNotEnableSVG" }] 27 | } 28 | ] 29 | }); 30 | -------------------------------------------------------------------------------- /tests/lib/rules/no-angularjs-sanitization-whitelist.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | "aHrefSanitizationWhitelist('.*')", 13 | "x.aHrefSanitizationWhitelist('.*')", 14 | "$compileProvider.aHrefSanitizationWhitelist()", 15 | "$compileProvider.AHrefSanitizationWhitelist('.*')" 16 | ], 17 | invalid: [ 18 | { 19 | code: "$compileProvider.aHrefSanitizationWhitelist('.*');", 20 | errors: [ 21 | { 22 | messageId: "noSanitizationWhitelist", 23 | line: 1, 24 | endLine: 1, 25 | column: 1, 26 | endColumn: 50 27 | } 28 | ] 29 | }, 30 | { 31 | code: "$compileProvider.imgSrcSanitizationWhitelist('.*');", 32 | errors: [ 33 | { 34 | messageId: "noSanitizationWhitelist", 35 | line: 1, 36 | endLine: 1, 37 | column: 1, 38 | endColumn: 51 39 | } 40 | ] 41 | } 42 | ] 43 | }); 44 | -------------------------------------------------------------------------------- /tests/lib/rules/no-cookies.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | const testUtils = require("../test-utils"); 9 | 10 | var ruleTester = new RuleTester(); 11 | 12 | ruleTester.run(ruleId, rule, { 13 | valid: [ 14 | ` 15 | function documentLikeAPIFunction(){ 16 | return { 17 | cookie:'fake.cookie' 18 | } 19 | } 20 | var document2 = documentLikeAPIFunction(); 21 | document2.cookie = '...'; 22 | document2.cookie = '...'; 23 | documentLikeAPIFunction().cookie = '...' 24 | `, 25 | { 26 | languageOptions: testUtils.tsLanguageOptions, 27 | code: ` 28 | interface DocumentLikeAPI { 29 | cookie: string; 30 | } 31 | function documentLikeAPIFunction(): DocumentLikeAPI { 32 | return null; 33 | } 34 | function X() { 35 | // These usages are OK because they are not on the DOM document 36 | var document: DocumentLikeAPI = documentLikeAPIFunction(); 37 | document.cookie = '...'; 38 | document.cookie = '...'; 39 | } 40 | 41 | documentLikeAPIFunction().cookie = '...'; 42 | ` 43 | } 44 | ], 45 | invalid: [ 46 | { 47 | code: "document.cookie = '...'", 48 | errors: [{ messageId: "doNotUseCookies" }] 49 | }, 50 | { 51 | code: "window.document.cookie = '...'", 52 | errors: [{ messageId: "doNotUseCookies" }] 53 | }, 54 | { 55 | code: "this.window.document.cookie = '...'", 56 | errors: [{ messageId: "doNotUseCookies" }] 57 | }, 58 | { 59 | code: "globalThis.window.document.cookie = '...'", 60 | errors: [{ messageId: "doNotUseCookies" }] 61 | }, 62 | { 63 | languageOptions: testUtils.tsLanguageOptions, 64 | code: ` 65 | function documentFunction(): Document { 66 | return window.document; 67 | } 68 | documentFunction().cookie = '...'; 69 | `, 70 | errors: [{ messageId: "doNotUseCookies" }] 71 | }, 72 | { 73 | languageOptions: testUtils.tsLanguageOptions, 74 | code: ` 75 | namespace Sample { 76 | function method() { 77 | return document.cookie; 78 | } 79 | } 80 | `, 81 | errors: [{ messageId: "doNotUseCookies" }] 82 | } 83 | ] 84 | }); 85 | -------------------------------------------------------------------------------- /tests/lib/rules/no-document-domain.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const testUtils = require("../test-utils"); 6 | const ruleId = path.parse(__filename).name; 7 | const rule = require(path.join("../../../lib/rules/", ruleId)); 8 | const RuleTester = require("eslint").RuleTester; 9 | var ruleTester = new RuleTester(); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | { 14 | languageOptions: testUtils.tsLanguageOptions, 15 | code: ` 16 | interface DocumentLikeAPI { 17 | domain: string; 18 | } 19 | function documentLikeAPIFunction(): DocumentLikeAPI { 20 | return null; 21 | } 22 | function main() { 23 | var document: DocumentLikeAPI = documentLikeAPIFunction(); 24 | document.domain = 'somevalue'; 25 | } 26 | ` 27 | } 28 | ], 29 | invalid: [ 30 | { 31 | languageOptions: testUtils.tsLanguageOptions, 32 | code: "var doc = window.document; doc.domain = 'somevalue';", 33 | errors: [{ messageId: "default" }] 34 | }, 35 | { 36 | code: "document.domain = 'somevalue'", 37 | errors: [{ messageId: "default" }] 38 | }, 39 | { 40 | code: "window.document.domain = 'somevalue'", 41 | errors: [{ messageId: "default" }] 42 | }, 43 | { 44 | code: ` 45 | var somevalue = 'somevalue'; 46 | document.domain = somevalue; 47 | window.document.domain = somevalue; 48 | newWindow.document.domain = somevalue; 49 | `, 50 | errors: [ 51 | { 52 | line: 3, 53 | messageId: "default" 54 | }, 55 | { 56 | line: 4, 57 | messageId: "default" 58 | }, 59 | { 60 | line: 5, 61 | messageId: "default" 62 | } 63 | ] 64 | } 65 | ] 66 | }); 67 | -------------------------------------------------------------------------------- /tests/lib/rules/no-document-write.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const testUtils = require("../test-utils"); 6 | const ruleId = path.parse(__filename).name; 7 | const rule = require(path.join("../../../lib/rules/", ruleId)); 8 | const RuleTester = require("eslint").RuleTester; 9 | var ruleTester = new RuleTester(); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | { 14 | languageOptions: testUtils.tsLanguageOptions, 15 | code: ` 16 | interface DocumentLikeAPI { 17 | write: ((arg : string) => void); 18 | writeln: ((arg : string) => void); 19 | } 20 | function documentLikeAPIFunction() : DocumentLikeAPI { 21 | return { 22 | write: () => {}, 23 | writeln: () => {}, 24 | }; 25 | } 26 | ` 27 | }, 28 | { 29 | code: ` 30 | function documentLikeAPIFunction() { 31 | return { 32 | write: function(){}, 33 | writeln: function(){} 34 | }; 35 | } 36 | var documentAPI = documentLikeAPIFunction(); 37 | documentAPI.write('...'); 38 | documentAPI.writeln('...'); 39 | documentLikeAPIFunction().write('...'); 40 | documentLikeAPIFunction().writeln('...'); 41 | // wrong # of args 42 | document.write(); 43 | document.write('', ''); 44 | document.writeln(); 45 | document.writeln('', ''); 46 | ` 47 | } 48 | ], 49 | invalid: [ 50 | { 51 | languageOptions: testUtils.tsLanguageOptions, 52 | code: ` 53 | var doc = document; 54 | doc.write('...'); 55 | doc.writeln('...'); 56 | function documentFunction() : Document { 57 | return window.document; 58 | } 59 | documentFunction().write('...'); 60 | documentFunction().writeln('...'); 61 | `, 62 | errors: [ 63 | { messageId: "default", line: 3 }, 64 | { messageId: "default", line: 4 }, 65 | { messageId: "default", line: 8 }, 66 | { messageId: "default", line: 9 } 67 | ] 68 | }, 69 | { 70 | code: ` 71 | document.write('...'); 72 | document.writeln('...'); 73 | window.document.write('...'); 74 | window.document.writeln('...'); 75 | newWindow.document.write('...'); 76 | newWindow.document.writeln('...'); 77 | `, 78 | errors: [ 79 | { messageId: "default", line: 2 }, 80 | { messageId: "default", line: 3 }, 81 | { messageId: "default", line: 4 }, 82 | { messageId: "default", line: 5 }, 83 | { messageId: "default", line: 6 }, 84 | { messageId: "default", line: 7 } 85 | ] 86 | } 87 | ] 88 | }); 89 | -------------------------------------------------------------------------------- /tests/lib/rules/no-electron-node-integration.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | 10 | ruleTester.run(ruleId, rule, { 11 | valid: [ 12 | { 13 | code: ` 14 | var mainWindow = new BrowserWindow({ 15 | webPreferences: { 16 | nodeIntegration: false, 17 | nodeIntegrationInWorker: false, 18 | nodeIntegrationInSubFrames: false 19 | } 20 | }); 21 | var view = new BrowserView({ 22 | webPreferences: { 23 | nodeIntegration: false 24 | } 25 | }); 26 | ` 27 | } 28 | ], 29 | invalid: [ 30 | { 31 | code: ` 32 | var mainWindow = new BrowserWindow({ 33 | webPreferences: { 34 | nodeIntegration: true, 35 | nodeIntegrationInWorker: true, 36 | nodeIntegrationInSubFrames: true 37 | } 38 | }); 39 | `, 40 | errors: [ 41 | { messageId: "default", line: 4 }, 42 | { messageId: "default", line: 5 }, 43 | { messageId: "default", line: 6 } 44 | ] 45 | }, 46 | { 47 | code: ` 48 | var view = new BrowserView({ 49 | webPreferences: { 50 | nodeIntegration: true, 51 | nodeIntegrationInWorker: true, 52 | nodeIntegrationInSubFrames: true 53 | } 54 | }); 55 | `, 56 | errors: [ 57 | { messageId: "default", line: 4 }, 58 | { messageId: "default", line: 5 }, 59 | { messageId: "default", line: 6 } 60 | ] 61 | } 62 | ] 63 | }); 64 | -------------------------------------------------------------------------------- /tests/lib/rules/no-html-method.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | const testUtils = require("../test-utils"); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | "test.html = 'test'", 14 | "test.html()", 15 | "test.html('','')", 16 | "element.html('');", 17 | "element.html(null);" 18 | ], 19 | invalid: [ 20 | { 21 | code: "$('p').html('XSS')", 22 | errors: [{ messageId: "default", line: 1 }] 23 | }, 24 | { 25 | code: "$(selector).html(sample_function())", 26 | errors: [{ messageId: "default", line: 1 }] 27 | }, 28 | { 29 | languageOptions: testUtils.es6LanguageOptions, 30 | code: ` 31 | import $ from "jquery"; 32 | test.html('XSS'); 33 | `, 34 | errors: [{ messageId: "default", line: 3 }] 35 | } 36 | ] 37 | }); 38 | -------------------------------------------------------------------------------- /tests/lib/rules/no-inner-html.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | const testUtils = require("../test-utils"); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | "var test = element.innerHTML", 14 | "var test = element.outerHTML", 15 | "document.body.innerHTML = ''", 16 | "document.test", 17 | "element.insertAdjacentHTML()", 18 | { 19 | languageOptions: testUtils.tsLanguageOptions, 20 | code: ` 21 | class Test { 22 | innerHTML: string; 23 | outerHTML: string; 24 | constructor(test: string) { 25 | this.innerHTML = test; 26 | this.outerHTML = test; 27 | } 28 | }; 29 | let test = new Test("test"); 30 | test.innerHTML = test; 31 | test.outerHTML = test; 32 | ` 33 | } 34 | ], 35 | invalid: [ 36 | // TypeScript with full type information 37 | { 38 | languageOptions: testUtils.tsLanguageOptions, 39 | code: ` 40 | var element = document.getElementById(id); 41 | element.innerHTML = 'test'; 42 | element.outerHTML = 'test'; 43 | element.insertAdjacentHTML('beforebegin', 'foo'); 44 | `, 45 | errors: [ 46 | { messageId: "noInnerHtml", line: 3 }, 47 | { messageId: "noInnerHtml", line: 4 }, 48 | { messageId: "noInsertAdjacentHTML", line: 5 } 49 | ] 50 | }, 51 | { 52 | code: ` 53 | element.innerHTML = 'test'; 54 | parent.child.innerHTML += 'test'; 55 | `, 56 | errors: [ 57 | { messageId: "noInnerHtml", line: 2 }, 58 | { messageId: "noInnerHtml", line: 3 } 59 | ] 60 | }, 61 | { 62 | code: ` 63 | element.outerHTML = 'test'; 64 | parent.child.outerHTML += 'test'; 65 | `, 66 | errors: [ 67 | { messageId: "noInnerHtml", line: 2 }, 68 | { messageId: "noInnerHtml", line: 3 } 69 | ] 70 | }, 71 | { 72 | code: "element.insertAdjacentHTML('beforebegin', 'foo')", 73 | errors: [{ messageId: "noInsertAdjacentHTML", line: 1 }] 74 | } 75 | ] 76 | }); 77 | -------------------------------------------------------------------------------- /tests/lib/rules/no-insecure-random.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | const testUtils = require("../test-utils"); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | "Math.Random;", 14 | "Math.random;", 15 | "math.random();", 16 | "random();", 17 | { 18 | code: ` 19 | Math.Random; 20 | Math.random; 21 | math.random(); 22 | random(); 23 | ` 24 | }, 25 | { 26 | code: ` 27 | require('./node_modules/not-unsafe-random'); 28 | require('eslint'); 29 | require('test'); 30 | require('random-package'); 31 | require('random-float2'); 32 | require('random2-seed'); 33 | ` 34 | }, 35 | { 36 | languageOptions: testUtils.es6LanguageOptions, 37 | code: ` 38 | import './node_modules/untest'; 39 | import 'random'; 40 | import 'random-3'; 41 | import 'eslint'; 42 | import 'eslint-plugin-sdl'; 43 | import 'testing'; 44 | ` 45 | }, 46 | { 47 | code: ` 48 | cryptos.pseudoRandomBytes(); 49 | pseudoRandomBytes(); 50 | pseudoRandomByte(); 51 | cryptos.pseudoRondomBytes(); 52 | ` 53 | }, 54 | { 55 | languageOptions: testUtils.tsLanguageOptions, 56 | code: ` 57 | function random(){} 58 | 59 | random(); 60 | 61 | Math.Random; 62 | Math.random; 63 | ` 64 | }, 65 | { 66 | languageOptions: testUtils.tsLanguageOptions, 67 | code: ` 68 | function pseudoRandomBytes(){} 69 | function pseudoRandomByte(){} 70 | 71 | pseudoRandomBytes(); 72 | pseudoRandomByte(); 73 | cryptos.pseudoRondomBytes(); 74 | cryptos.pseudoRondomBytes(); 75 | ` 76 | } 77 | ], 78 | invalid: [ 79 | { 80 | code: ` 81 | Math.random(); 82 | crypto.pseudoRandomBytes(); 83 | `, 84 | errors: [ 85 | { messageId: "default", line: 2 }, 86 | { messageId: "default", line: 3 } 87 | ] 88 | }, 89 | { 90 | languageOptions: testUtils.tsLanguageOptions, 91 | code: ` 92 | Math.random(); 93 | this.Math.random(); 94 | `, 95 | errors: [ 96 | { messageId: "default", line: 2 }, 97 | { messageId: "default", line: 3 } 98 | ] 99 | }, 100 | { 101 | languageOptions: testUtils.tsLanguageOptions, 102 | code: ` 103 | function notMath() : Math{ 104 | return Math; 105 | } 106 | 107 | notMath().random(); 108 | `, 109 | errors: [{ messageId: "default", line: 6 }] 110 | }, 111 | { 112 | languageOptions: testUtils.tsLanguageOptions, 113 | code: ` 114 | crypto.pseudoRandomBytes(); 115 | `, 116 | errors: [{ messageId: "default", line: 2 }] 117 | }, 118 | { 119 | languageOptions: testUtils.tsLanguageOptions, 120 | code: ` 121 | function notCrypto() : Crypto{ 122 | return crypto; 123 | } 124 | 125 | notCrypto().pseudoRandomBytes(); 126 | `, 127 | errors: [{ messageId: "default", line: 6 }] 128 | }, 129 | { 130 | languageOptions: testUtils.es6LanguageOptions, 131 | code: ` 132 | import './node_modules/unique-random'; 133 | import 'chance'; 134 | import 'random-number'; 135 | import 'random-int'; 136 | import 'random-float'; 137 | import 'random-seed'; 138 | `, 139 | errors: [ 140 | { messageId: "default", line: 2 }, 141 | { messageId: "default", line: 3 }, 142 | { messageId: "default", line: 4 }, 143 | { messageId: "default", line: 5 }, 144 | { messageId: "default", line: 6 }, 145 | { messageId: "default", line: 7 } 146 | ] 147 | }, 148 | { 149 | languageOptions: testUtils.es6LanguageOptions, 150 | code: ` 151 | import * as chance1 from 'chance'; 152 | import defaultExport from 'chance'; 153 | import { chance } from 'chance'; 154 | import { chance as chance2 } from 'chance'; 155 | import { chance3, chance4 } from 'chance'; 156 | `, 157 | errors: [ 158 | { messageId: "default", line: 2 }, 159 | { messageId: "default", line: 3 }, 160 | { messageId: "default", line: 4 }, 161 | { messageId: "default", line: 5 }, 162 | { messageId: "default", line: 6 } 163 | ] 164 | }, 165 | { 166 | code: ` 167 | require('./node_modules/unique-random'); 168 | require('**/chance.js'); 169 | require('random-number'); 170 | require('random-int'); 171 | require('random-float'); 172 | require('random-seed'); 173 | `, 174 | errors: [ 175 | { messageId: "default", line: 2 }, 176 | { messageId: "default", line: 3 }, 177 | { messageId: "default", line: 4 }, 178 | { messageId: "default", line: 5 }, 179 | { messageId: "default", line: 6 }, 180 | { messageId: "default", line: 7 } 181 | ] 182 | } 183 | ] 184 | }); 185 | -------------------------------------------------------------------------------- /tests/lib/rules/no-insecure-url.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | const testUtils = require("../test-utils"); 9 | 10 | /** 11 | * Notes: 12 | * - ES2015/ES6 introduced template literals (``). This is considered in parserOptions for relevant tests. 13 | */ 14 | 15 | let ruleTester = new RuleTester(); 16 | 17 | ruleTester.run(ruleId, rule, { 18 | valid: [ 19 | { 20 | // should allow https,ftps strings in variables 21 | code: ` 22 | var x = 'https://www.example.com' 23 | var y = 'ftps://www.example.com' 24 | ` 25 | }, 26 | { 27 | // should allow https,ftps template strings in variables 28 | code: ` 29 | var x = \`https://www.template-examples.com\` 30 | var y = \`ftps://www.template-file-examples.com\` 31 | `, 32 | languageOptions: testUtils.es6LanguageOptions 33 | }, 34 | { 35 | // should allow https,ftps multipart template strings in variables 36 | code: ` 37 | var x = \`https://www.\${multipartExample}.com\` 38 | var y = \`ftps://www.\${multipartExample}.com\` 39 | `, 40 | languageOptions: testUtils.es6LanguageOptions 41 | }, 42 | { 43 | // should allow http,ftp in middle of string 44 | code: "var x = 'The protocol may be http://, https://, ftp:// or ftps://'" 45 | }, 46 | { 47 | // should allow https,ftps strings in default values 48 | code: ` 49 | function f(x : string = 'https://www.example.com') {} 50 | function f(y : string = 'ftps://www.example.com') {} 51 | `, 52 | languageOptions: testUtils.tsLanguageOptions 53 | }, 54 | { 55 | // should allow user-provided exceptions matches, regardless of upper/lower-case 56 | code: ` 57 | var a1 = 'http://www.allow-example.com' 58 | var a2 = 'HtTp://www.allow-example.com/path' 59 | var b1 = 'FTP://www.allow-file-example.com' 60 | var c1 = 'LDaP://www.allow-ldap-example.com' 61 | `, 62 | options: [ 63 | { 64 | exceptions: [ 65 | "HTTP://www.allow-example.com/?.*", 66 | "FtP://www.allow-file-example.com", 67 | "LdaP://www.allow-ldap-example.com" 68 | ] 69 | } 70 | ] 71 | }, 72 | { 73 | // should allow user-provided exceptions for variable name matches, regardless of upper/lower-case 74 | code: ` 75 | var insecureURL = 'http://www.allow-example.com' 76 | var InSeCuReURL = 'ftp://www.allow-example.com/path' 77 | `, 78 | options: [ 79 | { 80 | varExceptions: ["insecure?.*"] 81 | } 82 | ] 83 | }, 84 | { 85 | // should allow xml namespaces, as they are not accessed by the browser 86 | code: ` 87 | const someSvg: React.FC = () => { 88 | return ( 89 | 90 | 91 | ); 92 | }; 93 | `, 94 | languageOptions: testUtils.tsReactLanguageOptions 95 | }, 96 | { 97 | // should allow localhost 98 | code: ` 99 | var x = "http://localhost/test"; 100 | var y = "http://localhost"; 101 | ` 102 | }, 103 | { 104 | // should allow xml namespaces for XHTML and SVG even if outside of jsx xmlns attribute 105 | code: ` 106 | var x = "http://www.w3.org/1999/xhtml"; 107 | var y = "http://www.w3.org/2000/svg"; 108 | ` 109 | } 110 | ], 111 | invalid: [ 112 | { 113 | // should ban http,ftp strings in variables 114 | code: ` 115 | var x1 = 'http://www.examples.com' 116 | var x2 = 'HTTP://www.examples.com' 117 | var y1 = 'ftp://www.file-examples.com' 118 | var y2 = 'FTP://www.file-examples.com' 119 | `, 120 | output: ` 121 | var x1 = "https://www.examples.com" 122 | var x2 = "https://www.examples.com" 123 | var y1 = 'ftp://www.file-examples.com' 124 | var y2 = 'FTP://www.file-examples.com' 125 | `, 126 | errors: [ 127 | { messageId: "doNotUseInsecureUrl", line: 2 }, 128 | { messageId: "doNotUseInsecureUrl", line: 3 }, 129 | { messageId: "doNotUseInsecureUrl", line: 4 }, 130 | { messageId: "doNotUseInsecureUrl", line: 5 } 131 | ] 132 | }, 133 | { 134 | // should ban http,ftp template strings in variables 135 | code: ` 136 | var x1 = \`http://www.template-examples.com\` 137 | var x2 = \`HTTP://www.template-examples.com\` 138 | var y1 = \`ftp://www.file-examples.com\` 139 | var y2 = \`FTP://www.file-examples.com\` 140 | `, 141 | output: ` 142 | var x1 = \`https://www.template-examples.com\` 143 | var x2 = \`https://www.template-examples.com\` 144 | var y1 = \`ftp://www.file-examples.com\` 145 | var y2 = \`FTP://www.file-examples.com\` 146 | `, 147 | errors: [ 148 | { messageId: "doNotUseInsecureUrl", line: 2 }, 149 | { messageId: "doNotUseInsecureUrl", line: 3 }, 150 | { messageId: "doNotUseInsecureUrl", line: 4 }, 151 | { messageId: "doNotUseInsecureUrl", line: 5 } 152 | ], 153 | languageOptions: testUtils.es6LanguageOptions 154 | }, 155 | { 156 | // should ban http,ftp multipart template strings in variables 157 | code: ` 158 | var x1 = \`http://www.\${multipartExample}.com\`; 159 | var y1 = \`ftp://www.\${multipartExample}.com\`; 160 | `, 161 | output: ` 162 | var x1 = \`https://www.\${multipartExample}.com\`; 163 | var y1 = \`ftp://www.\${multipartExample}.com\`; 164 | `, 165 | errors: [ 166 | { messageId: "doNotUseInsecureUrl", line: 2 }, 167 | { messageId: "doNotUseInsecureUrl", line: 3 } 168 | ], 169 | languageOptions: testUtils.es6LanguageOptions 170 | }, 171 | { 172 | // should ban http,ftp strings in default values 173 | code: ` 174 | function f(x : string = 'http://www.example.com') {} 175 | function f(y : string = 'ftp://www.example.com') {} 176 | `, 177 | output: ` 178 | function f(x : string = "https://www.example.com") {} 179 | function f(y : string = 'ftp://www.example.com') {} 180 | `, 181 | errors: [ 182 | { messageId: "doNotUseInsecureUrl", line: 2 }, 183 | { messageId: "doNotUseInsecureUrl", line: 3 } 184 | ], 185 | languageOptions: testUtils.tsLanguageOptions 186 | }, 187 | { 188 | // should ban user-provided blacklist matches, regardless of upper/lower-case 189 | code: ` 190 | var a1 = 'http://www.ban-example.com' 191 | var a2 = 'HTTP://www.ban-example.com/path' 192 | var b1 = 'FtP://www.ban-file-example.com' 193 | var c1 = 'LDAp://www.ban-ldap-example.com' 194 | `, 195 | output: ` 196 | var a1 = "https://www.ban-example.com" 197 | var a2 = "https://www.ban-example.com/path" 198 | var b1 = 'FtP://www.ban-file-example.com' 199 | var c1 = 'LDAp://www.ban-ldap-example.com' 200 | `, 201 | errors: [ 202 | { messageId: "doNotUseInsecureUrl", line: 2 }, 203 | { messageId: "doNotUseInsecureUrl", line: 3 }, 204 | { messageId: "doNotUseInsecureUrl", line: 4 }, 205 | { messageId: "doNotUseInsecureUrl", line: 5 } 206 | ], 207 | options: [ 208 | { 209 | blocklist: [ 210 | "htTp://www.ban-example.com/?.*", 211 | "fTp://www.ban-file-example.com/?.*", 212 | "lDAp://www.ban-ldap-example.com/?.*" 213 | ] 214 | } 215 | ] 216 | }, 217 | { 218 | // should ban any other xml attribute with urls in them 219 | code: ` 220 | const someSvg: React.FC = () => { 221 | return ( 222 | 223 | 224 | ); 225 | }; 226 | `, 227 | output: ` 228 | const someSvg: React.FC = () => { 229 | return ( 230 | 231 | 232 | ); 233 | }; 234 | `, 235 | errors: [{ messageId: "doNotUseInsecureUrl", line: 4 }], 236 | languageOptions: testUtils.tsReactLanguageOptions 237 | }, 238 | { 239 | // should escape the url string correctly 240 | code: `var a1 = "http://moz\ti\tlla.org";`, 241 | output: `var a1 = "https://moz\\ti\\tlla.org";`, 242 | errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }] 243 | }, 244 | { 245 | // should fix url in `` correctly 246 | code: "var x1 = `http://foo${multipartExample} http://${multipartExample}.com`;", 247 | output: "var x1 = `https://foo${multipartExample} http://${multipartExample}.com`;", 248 | errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }], 249 | 250 | languageOptions: testUtils.es6LanguageOptions 251 | }, 252 | { 253 | // should escape the string and fix it properly in `` 254 | code: `var a1 = \`http://moz\ti\tlla.org\`;`, 255 | output: `var a1 = \`https://moz\\ti\\tlla.org\`;`, 256 | errors: [{ messageId: "doNotUseInsecureUrl", line: 1 }], 257 | 258 | languageOptions: testUtils.es6LanguageOptions 259 | } 260 | ] 261 | }); 262 | -------------------------------------------------------------------------------- /tests/lib/rules/no-msapp-exec-unsafe.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | const testUtils = require("../test-utils"); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: ["test.execUnsafeLocalFunction = 'test'", "MSApp.execUnsafeLocalFunction()"], 13 | invalid: [ 14 | { 15 | code: "MSApp.execUnsafeLocalFunction(testfunc)", 16 | errors: [{ messageId: "default", line: 1, type: "CallExpression" }] 17 | } 18 | ] 19 | }); 20 | -------------------------------------------------------------------------------- /tests/lib/rules/no-postmessage-star-origin.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const testUtils = require("../test-utils"); 6 | const ruleId = path.parse(__filename).name; 7 | const rule = require(path.join("../../../lib/rules/", ruleId)); 8 | const RuleTester = require("eslint").RuleTester; 9 | var ruleTester = new RuleTester(); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: [ 13 | "window.postMessage()", 14 | "window.postMessage = ''", 15 | "window.postMessage(1)", 16 | "window.postMessage(1, 2, 3, 4)", 17 | "window.postMessage('data', 'https://target.domain')", 18 | "window.postMessage('data', 'https://target.domain', 'menubar=yes')", 19 | { 20 | languageOptions: testUtils.tsLanguageOptions, 21 | code: ` 22 | class WindowLike { 23 | postMessage(): void { 24 | }; 25 | } 26 | function main() { 27 | var w: WindowLike = new WindowLike(); 28 | w.postMessage('test', '*'); 29 | } 30 | ` 31 | } 32 | ], 33 | invalid: [ 34 | { 35 | code: ` 36 | any.postMessage(message, "*"); 37 | any.postMessage(message, "*", "menubar=yes"); 38 | `, 39 | errors: [ 40 | { messageId: "default", line: 2 }, 41 | { messageId: "default", line: 3 } 42 | ] 43 | }, 44 | { 45 | languageOptions: testUtils.tsLanguageOptions, 46 | code: ` 47 | window.frames[0].postMessage(message, "*"); 48 | var w1 = window.open(url); 49 | w1.postMessage(message, "*"); 50 | `, 51 | errors: [ 52 | { messageId: "default", line: 2 }, 53 | { messageId: "default", line: 4 } 54 | ] 55 | } 56 | ] 57 | }); 58 | -------------------------------------------------------------------------------- /tests/lib/rules/no-unsafe-alloc.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | "use strict"; 5 | const path = require("path"); 6 | const ruleId = path.parse(__filename).name; 7 | const rule = require(path.join("../../../lib/rules/", ruleId)); 8 | const RuleTester = require("eslint").RuleTester; 9 | var ruleTester = new RuleTester(); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: ["foo.allocUnsafe", "Buffer.allocUnsafe(0)", "Buffer.allocUnsafeSlow(0)"], 13 | invalid: [ 14 | { 15 | code: ` 16 | var buf1 = Buffer.allocUnsafe(10); 17 | var buf2 = Buffer.allocUnsafeSlow(10) 18 | `, 19 | errors: [ 20 | { messageId: "default", line: 2 }, 21 | { messageId: "default", line: 3 } 22 | ] 23 | } 24 | ] 25 | }); 26 | -------------------------------------------------------------------------------- /tests/lib/rules/no-winjs-html-unsafe.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | const path = require("path"); 5 | const ruleId = path.parse(__filename).name; 6 | const rule = require(path.join("../../../lib/rules/", ruleId)); 7 | const RuleTester = require("eslint").RuleTester; 8 | var ruleTester = new RuleTester(); 9 | const testUtils = require("../test-utils"); 10 | 11 | ruleTester.run(ruleId, rule, { 12 | valid: ['element.insertAdjacentHTMLUnsafe = "test";'], 13 | invalid: [ 14 | { 15 | code: ` 16 | WinJS.Utilities.insertAdjacentHTMLUnsafe(element, position, text); 17 | WinJS.Utilities.setInnerHTMLUnsafe(element, text); 18 | WinJS.Utilities.setOuterHTMLUnsafe(element, text); 19 | `, 20 | errors: [ 21 | { messageId: "default", line: 2, type: "CallExpression" }, 22 | { messageId: "default", line: 3, type: "CallExpression" }, 23 | { messageId: "default", line: 4, type: "CallExpression" } 24 | ] 25 | } 26 | ] 27 | }); 28 | -------------------------------------------------------------------------------- /tests/lib/test-utils.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | /** 5 | * @fileoverview Common utils for tests. 6 | */ 7 | 8 | "use strict"; 9 | 10 | const path = require("path"); 11 | const tsParser = require("@typescript-eslint/parser"); 12 | 13 | module.exports = { 14 | es6LanguageOptions: { 15 | parserOptions: { 16 | ecmaVersion: 6, 17 | sourceType: "module" 18 | } 19 | }, 20 | tsLanguageOptions: { 21 | parser: tsParser, 22 | parserOptions: { 23 | tsconfigRootDir: path.join(__dirname, "..", "fixtures", "ts"), 24 | projectService: true 25 | } 26 | }, 27 | tsReactLanguageOptions: { 28 | parser: tsParser, 29 | parserOptions: { 30 | tsconfigRootDir: path.join(__dirname, "..", "fixtures", "tsx"), 31 | projectService: true, 32 | ecmaFeatures: { 33 | jsx: true 34 | } 35 | } 36 | } 37 | }; 38 | --------------------------------------------------------------------------------