Initial Commit
This commit is contained in:
Родитель
ddfcd808c5
Коммит
a1d06caa4e
|
@ -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
|
|
@ -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
|
|
@ -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}}
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
node_modules/
|
||||
.vscode/
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
tests/
|
||||
.github/
|
|
@ -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
|
|
@ -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.
|
|
@ -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 -->
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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.
|
|
@ -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/)
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -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'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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,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' }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
|
@ -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' }]
|
||||
}
|
||||
]
|
||||
});
|
|
@ -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"
|
||||
}
|
||||
};
|
Загрузка…
Ссылка в новой задаче