This commit is contained in:
A-Katopodis 2020-11-07 22:08:00 +02:00
Родитель ddfcd808c5
Коммит a1d06caa4e
75 изменённых файлов: 4612 добавлений и 0 удалений

79
.github/workflows/E2E integration.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,79 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
name: E2E integration
on:
push:
branches: [main]
pull_request_target:
branches: [main, release/vNext]
env:
PROJECT: discordjs # The name of the project you want to clone. It must be on github
REPOSITORY: discord.js # The repository name
FOLDER_TO_SCAN: src # The folder under which the source code you have is contained. Relative to the repository
TS_CONFIG_PATH: tsconfig.json # The tsconfig.json path relative to the repository
jobs:
build:
name: E2E run with sarif
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, windows-2019]
steps:
- name: Setup Node.js environment
uses: actions/setup-node@v1.4.3
- uses: actions/checkout@v2
with:
repository: ${{env.PROJECT}}/${{env.REPOSITORY}}
path: ${{env.PROJECT}}
clean: true
- uses: actions/checkout@v2
with:
path: eslint-plugin-sdl
clean: true
# store them inside a seperate folder for performance in case of multiple projects.
# Trying to install eslint on a project with an existing package.json will install all of the package.json packages as well
- name: Create eslint directory
run: |
mkdir eslint
continue-on-error: true
- name: Install eslint for discord library and eslint-plugin-dependencies
run: |
npm i eslint
npm i eslint-plugin-security
npm i eslint-plugin-react
npm i eslint-plugin-node
npm i typescript
npm i @microsoft/eslint-formatter-sarif
working-directory: eslint
- name: Link the eslint-plugin-sdl
run: npm link ../eslint-plugin-sdl
working-directory: eslint
- name: Run eslint
run: npx eslint
-c node_modules/@microsoft/eslint-plugin-sdl/config/recommended.js
../${{env.PROJECT}}/${{env.FOLDER_TO_SCAN}}/
--ext .js
--parser-options=project:../${{env.PROJECT}}/${{env.TS_CONFIG_PATH}}
--no-eslintrc
-f @microsoft/eslint-formatter-sarif
-o ../${{env.PROJECT}}/eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif
working-directory: eslint
continue-on-error: true
- name: Upload eslint results as artifact
uses: actions/upload-artifact@v2
with:
name: eslint-result
path: ${{env.PROJECT}}/eslint-result-${{ matrix.os }}-${{github.run_id}}.sarif
if-no-files-found: error

30
.github/workflows/node-version-integration.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
name: Node CI
on:
push:
branches: [main]
pull_request_target:
branches: [main, release/vNext]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, windows-2019, macos-10.15]
node-version: [12.x, 14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test

34
.github/workflows/publish.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
name: Publish Package
on:
release:
types: [created]
jobs:
# Run one last check
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm ci
- run: npm test
publish-npm:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

4
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
node_modules/
.vscode/

4
.npmignore Normal file
Просмотреть файл

@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
tests/
.github/

9
CODE_OF_CONDUCT.md Normal file
Просмотреть файл

@ -0,0 +1,9 @@
# Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
Resources:
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns

66
README.md Normal file
Просмотреть файл

@ -0,0 +1,66 @@
# eslint-plugin-sdl
[ESLint Plugin](https://eslint.org/docs/developer-guide/working-with-plugins) focused on common security issues and misconfigurations.
Plugin is intended as a baseline for projects that follow [Microsoft Security Development Lifecycle (SDL)](https://www.microsoft.com/en-us/securityengineering/sdl) and use ESLint to perform [Static Analysis Security Testing (SAST)](https://www.microsoft.com/en-us/securityengineering/sdl/practices#practice9).
## Configs
Plugin is shipped with following [Shareable Configs](http://eslint.org/docs/developer-guide/shareable-configs):
- [angular](config/angular.js) - Set of rules for [Angular](https://angular.io) applications
- [angularjs](config/angularjs.js) - Set of rules for [AngularJS](https://docs.angularjs.org) applications
- [common](config/common.js) - Set of rules for common JavaScript applications
- [electron](config/electron.js) - Set of rules for Electron applications
- [node](config/node.js) - Set of rules for Node applications
- [react](config/react.js) - Set of rules for [ReactJS](https://reactjs.org) applications
- [**recommended**](config/recommended.js) - SDL Recommended rules for all applications
- [**required**](config/required.js) - SDL Required rules for all applications
- [typescript](config/typescript.js) - Set of rules for TypeScript applications
## Rules
Where possible, we leverage existing rules from [ESLint](https://eslint.org/docs/rules/) and community plugins such as [react](https://github.com/yannickcr/eslint-plugin-react), [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules) or [security](https://github.com/nodesecurity/eslint-plugin-security#rules).
We also implemented several [custom rules](./lib/rules) where we did not find sufficient alternative in the community.
| Name | Description |
| --- | --- |
| [no-caller](https://eslint.org/docs/rules/no-caller) | Bans usage of deprecated functions `arguments.caller()` and `arguments.callee` that could potentially allow access to call stack. |
| [no-delete-var](https://eslint.org/docs/rules/no-delete-var) | Bans usage of operator `delete` on variables as it can lead to unexpected behavior. |
| [no-eval](https://eslint.org/docs/rules/no-eval) | Bans usage of [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) that allows code execution from string argument. |
| [no-implied-eval](https://eslint.org/docs/rules/no-implied-eval) | Bans usage of `setTimeout()`, `setInterval()` and `execScript()`. These functions are similar to `eval()` and prone to code execution. |
| [no-new-func](https://eslint.org/docs/rules/no-new-func) | Bans calling `new Function()` as it's similar to `eval()` and prone to code execution. |
| [node/no-deprecated-api](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/no-deprecated-api.md) | Bans usage of deprecated APIs in Node. |
| [@microsoft/sdl/no-angular-bypass-sanitizer](./docs/rules/no-angular-bypass-sanitizer.md) | Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass [DomSanitizer](https://angular.io/api/platform-browser/DomSanitizer#security-risk) in Angular and need to be reviewed. |
| [@microsoft/sdl/no-angularjs-bypass-sce](./docs/rules/no-angularjs-bypass-sce.md) | Calls to `$sceProvider.enabled(false)`, `$sceDelegate.trustAs()`, `$sce.trustAs()` and relevant shorthand methods (e.g. `trustAsHtml` or `trustAsJs`) bypass [Strict Contextual Escaping (SCE)](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) in AngularJS and need to be reviewed. |
| [@microsoft/sdl/no-angularjs-enable-svg](./docs/rules/no-angularjs-enable-svg.md) | Calls to [`$sanitizeProvider.enableSvg(true)`](https://docs.angularjs.org/api/ngSanitize/provider/$sanitizeProvider#enableSvg) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed. |
| [@microsoft/sdl/no-angularjs-sanitization-whitelist](./docs/rules/no-angularjs-sanitization-whitelist.md) | Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed. |
| [@microsoft/sdl/no-cookies](./docs/rules/no-cookies.md) | HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other modern methods instead. |
| [@microsoft/sdl/no-document-domain](./docs/rules/no-document-domain.md) | Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited. |
| [@microsoft/sdl/no-document-write](./docs/rules/no-document-write.md) | Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. |
| [@microsoft/sdl/no-electron-node-integration](./docs/rules/no-electron-node-integration.md) | [Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks. |
| [@microsoft/sdl/no-html-method](./docs/rules/no-html-method.md) | Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead. |
| [@microsoft/sdl/no-inner-html](./docs/rules/no-inner-html.md) | Assignments to innerHTML or outerHTML properties manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead. |
| [@microsoft/sdl/no-insecure-url](./docs/rules/no-insecure-url.md) | Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending potentially sensitive data over untrusted networks in plaintext. |
| [@microsoft/sdl/no-msapp-exec-unsafe](./docs/rules/no-msapp-exec-unsafe.md) | Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided. |
| [@microsoft/sdl/no-postmessage-star-origin](./docs/rules/no-postmessage-star-origin.md) | Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary. |
| [@microsoft/sdl/no-unsafe-alloc](./docs/rules/no-unsafe-alloc.md) | When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data. |
| [@microsoft/sdl/no-winjs-html-unsafe](./docs/rules/no-winjs-html-unsafe.md) | Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)) and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead. |
| [@microsoft/sdl/react-iframe-missing-sandbox](./docs/rules/react-iframe-missing-sandbox.md) | The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified. |
| [react/no-danger](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md) | Bans usage of `dangerouslySetInnerHTML` property in React as it allows passing unsanitized HTML in DOM. |
| [@typescript-eslint/no-implied-eval](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-implied-eval.md) | Similar to built-in ESLint rule `no-implied-eval`. Bans usage of `setTimeout()`, `setInterval()`, `setImmediate()`, `execScript()` or `new Function()` as they are similar to `eval()` and allow code execution from string arguments. |
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

41
SECURITY.md Normal file
Просмотреть файл

@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## Security
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/).
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.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
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).
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).
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:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
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.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

17
config/angular.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for Angular apps.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl"
],
rules: {
"@microsoft/sdl/no-angular-bypass-sanitizer": "error"
}
}

19
config/angularjs.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for AngularJS apps.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl"
],
rules: {
"@microsoft/sdl/no-angularjs-enable-svg": "error",
"@microsoft/sdl/no-angularjs-sanitization-whitelist": "error",
"@microsoft/sdl/no-angularjs-bypass-sce": "error"
}
}

30
config/common.js Normal file
Просмотреть файл

@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for common JavaScript apps.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl"
],
rules: {
"no-caller": "error",
"no-delete-var": "error",
"no-eval": "error",
"no-implied-eval": "error",
"no-new-func": "error",
"@microsoft/sdl/no-cookies": "error",
"@microsoft/sdl/no-document-domain": "error",
"@microsoft/sdl/no-document-write": "error",
"@microsoft/sdl/no-html-method": "error",
"@microsoft/sdl/no-inner-html": "error",
"@microsoft/sdl/no-insecure-url": "error",
"@microsoft/sdl/no-msapp-exec-unsafe": "error",
"@microsoft/sdl/no-postmessage-star-origin": "error",
"@microsoft/sdl/no-winjs-html-unsafe": "error"
}
}

17
config/electron.js Normal file
Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for Electron apps.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl"
],
rules: {
"@microsoft/sdl/no-electron-node-integration": "error"
}
}

19
config/node.js Normal file
Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for Node apps.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl",
"node"
],
rules: {
"@microsoft/sdl/no-unsafe-alloc": "error",
"node/no-deprecated-api": "error"
}
}

19
config/react.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for React apps.
*/
"use strict";
module.exports = {
plugins: [
"react",
"@microsoft/sdl"
],
rules: {
"react/no-danger": "error",
"@microsoft/sdl/react-iframe-missing-sandbox": "error"
}
}

26
config/recommended.js Normal file
Просмотреть файл

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Default SDL recommended config for all applications.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl",
"security"
],
extends: [
"plugin:@microsoft/sdl/required",
"plugin:@microsoft/sdl/typescript"
],
// TODO:
// - Consider using SDL Recommended for identifying places in code that are often misused and can be potentially risky.
// - The action should be to review the code, not to remove it.
// - Good lists of such APIs
// - https://github.com/mozfreddyb/eslint-plugin-scanjs-rules/tree/master/lib/rules
// - https://github.com/ajinabraham/njsscan/tree/master/njsscan/rules/semantic_grep
// - Eventually we might remove detect-* rules from security plugin as they have high FP-rate.
}

22
config/required.js Normal file
Просмотреть файл

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Default SDL required config for all applications.
*/
"use strict";
module.exports = {
plugins: [
"@microsoft/sdl"
],
extends: [
"plugin:@microsoft/sdl/angular",
"plugin:@microsoft/sdl/angularjs",
"plugin:@microsoft/sdl/common",
"plugin:@microsoft/sdl/electron",
"plugin:@microsoft/sdl/node",
"plugin:@microsoft/sdl/react"
]
}

36
config/typescript.js Normal file
Просмотреть файл

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* Shareable config for TypeScript applications.
*/
"use strict";
module.exports = {
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 6,
sourceType: "module",
ecmaFeatures: {
jsx: true
}
},
plugins: [
"@typescript-eslint"
],
overrides: [
{
files: [
"**/*.{ts,tsx}"
],
rules: {
"@typescript-eslint/no-implied-eval": "error",
// @typescript-eslint/no-implied-eval offers more accurate results for typescript.
// thus we turn the more generic rule off for ts and tsx files.
// This also avoids duplicate hits.
"no-implied-eval": "off"
}
}
]
}

Просмотреть файл

@ -0,0 +1,7 @@
# Do not bypass Angular's built-in sanitization (no-angular-bypass-sanitizer)
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.
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.
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.

Просмотреть файл

@ -0,0 +1,7 @@
# Do not bypass Strict Contextual Escaping (SCE) in AngularJS (no-angularjs-bypass-sce)
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.
SCE should be bypassed only in very rare and justifiable cases after careful review so that the risk of introducing Cross-Site-Scripting (XSS) vulnerability is minimized.
See [official documentation](https://docs.angularjs.org/api/ng/service/$sce#strict-contextual-escaping) for more details.

Просмотреть файл

@ -0,0 +1,7 @@
# Do not enable SVG support in AngularJS (no-angularjs-enable-svg)
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.
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.
See [official documentation](https://docs.angularjs.org/api/ngSanitize/provider/$sanitizeProvider#enableSvg) for more details about the issue.

Просмотреть файл

@ -0,0 +1,7 @@
# Do not bypass Angular's built-in sanitization (no-angularjs-sanitization-whitelist)
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.
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.
See [official documentation](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) for more details about the issue.

13
docs/rules/no-cookies.md Normal file
Просмотреть файл

@ -0,0 +1,13 @@
# Do not use HTTP cookies in modern applications (no-cookies)
HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other modern methods instead.
Cookies should be used only in rare and justifiable cases after thorough security review.
## Further Reading
* [Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
## Related Rules
* [tslint-microsoft-contrib/no-cookies](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noCookiesRule.ts)

Просмотреть файл

@ -0,0 +1,7 @@
# Do not write to document.domain property (no-document-domain)
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.
## Related Rules
* [tslint-microsoft-contrib/no-document-domain](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentDomainRule.ts)

Просмотреть файл

@ -0,0 +1,7 @@
# Do not write to DOM directly using document.write or document.writeln methods (no-document-write)
Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.
## Related Rules
* [tslint-microsoft-contrib/no-document-write](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noDocumentWriteRule.ts)

Просмотреть файл

@ -0,0 +1,9 @@
# Do not enable Node.js Integration for Remote Content (no-electron-node-integration)
[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.
[Rule Source Code](../../lib/rules/no-electron-node-integration.js)
## Related Rules
* [codeql/js/enabling-electron-renderer-node-integration](https://help.semmle.com/wiki/display/JS/Enabling+Node.js+integration+for+Electron+web+content+renderers)

Просмотреть файл

@ -0,0 +1,7 @@
# Do not write to DOM directly using jQuery html() method (no-html-method)
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.
## Related Rules
* [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts)

Просмотреть файл

@ -0,0 +1,11 @@
# Do not write to DOM directly using innerHTML/outerHTML property (no-inner-html)
Assignments to [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)/[outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) properties or calls to [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.
* [Rule Source](../../lib/rules/no-inner-html.js)
* [Rule Test](../../tests/lib/rules/no-inner-html.js)
## Related Rules
* [tslint-microsoft-contrib/no-inner-html](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noInnerHtml.ts)
* [eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/master/docs/rules/method.md)

Просмотреть файл

@ -0,0 +1,17 @@
# Do not use insecure random functions
Methods such as Math.random or crypto.pseudoRandomBytes do not produce cryptographically-secure random numbers and must not be used for security purposes such as generating tokens, passwords or keys.
Use crypto.randomBytes() or window.crypto.getRandomValues() instead.
## Related Rules
* [tslint-microsoft-contrib/no-insecure-random](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/insecureRandomRule.ts)
- https://help.semmle.com/wiki/display/JS/Insecure+randomness
- [source](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureRandomnessCustomizations.qll)
- https://vulncat.fortify.com/en/detail?id=desc.semantic.abap.insecure_randomness#JavaScript
- https://rules.sonarsource.com/javascript/RSPEC-2245
- [source](https://github.com/SonarSource/SonarJS/blob/master/eslint-bridge/src/rules/pseudo-random.ts)
- https://github.com/nodesecurity/eslint-plugin-security/blob/master/rules/detect-pseudoRandomBytes.js
- https://github.com/gkouziik/eslint-plugin-security-node/blob/master/lib/rules/detect-insecure-randomness.js

Просмотреть файл

@ -0,0 +1,55 @@
# Do not use insecure URLs (no-insecure-url)
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.
- [Rule Source](../../lib/rules/no-insecure-url.js)
- [Rule Test](../../tests/lib/rules/no-insecure-url.js)
## Options
This rule comes with two [default lists](../../lib/rules/no-insecure-url.js#L13):
- **blacklist** - a RegEx list of insecure URL patterns.
- **exceptions** - a RegEx list of common false positive patterns. For example, HTTP URLs to XML schemas are usually allowed as they are used as identifiers, not for establishing actual network connections.
These lists can be overrided by providing options.
---
For example, providing these options... :
```javascript
"@microsoft/sdl/no-insecure-url": ["error", {
"blacklist": ["^(http|ftp):\\/\\/", "^https:\\/\\/www\\.disallow-example\\.com"],
"exceptions": ["^http:\\/\\/schemas\\.microsoft\\.com\\/\\/?.*"]
}]
```
... overrides the internal blacklist, blocking the following URL patterns... :
- `http://`...
- `ftp://`...
- `https://www.disallow-example.com`
... and also overrides the internal exceptions list, allowing the following URL patterns as exceptions.:
- `http://schemas.microsoft.com`
- `http://schemas.microsoft.com/sharepoint`
- `http://schemas.microsoft.com/path/subpath`
- ...
URLs in neither the blacklist nor the exceptions list, are allowed:
- `telnet://`...
- `ws://`...
- ...
---
**Note**: The RegEx for the lists is provided within a string in a JSON. It is without delimiting slashes `/ /` and thus users cannot pass RegEx parameters. We make it case-insensitive after user input. Do not forget to escape characters:
```javascript
let pureRegex = /^https:\/\/www\.disallow-example\.com/;
let regexInString = "^https:\\/\\/www\\.disallow-example\\.com";
```
## Related Rules
* [tslint-microsoft-contrib/no-http-string](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/noHttpStringRule.ts)
* [CodeQL/InsecureDownloadCustomizations.qll](https://github.com/github/codeql/blob/master/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownloadCustomizations.qll#L62)
* [DevSkim/DS137138](https://github.com/microsoft/DevSkim/blob/main/guidance/DS137138.md)
* [Fortify/insecure_transport](https://vulncat.fortify.com/en/detail?id=desc.config.java.insecure_transport#JavaScript%2fTypeScript)
## Further Reading
* [HTTPS Everywhere](https://en.wikipedia.org/wiki/HTTPS_Everywhere)

Просмотреть файл

@ -0,0 +1,3 @@
# Do not bypass script injection validation (no-msapp-exec-unsafe)
Calls to `MSApp.execUnsafeLocalFunction()` bypass script injection validation and should be avoided.

Просмотреть файл

@ -0,0 +1,3 @@
# Do not use * as target origin when sending data to other windows (no-postmessage-star-origin)
Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary.

Просмотреть файл

@ -0,0 +1,12 @@
# Do not allocate uninitialized buffers in Node.js (no-unsafe-alloc)
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.
These methods should be used only in justifiable cases (e.g. due to performance reasons) after thorough security review.
* [Rule Source](../../lib/rules/no-unsafe-alloc.js)
* [Rule Test](../../tests/lib/rules/no-unsafe-alloc.js)
## Resources
* [Node.js - What makes Buffer.allocUnsafe() and Buffer.allocUnsafeSlow() "unsafe"?](https://nodejs.org/api/buffer.html#buffer_what_makes_buffer_allocunsafe_and_buffer_allocunsafeslow_unsafe)

Просмотреть файл

@ -0,0 +1,3 @@
# Do not set HTML using unsafe methods from WinJS.Utilities (no-winjs-html-unsafe)
Calls to [`setInnerHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)), [`setOuterHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211698(v=win.10)) or [`insertAdjacentHTMLUnsafe`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br229832(v=win.10)) methods from [Windows Library for JavaScript](https://docs.microsoft.com/en-us/previous-versions/windows/apps/mt502392(v=win.10)) do not perform input validation and should be avoided. Use alternate methods such as [`setInnerHTML`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead.

Просмотреть файл

@ -0,0 +1,17 @@
# An iframe element is missing a sandbox attribute (react-iframe-missing-sandbox)
The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified.
Additional functionality such as the ability to run scripts should be enabled only in justifiable cases after thorough security review.
* [Rule Source](../../lib/rules/react-iframe-missing-sandbox.js)
* [Rule Test](../../tests/lib/rules/react-iframe-missing-sandbox.js)
## Related Rules
* [tslint-microsoft-contrib/react-iframe-missing-sandbox](https://github.com/microsoft/tslint-microsoft-contrib/blob/master/src/reactIframeMissingSandboxRule.ts)
## More Reading
* [How to safely inject HTML in React using an iframe](https://medium.com/the-thinkmill/how-to-safely-inject-html-in-react-using-an-iframe-adc775d458bc)
* [Play safely in sandboxed IFrames](https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/)

77
lib/ast-utils.js Normal file
Просмотреть файл

@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Common utils for AST.
*/
"use strict";
module.exports = {
isTypeScriptParserServices(parserServices) {
// Check properties specific to @typescript-eslint/parser
return (
parserServices &&
parserServices.program &&
parserServices.esTreeNodeToTSNodeMap &&
parserServices.tsNodeToESTreeNodeMap
);
},
hasFullTypeInformation(context) {
var hasFullTypeInformation = (
context &&
this.isTypeScriptParserServices(context.parserServices) &&
context.parserServices.hasFullTypeInformation === true
);
return hasFullTypeInformation;
},
getFullTypeChecker(context) {
return this.hasFullTypeInformation(context) ? context.parserServices.program.getTypeChecker() : null;
},
getNodeType(node, context) {
const typeChecker = context.parserServices.program.getTypeChecker();
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node);
const tsType = typeChecker.getTypeAtLocation(tsNode);
return typeChecker.typeToString(tsType);
},
getCallerType(fullTypeChecker, object, context){
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(object);
const tsType = fullTypeChecker.getTypeAtLocation(tsNode);
const type = fullTypeChecker.typeToString(tsType);
return type;
},
isDocumentObject(node, context, fullTypeChecker) {
if (fullTypeChecker) {
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node);
const tsType = fullTypeChecker.getTypeAtLocation(tsNode);
const type = fullTypeChecker.typeToString(tsType);
return (type === "Document");
}
// Best-effort checking without Type information
switch (node.type) {
case "Identifier":
return node != undefined && node.name == "document";
case "MemberExpression":
return (
node != undefined &&
node.property != undefined &&
node.property.name == "document" && (
(node.object != undefined &&
node.object.name == "window") ||
(
node.object != undefined &&
node.object.property != undefined &&
node.object.property.name == "window" &&
(
(node.object.object != undefined && node.object.object.type == "ThisExpression") ||
(node.object.object != undefined && node.object.object.name == "globalThis")
)
)
)
);
};
return false;
}
};

31
lib/index.js Normal file
Просмотреть файл

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview ESLint plugin that implements rules intended for static testing during SDL
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
const path = require("path");
const fs = require('fs');
function readFilesAsMap(relativeDir) {
var absoluteDir = path.resolve(__dirname, relativeDir);
var files = fs.readdirSync(absoluteDir);
var output = {};
files.forEach(filename => {
var file = path.parse(filename);
var obj = require(path.join(absoluteDir, file.base));
output[file.name] = obj;
});
return output;
}
module.exports = {
rules: readFilesAsMap("./rules"),
configs: readFilesAsMap("../config")
}

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow bypassing Angular's built-in sanitizer
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Calls to bypassSecurityTrustHtml, bypassSecurityTrustScript and similar methods bypass DomSanitizer in Angular and need to be reviewed.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angular-bypass-sanitizer.md"
},
messages: {
noBypass: "Do not bypass Angular's built-in sanitizer"
}
},
create: function(context) {
return {
"CallExpression[arguments!=''][callee.property.name=/bypassSecurityTrust(Html|ResourceUrl|Script|Style|Url)/]"(node) {
context.report(
{
node: node,
messageId: "noBypass"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow bypassing Strict Contextual Escaping (SCE) in AngularJS
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Calls to $sceProvider.enabled(false), $sceDelegate.trustAs(), $sce.trustAs() and relevant shorthand methods (e.g. trustAsHtml or trustAsJs) bypass Strict Contextual Escaping (SCE) in AngularJS and need to be reviewed.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-bypass-sce.md"
},
messages: {
doNotBypass: "Do not bypass Strict Contextual Escaping (SCE) in AngularJS"
}
},
create: function (context) {
function reportIt(node) {
context.report({
node: node,
messageId: "doNotBypass"
});
}
return {
"CallExpression[arguments!=''][callee.object.name='$sceProvider'][callee.property.name='enabled']"(node) {
// Known false positives
if (node.arguments == undefined || node.arguments.length != 1 || (node.arguments[0].type == "Literal" && /true|1/.test(node.arguments[0].value))){
return;
}
return reportIt(node)
},
"CallExpression[arguments!=''][callee.object.name='$sceDelegate'][callee.property.name='trustAs']": reportIt,
"CallExpression[arguments!=''][callee.object.name='$sce'][callee.property.name=/trustAs(Css|Html|Js|ResourceUrl|Url)?/]"(node) {
// Known false positives
if (
node.arguments
&& node.arguments.length === 1
&& node.arguments[0].type === "Literal"
&& node.arguments[0].value === ""
) {
return;
}
return reportIt(node);
}
};
}
};
// TODO: Review https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlWhitelist and https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#resourceUrlBlacklist

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow enabling SVG in AngularJS apps
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Calls to $sanitizeProvider.enableSvg(true) increase attack surface of the application by enabling SVG support in AngularJS sanitizer and need to be reviewed.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-enable-svg.md"
},
messages: {
doNotEnableSVG: "Do not enable SVG support in AngularJS"
}
},
create: function (context) {
return {
"CallExpression[callee.object.name='$sanitizeProvider'][callee.property.name='enableSvg']"(node) {
// Known false positives
if (
(node.arguments != undefined &&
node.arguments.length != 1) ||
(
node.arguments[0].type == "Literal" && (
node.arguments[0].value == "false" || node.arguments[0].value == "0"
)
))
{
return;
}
context.report(
{
node: node,
messageId: "doNotEnableSVG"
});
}
};
}
};
// TODO: Add rules for $sanitizeProvider.addValidElements() and $sanitizeProvider.addValidAttrs()

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow modifying sanitization whitelist in AngularJS
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Calls to [`$compileProvider.aHrefSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationWhitelist) or [`$compileProvider.imgSrcSanitizationWhitelist`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationWhitelist) configure whitelists in AngularJS sanitizer and need to be reviewed.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-angularjs-sanitization-whitelist.md"
},
messages: {
noSanitizationWhitelist: "Do not modify sanitization whitelist in AngularJS"
}
},
create: function(context) {
return {
"CallExpression[arguments!=''][callee.object.name='$compileProvider'][callee.property.name=/(aHref|imgSrc)SanitizationWhitelist/]"(node) {
context.report(
{
node: node,
messageId: "noSanitizationWhitelist"
});
}
};
}
};

43
lib/rules/no-cookies.js Normal file
Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow usage of HTTP cookies
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "HTTP cookies are an old client-side storage mechanism with inherent risks and limitations. Use Web Storage, IndexedDB or other more modern methods instead.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-cookies.md"
},
messages: {
doNotUseCookies: "Do not use HTTP cookies in modern applications"
}
},
create: function (context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
"MemberExpression[property.name='cookie']"(node) {
if (astUtils.isDocumentObject(node.object, context, fullTypeChecker)) {
context.report({
node: node,
messageId: "doNotUseCookies"
});
}
}
};
}
};

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow document.domain property
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Writes to [`document.domain`](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain) property must be reviewed to avoid bypass of [same-origin checks](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin). Usage of top level domains such as `azurewebsites.net` is strictly prohibited.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-domain.md"
},
messages: {
default: 'Do not write to document.domain property'
}
},
create: function(context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
"AssignmentExpression[operator='='][left.property.name='domain']"(node) {
if (astUtils.isDocumentObject(node.left.object, context, fullTypeChecker)) {
context.report(
{
node: node,
messageId: "default"
});
}
}
};
}
};

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow document.write or document.writeln method call
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "Calls to document.write or document.writeln manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-document-write.md"
},
messages: {
default: 'Do not write to DOM directly using document.write or document.writeln methods'
}
},
create: function(context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
"CallExpression[arguments.length=1][callee.property.name=/write|writeln/]"(node) {
if (astUtils.isDocumentObject(node.callee.object, context, fullTypeChecker)) {
context.report(
{
node: node,
messageId: "default"
});
}
}
};
}
};

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow enabling Node.js integration in Electron apps
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "[Node.js Integration](https://www.electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content) must not be enabled in any renderer that loads remote content to avoid remote code execution attacks.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-electron-node-integration.md"
},
messages: {
default: "Do not enable Node.js Integration for Remote Content"
}
},
create: function(context) {
return {
"NewExpression[callee.name=/BrowserWindow|BrowserView/] > ObjectExpression.arguments > Property.properties[key.name=webPreferences] > ObjectExpression.value > Property.properties[key.name=/nodeIntegration|nodeIntegrationInWorker|nodeIntegrationInSubFrames/][value.value='true']"(node) {
context.report(
{
node: node,
messageId: "default"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow call to html() method
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs:{
description: "Direct calls to method `html()` often (e.g. in jQuery framework) manipulate DOM without any sanitization and should be avoided. Use document.createElement() or similar methods instead.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-html-method.md"
},
messages: {
default: 'Do not write to DOM directly using jQuery html() method'
}
},
create: function(context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
// TODO:
// - Cover similar methods that can manipulate DOM such as append(string), jQuery(string)
// - Improve rule with type information from TypeScript parser
// - Consider ignoring all Literals?
"CallExpression[arguments.length=1] > MemberExpression.callee[property.name='html']"(node) {
// Known false positives
if (
// element.html("")
node.parent.arguments[0].type === "Literal"
&& (
node.parent.arguments[0].value === ""
|| node.parent.arguments[0].value === null
)
) {
return;
}
context.report(
{
node: node,
messageId: "default"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow assignment to innerHTML or outerHTML properties
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
description: "Assignments to [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)/[outerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML) properties or calls to [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method manipulate DOM directly without any sanitization and should be avoided. Use document.createElement() or similar methods instead.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-inner-html.md"
},
messages: {
noInnerHtml: 'Do not write to DOM directly using innerHTML/outerHTML property',
noInsertAdjacentHTML: 'Do not write to DOM using insertAdjacentHTML method'
}
},
create: function (context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
function getNodeTypeAsString(node) {
if (fullTypeChecker && node) {
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node);
const tsType = fullTypeChecker.getTypeAtLocation(tsNode);
const type = fullTypeChecker.typeToString(tsType);
return type;
}
return "any";
}
function mightBeHTMLElement(node) {
const type = getNodeTypeAsString(node);
return type === "HTMLElement" || type === "any";
}
return {
"CallExpression[arguments.length=2] > MemberExpression.callee[property.name='insertAdjacentHTML']"(node) {
// Ignore known false positives
if (
node.parent != undefined
&& node.parent.arguments != undefined
&& node.parent.arguments.length >= 1
&& node.parent.arguments[1] != undefined
// element.insertAdjacentHTML('')
&& node.parent.arguments[1].type === 'Literal' && node.parent.arguments[1].value === ''
) {
return;
}
if (mightBeHTMLElement(node.object)) {
context.report({
node: node,
messageId: "noInsertAdjacentHTML"
});
}
},
"AssignmentExpression[left.type='MemberExpression'][left.property.name=/innerHTML|outerHTML/]"(node) {
// Ignore known false positives
if (
node.right != undefined
// element.innerHTML = ''
&& node.right.type === 'Literal' && node.right.value === ''
) {
return;
}
if (mightBeHTMLElement(node.left.object)) {
context.report({
node: node,
messageId: "noInnerHtml"
});
}
}
};
}
};

Просмотреть файл

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow call to Math.random and crypto.pseudoRandomBytes functions
* @author Antonios Katopodis
*/
"use strict";
const astUtils = require("../ast-utils");
const eslint = require("eslint/lib/eslint");
const path = require('path');
const bannedRandomLibraries = [
'chance',
'random-number',
'random-int',
'random-float',
'random-seed',
'unique-random'
]
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs:{
description: `Methods such as Math.random or crypto.pseudoRandomBytes do not produce cryptographically-secure random numbers and must not be used for security purposes such as generating tokens, passwords or keys.
Use crypto.randomBytes() or window.crypto.getRandomValues() instead.
`,
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-random.md"
},
messages: {
default: 'Do not use pseudo-random number generators for generating secret values such as tokens, passwords or keys.'
}
},
create: function(context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
"CallExpression > MemberExpression[property.name='pseudoRandomBytes']"(node) {
var notFalsePositive = false;
if (fullTypeChecker) {
const type = astUtils.getCallerType(fullTypeChecker, node.object, context);
notFalsePositive = type === "any" || type === "Crypto";
}else{
notFalsePositive = node.object.name === 'crypto';
}
if(notFalsePositive){
context.report({
node: node,
messageId: "default"
});
}
},
"CallExpression > MemberExpression[property.name='random']"(node) {
var notFalsePositive = false;
if (fullTypeChecker) {
const type = astUtils.getCallerType(fullTypeChecker, node.object, context);
notFalsePositive = type === "any" || type === "Math";
}else{
notFalsePositive = node.object.name === 'Math';
}
if(notFalsePositive){
context.report({
node: node,
messageId: "default"
});
}
},
ImportDeclaration(node){
if(bannedRandomLibraries.includes(path.basename(node.source.value))){
context.report({
node: node,
messageId: "default"
});
}
},
"CallExpression[callee.name='require'][arguments.length=1]"(node){
var requireName = path.parse(path.basename(node.arguments[0].value)).name;
if(bannedRandomLibraries.includes(requireName)){
context.report({
node: node,
messageId: "default"
});
}
}
};
}
};

Просмотреть файл

@ -0,0 +1,93 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Disallows usage of insecure protocols in URL strings
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
const DEFAULT_BLACKLIST = [
/^(ftp|http|telnet|ws):\/\//i
];
const DEFAULT_EXCEPTIONS = [ // TODO: add more typical false positives such as XML schemas after more testing
/^http:(\/\/|\\u002f\\u002f)schemas\.microsoft\.com(\/\/|\\u002f\\u002f)?.*/i,
/^http:(\/\/|\\u002f\\u002f)schemas\.openxmlformats\.org(\/\/|\\u002f\\u002f)?.*/i
];
module.exports = {
defaultBlacklist: DEFAULT_BLACKLIST,
defaultExceptions: DEFAULT_EXCEPTIONS,
meta: {
type: "suggestion",
fixable: "code",
schema: [
{
type: "object",
properties: {
blacklist: {
type: "array",
items: {
type: "string"
}
},
exceptions: {
type: "array",
items: {
type: "string"
}
},
},
additionalProperties: false
}
],
docs: {
category: "Security",
description: "Insecure protocols such as [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) or [FTP](https://en.wikipedia.org/wiki/File_Transfer_Protocol) should be replaced by their encrypted counterparts ([HTTPS](https://en.wikipedia.org/wiki/HTTPS), [FTPS](https://en.wikipedia.org/wiki/FTPS)) to avoid sending (potentially sensitive) data over untrusted network in plaintext.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-insecure-url.md"
},
messages: {
doNotUseInsecureUrl: "Do not use insecure URLs"
}
},
create: function (context) {
const options = context.options[0] || {};
const blacklist = (options.blacklist || DEFAULT_BLACKLIST).map((pattern) => { return new RegExp(pattern, "i"); });
const exceptions = (options.exceptions || DEFAULT_EXCEPTIONS).map((pattern) => { return new RegExp(pattern, "i"); });
function matches(patterns, value) {
return patterns.find((re) => { return re.test(value) }) !== undefined;
}
return {
"Literal"(node) {
if (typeof node.value === "string") {
if (matches(blacklist, node.value) && !matches(exceptions, node.value)) {
context.report({
node: node,
messageId: "doNotUseInsecureUrl"
});
}
}
},
"TemplateElement"(node) {
if (typeof node.value.raw === "string" && typeof node.value.cooked === "string") {
const rawStringText = node.value.raw;
const cookedStringText = node.value.cooked;
if ((matches(blacklist, rawStringText) && !matches(exceptions, rawStringText)) ||
(matches(blacklist, cookedStringText) && !matches(exceptions, cookedStringText))) {
context.report({
node: node,
messageId: "doNotUseInsecureUrl"
});
}
}
}
};
},
};

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow MSApp.execUnsafeLocalFunction method call
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs:{
description: "Calls to [`MSApp.execUnsafeLocalFunction()`](https://docs.microsoft.com/en-us/previous-versions/hh772324(v=vs.85)) bypass script injection validation and should be avoided.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-msapp-exec-unsafe.md"
},
messages: {
default: 'Do not bypass script injection validation'
}
},
create: function(context) {
return {
"CallExpression[arguments.length=1][callee.object.name='MSApp'][callee.property.name='execUnsafeLocalFunction']"(node) {
context.report(
{
node: node,
messageId: "default"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow * as target origin in window.postMessage
*/
"use strict";
const astUtils = require("../ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
description: "Always provide specific target origin, not * when sending data to other windows using [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage#Security_concerns) to avoid data leakage outside of trust boundary.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-postmessage-star-origin.md"
},
messages: {
default: 'Do not use * as target origin when sending data to other windows'
}
},
create: function (context) {
const fullTypeChecker = astUtils.getFullTypeChecker(context);
return {
"CallExpression[arguments.length>=2][arguments.length<=3][callee.property.name=postMessage]"(node) {
// Check that second argument (target origin) is Literal "*"
if (!(node.arguments[1].type === 'Literal' && node.arguments[1].value == '*')) {
return;
}
// Check that object type is Window when full type information is available
if (fullTypeChecker) {
const tsNode = context.parserServices.esTreeNodeToTSNodeMap.get(node.callee.object);
const tsType = fullTypeChecker.getTypeAtLocation(tsNode);
const type = fullTypeChecker.typeToString(tsType);
if (type !== "any" && type !== "Window") {
return;
}
}
context.report({
node: node,
messageId: "default"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
description: "When calling [`Buffer.allocUnsafe`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafe_size) and [`Buffer.allocUnsafeSlow`](https://nodejs.org/api/buffer.html#buffer_static_method_buffer_allocunsafeslow_size), the allocated memory is not wiped-out and can contain old, potentially sensitive data.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-unsafe-alloc.md"
},
messages: {
default: 'Do not allocate uninitialized buffers in Node.js'
}
},
create: function (context) {
return {
"MemberExpression[object.name='Buffer'][property.name=/allocUnsafe|allocUnsafeSlow/]"(node) {
// Known false positives
if (
node.parent != undefined &&
node.parent.arguments != undefined &&
node.parent.arguments.length != undefined &&
// Buffer.allocUnsafe(0);
node.parent.type === 'CallExpression' &&
node.parent.arguments.length == 1 &&
node.parent.arguments[0] != undefined &&
node.parent.arguments[0].type === 'Literal' &&
node.parent.arguments[0].value == '0'
) {
return;
}
context.report({
node: node,
messageId: "default"
});
}
};
}
};

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to disallow WinJS.Utilities.setInnerHTMLUnsafe or WinJS.Utilities.setOuterHTMLUnsafe method call
* @author Antonios Katopodis
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs:{
description: "Calls to [`WinJS.Utilities.setInnerHTMLUnsafe()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211696(v=win.10)) and similar methods do not perform any input validation and should be avoided. Use [`WinJS.Utilities.setInnerHTML()`](https://docs.microsoft.com/en-us/previous-versions/windows/apps/br211697(v=win.10)) instead.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/no-winjs-html-unsafe.md"
},
messages: {
default: 'Do not set HTML using unsafe methods from WinJS.Utilities'
}
},
create: function(context) {
return {
"CallExpression[callee.object.object.name='WinJS'][callee.object.property.name='Utilities'][callee.property.name=/(insertAdjacent|setInner|setOuter)HTMLUnsafe/]"(node) {
context.report(
{
node: node,
messageId: "default"
});
}
};
}
};

108
lib/rules/react-iframe-missing-sandbox.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Rule to enforce sandbox attribute on iframe elements
*/
"use strict";
// TODO: Follow-up on https://github.com/yannickcr/eslint-plugin-react/issues/2754 and try to merge rule into eslint-plugin-react
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
type: "suggestion",
fixable: "code",
schema: [],
docs: {
category: "Security",
description: "The [sandbox](https://www.w3schools.com/tags/att_iframe_sandbox.asp) attribute enables an extra set of restrictions for the content in the iframe and should always be specified.",
url: "https://github.com/microsoft/eslint-plugin-sdl/blob/master/docs/rules/react-iframe-missing-sandbox.md"
},
messages: {
attributeMissing: 'An iframe element is missing a sandbox attribute',
invalidValue: 'An iframe element defines a sandbox attribute with invalid value "{{ value }}"',
invalidCombination: 'An iframe element defines a sandbox attribute with both allow-scripts and allow-same-origin which is invalid'
}
},
create(context) {
const ALLOWED_VALUES = [
// From https://www.w3schools.com/tags/att_iframe_sandbox.asp
'',
'allow-forms',
'allow-modals',
'allow-orientation-lock',
'allow-pointer-lock',
'allow-popups',
'allow-popups-to-escape-sandbox',
'allow-presentation',
'allow-same-origin',
'allow-scripts',
'allow-top-navigation',
'allow-top-navigation-by-user-activation'
];
function validateSandboxAttribute(node, attribute) {
const values = attribute.value.value.split(' ');
let allowScripts = false;
let allowSameOrigin = false;
values.forEach((attributeValue) => {
const trimmedAttributeValue = attributeValue.trim();
if (ALLOWED_VALUES.indexOf(trimmedAttributeValue) === -1) {
context.report({
node,
messageId: 'invalidValue',
data: {
value: trimmedAttributeValue
}
});
}
if (trimmedAttributeValue === 'allow-scripts') {
allowScripts = true;
}
if (trimmedAttributeValue === 'allow-same-origin') {
allowSameOrigin = true;
}
});
if (allowScripts && allowSameOrigin) {
context.report({
node,
messageId: 'invalidCombination'
});
}
}
return {
'JSXOpeningElement[name.name="iframe"]'(node) {
let sandboxAttributeFound = false;
node.attributes.forEach((attribute) => {
if (attribute.type === 'JSXAttribute'
&& attribute.name
&& attribute.name.type === 'JSXIdentifier'
&& attribute.name.name === 'sandbox'
) {
sandboxAttributeFound = true;
if (
attribute.value
&& attribute.value.type === 'Literal'
&& attribute.value.value
) {
// Only string literals are supported for now
validateSandboxAttribute(node, attribute);
}
}
});
if (!sandboxAttributeFound) {
context.report({
node,
messageId: 'attributeMissing'
});
}
}
};
}
};

1638
package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

33
package.json Normal file
Просмотреть файл

@ -0,0 +1,33 @@
{
"name": "@microsoft/eslint-plugin-sdl",
"version": "0.1.5",
"description": "ESLint plugin focused on common security issues and misconfigurations discoverable during static testing as part of Microsoft Security Development Lifecycle (SDL)",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin",
"sdl"
],
"author": "Microsoft",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/eslint-plugin-sdl"
},
"homepage": "https://github.com/microsoft/eslint-plugin-sdl",
"bugs": "https://github.com/microsoft/eslint-plugin-sdl/issues",
"main": "lib/index.js",
"scripts": {
"test": "mocha tests --recursive"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.7.0",
"eslint": "^7.1.0",
"mocha": "^7.2.0",
"typescript": "^3.9.7"
},
"engines": {
"node": ">=0.10.0"
},
"license": "ISC"
}

0
tests/fixtures/estree.ts поставляемый Normal file
Просмотреть файл

8
tests/fixtures/tsconfig.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
{
"compilerOptions": {
"jsx": "preserve"
},
"include": [
"estree.ts"
]
}

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"bypassSecurityTrustHtml('XSS')",
"x.bypassSecurityTrustHtml()",
"x.BypassSecurityTrustHtml('XSS')"
],
invalid: [
{
code: "$('p').bypassSecurityTrustHtml('XSS');",
errors: [
{
messageId: "noBypass",
line: 1,
endLine: 1,
column: 1,
endColumn: 38
}
]
},
{
code: "$('p').bypassSecurityTrustResourceUrl('XSS')",
errors: [{ messageId: "noBypass"}]
},
{
code: "$('p').bypassSecurityTrustScript('XSS')",
errors: [{ messageId: "noBypass"}]
},
{
code: "$('p').bypassSecurityTrustStyle('XSS')",
errors: [{ messageId: "noBypass"}]
},
{
code: "$('p').bypassSecurityTrustUrl('XSS')",
errors: [{ messageId: "noBypass"}]
}
]
});

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"trustAsHtml()",
"$sce.trustAsHtml()",
"$sce.trustAsHtml('')",
"$sce.TrustAsHtml('XSS')",
"x.trustAsHtml('XSS')",
"$sceProvider.enabled()",
"$sceProvider.enabled(true)",
"$sceProvider.enabled(1)",
],
invalid: [
{
code: "$sceDelegate.trustAs($sce.HTML, 'XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAs($sce.HTML, 'XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAsCss('XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAsHtml('XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAsJs('XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAsResourceUrl('XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sce.trustAsUrl('XSS')",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sceProvider.enabled(false)",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sceProvider.enabled(0)",
errors: [{ messageId: "doNotBypass" }]
},
{
code: "$sceProvider.enabled(true != true)",
errors: [{ messageId: "doNotBypass" }]
}
]
});

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"enableSvg()",
"enableSvg(true)",
"$sanitizeProvider.enableSvg()",
"$sanitizeProvider.enableSvg(false)",
"$sanitizeProvider.enableSvg(0)",
"$sanitizeProvider.EnableSvg(0)"
],
invalid: [
{
code: "$sanitizeProvider.enableSvg(true)",
errors: [{ messageId: "doNotEnableSVG" }]
},
{
code: "$sanitizeProvider.enableSvg(1)",
errors: [{ messageId: "doNotEnableSVG" }]
}
]
});

Просмотреть файл

@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"aHrefSanitizationWhitelist('.*')",
"x.aHrefSanitizationWhitelist('.*')",
"$compileProvider.aHrefSanitizationWhitelist()",
"$compileProvider.AHrefSanitizationWhitelist('.*')"
],
invalid: [
{
code: "$compileProvider.aHrefSanitizationWhitelist('.*');",
errors: [
{
messageId: "noSanitizationWhitelist",
line: 1,
endLine: 1,
column: 1,
endColumn: 50
}
]
},
{
code: "$compileProvider.imgSrcSanitizationWhitelist('.*');",
errors: [
{
messageId: "noSanitizationWhitelist",
line: 1,
endLine: 1,
column: 1,
endColumn: 51
}
]
}
]
});

Просмотреть файл

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
const typescriptParserPath = require.resolve("@typescript-eslint/parser");
const testUtils = require("../test-utils");
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
`
function documentLikeAPIFunction(){
return {
cookie:'fake.cookie'
}
}
var document2 = documentLikeAPIFunction();
document2.cookie = '...';
document2.cookie = '...';
documentLikeAPIFunction().cookie = '...'
`,
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
interface DocumentLikeAPI {
cookie: string;
}
function documentLikeAPIFunction(): DocumentLikeAPI {
return null;
}
function X() {
// These usages are OK because they are not on the DOM document
var document: DocumentLikeAPI = documentLikeAPIFunction();
document.cookie = '...';
document.cookie = '...';
}
documentLikeAPIFunction().cookie = '...';
`
}
],
invalid: [
{
code: "document.cookie = '...'",
errors: [{ messageId: "doNotUseCookies" }]
},
{
code: "window.document.cookie = '...'",
errors: [{ messageId: "doNotUseCookies" }]
},
{
code: "this.window.document.cookie = '...'",
errors: [{ messageId: "doNotUseCookies" }]
},
{
code: "globalThis.window.document.cookie = '...'",
errors: [{ messageId: "doNotUseCookies" }]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
function documentFunction(): Document {
return window.document;
}
documentFunction().cookie = '...';
`,
errors: [{ messageId: "doNotUseCookies" }]
},
{
parser: typescriptParserPath,
code: `
namespace Sample {
function method() {
return document.cookie;
}
}
`,
errors: [{ messageId: "doNotUseCookies" }]
}
]
});

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const testUtils = require("../test-utils");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
interface DocumentLikeAPI {
domain: string;
}
function documentLikeAPIFunction(): DocumentLikeAPI {
return null;
}
function main() {
var document: DocumentLikeAPI = documentLikeAPIFunction();
document.domain = 'somevalue';
}
`
}
],
invalid: [
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: "var doc = window.document; doc.domain = 'somevalue';",
errors: [{ messageId: "default" }]
},
{
code: "document.domain = 'somevalue'",
errors: [{ messageId: "default" }]
},
{
code: "window.document.domain = 'somevalue'",
errors: [{ messageId: "default" }]
},
{
code: `
var somevalue = 'somevalue';
document.domain = somevalue;
window.document.domain = somevalue;
`,
errors: [
{
line: 3,
messageId: "default"
},
{
line: 4,
messageId: "default"
}
]
}
]
});

Просмотреть файл

@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const testUtils = require("../test-utils");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
interface DocumentLikeAPI {
write: ((arg : string) => void);
writeln: ((arg : string) => void);
}
function documentLikeAPIFunction() : DocumentLikeAPI {
return {
write: () => {},
writeln: () => {},
};
}
`
},
{
code: `
function documentLikeAPIFunction() {
return {
write: function(){},
writeln: function(){}
};
}
var documentAPI = documentLikeAPIFunction();
documentAPI.write('...');
documentAPI.writeln('...');
documentLikeAPIFunction().write('...');
documentLikeAPIFunction().writeln('...');
// wrong # of args
document.write();
document.write('', '');
document.writeln();
document.writeln('', '');
`
}
],
invalid: [
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
var doc = document;
doc.write('...');
doc.writeln('...');
function documentFunction() : Document {
return window.document;
}
documentFunction().write('...');
documentFunction().writeln('...');
`,
errors: [
{ messageId: "default", line: 3 },
{ messageId: "default", line: 4 },
{ messageId: "default", line: 8 },
{ messageId: "default", line: 9 }
]
},
{
code: `
document.write('...');
document.writeln('...');
window.document.write('...');
window.document.writeln('...');
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 },
{ messageId: "default", line: 4 },
{ messageId: "default", line: 5 }
]
}
]
});

Просмотреть файл

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
{
code: `
var mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false
}
});
var view = new BrowserView({
webPreferences: {
nodeIntegration: false
}
});
`
}
],
invalid: [
{
code: `
var mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
nodeIntegrationInSubFrames: true
}
});
`,
errors: [
{ messageId: "default", line: 4},
{ messageId: "default", line: 5},
{ messageId: "default", line: 6}
]
},
{
code: `
var view = new BrowserView({
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: true,
nodeIntegrationInSubFrames: true
}
});
`,
errors: [
{ messageId: "default", line: 4},
{ messageId: "default", line: 5},
{ messageId: "default", line: 6}
]
}
]
});

Просмотреть файл

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
const testUtils = require("../test-utils");
ruleTester.run(ruleId, rule, {
valid: [
"test.html = 'test'",
"test.html()",
"test.html('','')",
"element.html('');",
"element.html(null);"
],
invalid: [
{
code: "$('p').html('XSS')",
errors: [{ messageId: "default", line: 1 }]
},
{
code: "$(selector).html(sample_function())",
errors: [{ messageId: "default", line: 1 }]
},
{
parser: testUtils.tsParser,
parserOptions: {
ecmaVersion: 6,
sourceType: "module"
},
env: {
},
code: `
import $ from "jquery";
test.html('XSS');
`,
errors: [
{ messageId: "default", line: 3 }
]
}
]
});

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
const testUtils = require("../test-utils");
ruleTester.run(ruleId, rule, {
valid: [
"var test = element.innerHTML",
"var test = element.outerHTML",
"document.body.innerHTML = ''",
"document.test",
"element.insertAdjacentHTML()",
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
class Test {
innerHTML: string;
outerHTML: string;
constructor(test: string) {
this.innerHTML = test;
this.outerHTML = test;
}
};
let test = new Test("test");
test.innerHTML = test;
test.outerHTML = test;
`
}
],
invalid: [
// TypeScript with full type information
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
var element = document.getElementById(id);
element.innerHTML = 'test';
element.outerHTML = 'test';
element.insertAdjacentHTML('beforebegin', 'foo');
`,
errors: [
{ messageId: "noInnerHtml", line: 3 },
{ messageId: "noInnerHtml", line: 4 },
{ messageId: "noInsertAdjacentHTML", line: 5 }
]
},
{
code: `
element.innerHTML = 'test';
parent.child.innerHTML += 'test';
`,
errors: [
{ messageId: "noInnerHtml", line: 2 },
{ messageId: "noInnerHtml", line: 3 }
]
},
{
code: `
element.outerHTML = 'test';
parent.child.outerHTML += 'test';
`,
errors: [
{ messageId: "noInnerHtml", line: 2 },
{ messageId: "noInnerHtml", line: 3 }
]
},
{
code: "element.insertAdjacentHTML('beforebegin', 'foo')",
errors: [{ messageId: "noInsertAdjacentHTML", line: 1 }]
}
]
});

Просмотреть файл

@ -0,0 +1,198 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
const testUtils = require("../test-utils");
ruleTester.run(ruleId, rule, {
valid: [
"Math.Random;",
"Math.random;",
"math.random();",
"random();",
{
code: `
Math.Random;
Math.random;
math.random();
random();
`
},
{
code:`
require('./node_modules/not-unsafe-random');
require('eslint');
require('test');
require('random-package');
require('random-float2');
require('random2-seed');
`
},
{
parserOptions: testUtils.moduleParserOptions,
code:`
import './node_modules/untest';
import 'random';
import 'random-3';
import 'eslint';
import 'eslint-plugin-sdl';
import 'testing';
`
},
{
code:`
cryptos.pseudoRandomBytes();
pseudoRandomBytes();
pseudoRandomByte();
cryptos.pseudoRondomBytes();
`
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
function random(){}
random();
Math.Random;
Math.random;
`
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code:`
function pseudoRandomBytes(){}
function pseudoRandomByte(){}
pseudoRandomBytes();
pseudoRandomByte();
cryptos.pseudoRondomBytes();
cryptos.pseudoRondomBytes();
`
}
],
invalid: [
{
code: `
Math.random();
crypto.pseudoRandomBytes();
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 }
]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
Math.random();
this.Math.random();
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 }
]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
function notMath() : Math{
return Math;
}
notMath().random();
`,
errors: [
{ messageId: "default", line: 6 }
]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
crypto.pseudoRandomBytes();
`,
errors: [
{ messageId: "default", line: 2 }
]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
function notCrypto() : Crypto{
return crypto;
}
notCrypto().pseudoRandomBytes();
`,
errors: [
{ messageId: "default", line: 6 }
]
},
{
parserOptions: testUtils.moduleParserOptions,
code:`
import './node_modules/unique-random';
import 'chance';
import 'random-number';
import 'random-int';
import 'random-float';
import 'random-seed';
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 },
{ messageId: "default", line: 4 },
{ messageId: "default", line: 5 },
{ messageId: "default", line: 6 },
{ messageId: "default", line: 7 }
]
},
{
parserOptions: testUtils.moduleParserOptions,
code:`
import * as chance1 from 'chance';
import defaultExport from 'chance';
import { chance } from 'chance';
import { chance as chance2 } from 'chance';
import { chance3, chance4 } from 'chance';
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 },
{ messageId: "default", line: 4 },
{ messageId: "default", line: 5 },
{ messageId: "default", line: 6 }
]
},
{
code:`
require('./node_modules/unique-random');
require('**/chance.js');
require('random-number');
require('random-int');
require('random-float');
require('random-seed');
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 },
{ messageId: "default", line: 4 },
{ messageId: "default", line: 5 },
{ messageId: "default", line: 6 },
{ messageId: "default", line: 7 }
]
}
]
});

Просмотреть файл

@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join("../../../lib/rules/", ruleId));
const RuleTester = require("eslint").RuleTester;
const testUtils = require("../test-utils");
/**
* Notes:
* - ES2015/ES6 introduced template literals (``). This is considered in parserOptions for relevant tests.
*/
let ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
{ // should allow https,ftps strings in variables
code: `
var x = 'https://www.example.com'
var y = 'ftps://www.example.com'
`
},
{ // should allow https,ftps template strings in variables
code: `
var x = \`https://www.template-examples.com\`
var y = \`ftps://www.template-file-examples.com\`
`,
parserOptions: testUtils.moduleParserOptions
},
{ // should allow https,ftps multipart template strings in variables
code: `
var x = \`https://www.\${multipartExample}.com\`
var y = \`ftps://www.\${multipartExample}.com\`
`,
parserOptions: testUtils.moduleParserOptions
},
{ // should allow http,ftp in middle of string
code: "var x = 'The protocol may be http://, https://, ftp:// or ftps://'"
},
{ // should allow https,ftps strings in default values
code: `
function f(x : string = 'https://www.example.com') {}
function f(y : string = 'ftps://www.example.com') {}
`,
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
},
{ // should allow user-provided exceptions matches, regardless of upper/lower-case
code: `
var a1 = 'http://www.allow-example.com'
var a2 = 'HtTp://www.allow-example.com/path'
var b1 = 'FTP://www.allow-file-example.com'
var c1 = 'LDaP://www.allow-ldap-example.com'
`,
options: [{
exceptions: ["HTTP:\/\/www\.allow-example\.com\/?.*", "FtP:\/\/www\.allow-file-example\.com", "LdaP:\/\/www\.allow-ldap-example\.com"]
}]
},
],
invalid: [
{ // should ban http,ftp strings in variables
code: `
var x1 = 'http://www.examples.com'
var x2 = 'HTTP://www.examples.com'
var y1 = 'ftp://www.file-examples.com'
var y2 = 'FTP://www.file-examples.com'
`,
errors: [
{ messageId: "doNotUseInsecureUrl", line: 2},
{ messageId: "doNotUseInsecureUrl", line: 3},
{ messageId: "doNotUseInsecureUrl", line: 4},
{ messageId: "doNotUseInsecureUrl", line: 5}
],
},
{ // should ban http,ftp template strings in variables
code: `
var x1 = \`http://www.template-examples.com\`
var x2 = \`HTTP://www.template-examples.com\`
var y1 = \`ftp://www.file-examples.com\`
var y2 = \`FTP://www.file-examples.com\`
`,
errors: [
{ messageId: "doNotUseInsecureUrl", line: 2},
{ messageId: "doNotUseInsecureUrl", line: 3},
{ messageId: "doNotUseInsecureUrl", line: 4},
{ messageId: "doNotUseInsecureUrl", line: 5}
],
parserOptions: testUtils.moduleParserOptions
},
{ // should ban http,ftp multipart template strings in variables
code: `
var x1 = \`http://www.\${multipartExample}.com\`;
var y1 = \`ftp://www.\${multipartExample}.com\`;
`,
errors: [
{ messageId: "doNotUseInsecureUrl", line: 2},
{ messageId: "doNotUseInsecureUrl", line: 3},
],
parserOptions: testUtils.moduleParserOptions
},
{ // should ban http,ftp strings in default values
code: `
function f(x : string = 'http://www.example.com') {}
function f(y : string = 'ftp://www.example.com') {}
`,
errors: [
{ messageId: "doNotUseInsecureUrl", line: 2},
{ messageId: "doNotUseInsecureUrl", line: 3},
],
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
},
{ // should ban user-provided blacklist matches, regardless of upper/lower-case
code: `
var a1 = 'http://www.ban-example.com'
var a2 = 'HTTP://www.ban-example.com/path'
var b1 = 'FtP://www.ban-file-example.com'
var c1 = 'LDAp://www.ban-ldap-example.com'
`,
errors: [
{ messageId: "doNotUseInsecureUrl", line: 2},
{ messageId: "doNotUseInsecureUrl", line: 3},
{ messageId: "doNotUseInsecureUrl", line: 4},
{ messageId: "doNotUseInsecureUrl", line: 5}
],
options: [{
blacklist: ["htTp:\/\/www\.ban-example\.com\/?.*", "fTp:\/\/www\.ban-file-example\.com\/?.*", "lDAp:\/\/www\.ban-ldap-example\.com\/?.*"]
}]
},
]
});

Просмотреть файл

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
const testUtils = require("../test-utils");
ruleTester.run(ruleId, rule, {
valid: [
"test.execUnsafeLocalFunction = 'test'",
"MSApp.execUnsafeLocalFunction()"
],
invalid: [
{
code: "MSApp.execUnsafeLocalFunction(testfunc)",
errors: [
{ messageId: "default", line: 1, type: 'CallExpression' }
]
}
]
});

Просмотреть файл

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const testUtils = require("../test-utils");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"window.postMessage()",
"window.postMessage = ''",
"window.postMessage(1)",
"window.postMessage(1, 2, 3, 4)",
"window.postMessage('data', 'https://target.domain')",
"window.postMessage('data', 'https://target.domain', 'menubar=yes')",
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
class WindowLike {
postMessage(): void {
};
}
function main() {
var w: WindowLike = new WindowLike();
w.postMessage('test', '*');
}
`
}
],
invalid: [
{
code: `
any.postMessage(message, "*");
any.postMessage(message, "*", "menubar=yes");
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 }
]
},
{
parser: testUtils.tsParser,
parserOptions: testUtils.tsParserOptions,
code: `
window.frames[0].postMessage(message, "*");
var w1 = window.open(url);
w1.postMessage(message, "*");
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 4 }
]
},
]
});

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
"use strict";
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run(ruleId, rule, {
valid: [
"foo.allocUnsafe",
"Buffer.allocUnsafe(0)",
"Buffer.allocUnsafeSlow(0)"
],
invalid: [
{
code: `
var buf1 = Buffer.allocUnsafe(10);
var buf2 = Buffer.allocUnsafeSlow(10)
`,
errors: [
{ messageId: "default", line: 2 },
{ messageId: "default", line: 3 }
]
}
]
});

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
const testUtils = require("../test-utils");
ruleTester.run(ruleId, rule, {
valid: [
'element.insertAdjacentHTMLUnsafe = "test";'
],
invalid: [
{
code: `
WinJS.Utilities.insertAdjacentHTMLUnsafe(element, position, text);
WinJS.Utilities.setInnerHTMLUnsafe(element, text);
WinJS.Utilities.setOuterHTMLUnsafe(element, text);
`,
errors: [
{ messageId: "default", line: 2, type: 'CallExpression' },
{ messageId: "default", line: 3, type: 'CallExpression' },
{ messageId: "default", line: 4, type: 'CallExpression' }
]
}
]
});

79
tests/lib/rules/react-iframe-missing-sandbox.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
'use strict';
const path = require("path");
const ruleId = path.parse(__filename).name;
const rule = require(path.join('../../../lib/rules/', ruleId));
const RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
}
});
ruleTester.run(ruleId, rule, {
valid: [
{ code: '<div sandbox="__unknown__" />;' },
{ code: '<iframe sandbox="" />;' },
{ code: '<iframe src="foo.htm" sandbox></iframe>' },
{ code: '<iframe src="foo.htm" sandbox sandbox></iframe>' },
{ code: '<iframe sandbox={""} />' },
{ code: '<iframe sandbox="allow-forms"></iframe>' },
{ code: '<iframe sandbox="allow-modals"></iframe>' },
{ code: '<iframe sandbox="allow-orientation-lock"></iframe>' },
{ code: '<iframe sandbox="allow-pointer-lock"></iframe>' },
{ code: '<iframe sandbox="allow-popups"></iframe>' },
{ code: '<iframe sandbox="allow-popups-to-escape-sandbox"></iframe>' },
{ code: '<iframe sandbox="allow-presentation"></iframe>' },
{ code: '<iframe sandbox="allow-same-origin"></iframe>' },
{ code: '<iframe sandbox="allow-scripts"></iframe>' },
{ code: '<iframe sandbox="allow-top-navigation"></iframe>' },
{ code: '<iframe sandbox="allow-top-navigation-by-user-activation"></iframe>' },
{ code: '<iframe sandbox="allow-forms allow-modals"></iframe>' },
{ code: '<iframe sandbox="allow-popups allow-popups-to-escape-sandbox allow-pointer-lock allow-same-origin allow-top-navigation"></iframe>' }
],
invalid: [
{
code: '<iframe></iframe>;',
errors: [{ messageId: 'attributeMissing' }]
},
{
code: '<iframe/>;',
errors: [{ messageId: 'attributeMissing' }]
},
{
code: '<iframe sandbox="__unknown__"></iframe>',
errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }]
},
{
code: '<iframe sandbox="allow-popups __unknown__"/>',
errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }]
},
{
code: '<iframe sandbox="__unknown__ allow-popups"/>',
errors: [{ messageId: 'invalidValue', data: { value: '__unknown__' } }]
},
{
code: '<iframe sandbox=" allow-forms __unknown__ allow-popups __unknown__ "/>',
errors: [
{ messageId: 'invalidValue', data: { value: '__unknown__' } },
{ messageId: 'invalidValue', data: { value: '__unknown__' } }
]
},
{
code: '<iframe sandbox="allow-scripts allow-same-origin"></iframe>;',
errors: [{ messageId: 'invalidCombination' }]
},
{
code: '<iframe sandbox="allow-same-origin allow-scripts"/>;',
errors: [{ messageId: 'invalidCombination' }]
}
]
});

23
tests/lib/test-utils.js Normal file
Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* @fileoverview Common utils for tests.
*/
"use strict";
const path = require("path");
module.exports = {
tsParser: require.resolve("@typescript-eslint/parser"),
tsParserOptions: {
tsconfigRootDir: path.join(__dirname, '../fixtures'),
project: 'tsconfig.json',
sourceType: "module"
},
moduleParserOptions: {
ecmaVersion: 6,
sourceType: "module"
}
};