Switch to prettier for formatting; drop obsolete ESLint formatting rules. (#238)
This commit is contained in:
Родитель
64cbbae24e
Коммит
ea2f6debda
8
.babelrc
8
.babelrc
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
// @babel/eslint-parser, which is used in the test suite, requires a config
|
||||
// file by default, which is why this file exists. Instead of adding
|
||||
// configuration here, we should pass configuration to the parser using
|
||||
// `parserOptions` for each test instead.
|
||||
// @babel/eslint-parser, which is used in the test suite, requires a config
|
||||
// file by default, which is why this file exists. Instead of adding
|
||||
// configuration here, we should pass configuration to the parser using
|
||||
// `parserOptions` for each test instead.
|
||||
}
|
||||
|
|
|
@ -1,57 +1,26 @@
|
|||
{
|
||||
"rules": {
|
||||
"lines-around-comment": [
|
||||
2,
|
||||
{
|
||||
"allowArrayStart": true,
|
||||
"allowBlockStart": true,
|
||||
"beforeBlockComment": false,
|
||||
"beforeLineComment": true
|
||||
}
|
||||
],
|
||||
"prefer-template": [
|
||||
2
|
||||
],
|
||||
"object-shorthand": [
|
||||
2
|
||||
],
|
||||
"prefer-const": [
|
||||
2
|
||||
],
|
||||
"no-var": [
|
||||
2
|
||||
],
|
||||
"indent": [
|
||||
2,
|
||||
4
|
||||
],
|
||||
"quotes": [
|
||||
2,
|
||||
"double"
|
||||
],
|
||||
"linebreak-style": [
|
||||
2,
|
||||
"unix"
|
||||
],
|
||||
"semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"space-in-parens": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"valid-jsdoc": [
|
||||
2
|
||||
],
|
||||
"require-jsdoc": [
|
||||
2
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended"
|
||||
"rules": {
|
||||
"lines-around-comment": [
|
||||
2,
|
||||
{
|
||||
"allowArrayStart": true,
|
||||
"allowBlockStart": true,
|
||||
"allowObjectStart": true,
|
||||
"beforeBlockComment": false,
|
||||
"beforeLineComment": true
|
||||
}
|
||||
],
|
||||
"prefer-template": [2],
|
||||
"object-shorthand": [2],
|
||||
"prefer-const": [2],
|
||||
"no-var": [2],
|
||||
"valid-jsdoc": [2],
|
||||
"require-jsdoc": [2]
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "prettier"]
|
||||
}
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: 'Test: Node ${{ matrix.node-version }} - ESLint ${{ matrix.eslint-version }}'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
eslint-version: [8.x, 7.x, 6.x]
|
||||
node-version: [16.x, 14.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
ci:
|
||||
name: "Test: Node ${{ matrix.node-version }} - ESLint ${{ matrix.eslint-version }}"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
eslint-version: [8.x, 7.x, 6.x]
|
||||
node-version: [16.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 }}
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Use ESLint ${{ matrix.eslint-version }}
|
||||
run: yarn upgrade eslint@${{ matrix.eslint-version }}
|
||||
- name: Use ESLint ${{ matrix.eslint-version }}
|
||||
run: yarn upgrade eslint@${{ matrix.eslint-version }}
|
||||
|
||||
- name: Install
|
||||
run: yarn install
|
||||
- name: Install
|
||||
run: yarn install
|
||||
|
||||
- name: Lint
|
||||
run: yarn run lint
|
||||
- name: Lint
|
||||
run: yarn run lint
|
||||
|
||||
- name: Test
|
||||
run: yarn run test
|
||||
- name: Test
|
||||
run: yarn run test
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# Community Participation Guidelines
|
||||
|
||||
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
|
||||
This repository is governed by Mozilla's code of conduct and etiquette guidelines.
|
||||
For more details, please read the
|
||||
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
|
||||
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
|
||||
|
||||
## How to Report
|
||||
|
||||
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
|
||||
|
||||
<!--
|
||||
|
|
16
README.md
16
README.md
|
@ -1,4 +1,5 @@
|
|||
[![Build Status](https://travis-ci.org/mozilla/eslint-plugin-no-unsanitized.svg?branch=master)](https://travis-ci.org/mozilla/eslint-plugin-no-unsanitized)
|
||||
|
||||
# Disallow unsanitized code (no-unsanitized)
|
||||
|
||||
These rules disallow unsafe coding practices that may result into security
|
||||
|
@ -16,23 +17,24 @@ of our products and services.
|
|||
# Rule Details
|
||||
|
||||
## method
|
||||
The *method* rule disallows certain function calls.
|
||||
|
||||
The _method_ rule disallows certain function calls.
|
||||
E.g., `document.write()` or `insertAdjacentHTML()`.
|
||||
See [docs/rules/method.md](docs/rules/method.md) for more.
|
||||
|
||||
## property
|
||||
The *property* rule disallows certain assignment expressions, e.g., to `innerHTML`.
|
||||
|
||||
The _property_ rule disallows certain assignment expressions, e.g., to `innerHTML`.
|
||||
|
||||
See [docs/rules/property.md](docs/rules/property.md) for more.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
Here are a few examples of code that we do not want to allow:
|
||||
|
||||
```js
|
||||
foo.innerHTML = input.value;
|
||||
bar.innerHTML = "<a href='"+url+"'>About</a>";
|
||||
bar.innerHTML = "<a href='" + url + "'>About</a>";
|
||||
```
|
||||
|
||||
A few examples of allowed practices:
|
||||
|
@ -43,12 +45,10 @@ bar.innerHTML = "<a href='/about.html'>About</a>";
|
|||
bar.innerHTML = escapeHTML`<a href='${url}'>About</a>`;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
# Install
|
||||
|
||||
With **yarn** or **npm**:
|
||||
|
||||
```bash
|
||||
$ yarn add -D eslint-plugin-no-unsanitized
|
||||
$ npm install --save-dev eslint-plugin-no-unsanitized
|
||||
|
@ -65,6 +65,7 @@ In your `.eslintrc.json` file enable this rule with the following:
|
|||
```
|
||||
|
||||
Or:
|
||||
|
||||
```json
|
||||
{
|
||||
"plugins": ["no-unsanitized"],
|
||||
|
@ -76,4 +77,5 @@ Or:
|
|||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
See [docs/](docs/).
|
||||
|
|
12
SECURITY.md
12
SECURITY.md
|
@ -6,12 +6,14 @@ Only the latest version is supported.
|
|||
[Firefox might use a slightly older version](https://hg.mozilla.org/mozilla-central/file/tip/package.json), which is supported-ish.
|
||||
|
||||
## Our Threat Model
|
||||
|
||||
### What is considered a vulnerability?
|
||||
|
||||
We assume that a project which makes use of this plugin to apply
|
||||
a reasonable amount of scrutiny to all patches that are accepted.
|
||||
We understand that this is a fuzzy line and the level of expected
|
||||
judgement and sensitivity to security issues will vary between
|
||||
projects and reviewers.
|
||||
projects and reviewers.
|
||||
|
||||
JavaScript static analysis is always very limited. On top we are
|
||||
limiting ourselves to the APIs that are provided by eslint.
|
||||
|
@ -20,6 +22,7 @@ that have been defined statically in different code paths or different
|
|||
files.
|
||||
|
||||
Here's a a couple of examples which we do **not** consider a bug in the linter:
|
||||
|
||||
```js
|
||||
foo['inner'+'HTML'] = evil; // no way to resolve concatenation
|
||||
eval(atob(...)) // use eslint no-eval rules please.
|
||||
|
@ -28,8 +31,8 @@ function(d) { return document }
|
|||
d.write(evil); // no way to resolve "d" statically
|
||||
```
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
### Bug Bounty / Vulnerability Rewards
|
||||
|
||||
Momentarily, we do not offer a bug bounty for security issues with this linter rule.
|
||||
|
@ -43,12 +46,13 @@ I.e., for Firefox please follow the [Mozilla Client Bug Bounty FAQ and use the
|
|||
linked bugzilla submission form](https://www.mozilla.org/en-US/security/client-bug-bounty/#claiming-a-bounty)
|
||||
(requires you to create an account).
|
||||
|
||||
### Sneaking your *obfuscated* code past this linter
|
||||
### Sneaking your _obfuscated_ code past this linter
|
||||
|
||||
Hey, did you read "What is considered a vulnerability", above?
|
||||
|
||||
### Sneaking your innocent looking code past the linter
|
||||
|
||||
If what you found constitues in a code pattern that eslint should complain about, but doesn't,
|
||||
[please file a private bug Bugzilla using this form][bugzilla-enter-private-issue] (requires creating an account).
|
||||
|
||||
|
||||
[bugzilla-enter-private-issue]: https://bugzilla.mozilla.org/enter_bug.cgi?assigned_to=nobody%40mozilla.org&bug_ignored=0&bug_severity=--&bug_status=UNCONFIRMED&bug_type=defect&cf_fission_milestone=---&cf_fx_iteration=---&cf_fx_points=---&cf_root_cause=---&cf_status_firefox77=---&cf_status_firefox78=---&cf_status_firefox79=---&cf_status_firefox_esr68=---&cf_status_firefox_esr78=---&cf_status_thunderbird_esr60=---&cf_status_thunderbird_esr68=---&cf_tracking_firefox77=---&cf_tracking_firefox78=---&cf_tracking_firefox79=---&cf_tracking_firefox_esr68=---&cf_tracking_firefox_esr78=---&cf_tracking_firefox_relnote=---&cf_tracking_thunderbird_esr60=---&cf_tracking_thunderbird_esr68=---&component=Lint%20and%20Formatting&contenttypemethod=list&contenttypeselection=text%2Fplain&defined_groups=1&filed_via=standard_form&flag_type-203=X&flag_type-37=X&flag_type-4=X&flag_type-41=X&flag_type-607=X&flag_type-721=X&flag_type-737=X&flag_type-787=X&flag_type-799=X&flag_type-800=X&flag_type-803=X&flag_type-846=X&flag_type-855=X&flag_type-864=X&flag_type-913=X&flag_type-930=X&flag_type-936=X&flag_type-941=X&flag_type-945=X&form_name=enter_bug&groups=firefox-core-security&maketemplate=Remember%20values%20as%20bookmarkable%20template&op_sys=Unspecified&priority=--&product=Firefox%20Build%20System&rep_platform=Unspecified&status_whiteboard=%5Beslint-plugin-no-unsanitized%5D&target_milestone=---&version=unspecified
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
The plugin allows a limit back-tracing of variables.
|
||||
This will be used to check code like here:
|
||||
|
||||
```js
|
||||
const greeting_template = `<p>Hello World!</p>`;
|
||||
// ... lots of other code in between ...
|
||||
|
@ -19,28 +20,29 @@ option `variableTracing` to `false`.**
|
|||
Both values are supported and tested in CI.
|
||||
|
||||
## Customization Examples
|
||||
You can customize the way this rule works in various ways.
|
||||
* Add to the list of properties or functions to be checked for potentially
|
||||
dangers variable input
|
||||
* Add to the list of allowed escaping functions to mitigate security concerns
|
||||
* Besides adding to the list, you may override the defaults and provide an exhaustive list yourself
|
||||
|
||||
You can customize the way this rule works in various ways.
|
||||
|
||||
- Add to the list of properties or functions to be checked for potentially
|
||||
dangers variable input
|
||||
- Add to the list of allowed escaping functions to mitigate security concerns
|
||||
- Besides adding to the list, you may override the defaults and provide an exhaustive list yourself
|
||||
|
||||
### Disallow the `html` function by specifically checking input for the first function parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": {
|
||||
"rules": {
|
||||
"no-unsanitized/method": [
|
||||
"error",
|
||||
{
|
||||
},
|
||||
{},
|
||||
{
|
||||
"html": {
|
||||
"properties": [0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -81,10 +83,10 @@ dangers variable input
|
|||
}
|
||||
```
|
||||
|
||||
### Override list of escaping functions for property assignments only
|
||||
### Override list of escaping functions for property assignments only
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
#### More
|
||||
* See [our rule schema definition](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/main/SCHEMA.md).
|
||||
|
||||
- See [our rule schema definition](https://github.com/mozilla/eslint-plugin-no-unsanitized/blob/main/SCHEMA.md).
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
# Fixing Rule Violations
|
||||
|
||||
The default configuration will allow your code to pass if it ensures
|
||||
that all user input is properly escaped.
|
||||
Using the [sanitizer.js](https://github.com/fxos-eng/sanitizer)
|
||||
Using the [sanitizer.js](https://github.com/fxos-eng/sanitizer)
|
||||
library your code would look like this:
|
||||
|
||||
```js
|
||||
// example for no-unsanitized/property
|
||||
foo.innerHTML = Sanitizer.escapeHTML`<a href="${link}">click</a>`
|
||||
foo.innerHTML = Sanitizer.escapeHTML`<a href="${link}">click</a>`;
|
||||
|
||||
// example for no-unsanitized/method
|
||||
node.insertAdjacentHTML('afterend', Sanitizer.escapeHTML`<a href="${link}">click</a>`);
|
||||
node.insertAdjacentHTML(
|
||||
"afterend",
|
||||
Sanitizer.escapeHTML`<a href="${link}">click</a>`
|
||||
);
|
||||
```
|
||||
|
||||
## Wrapping & Unwrapping
|
||||
|
||||
If you need to generate your HTML somewhere else and e.g. cache it,
|
||||
you won't be able to run `escapeHTML` on a string that knows no
|
||||
distinction between HTML and user inputs.
|
||||
|
@ -22,21 +27,23 @@ allowed for direct innerHTML assignments (and insertAdjacentHTML
|
|||
calls): `createSafeHTML` and `unwrapSafeHTML`
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
// create the HTML object for later usage
|
||||
function storeGreeting(username) {
|
||||
var safeHTML = Sanitizer.createSafeHTML`<p>Hello ${username}</p>`;
|
||||
cache.store('greeting', safeHTML)
|
||||
var safeHTML = Sanitizer.createSafeHTML`<p>Hello ${username}</p>`;
|
||||
cache.store("greeting", safeHTML);
|
||||
}
|
||||
|
||||
// re-use the existing safe-HTML object
|
||||
function useGreeting(domNode) {
|
||||
var htmlObj = cache.retrieve('greeting');
|
||||
domNode.innerHTML = Sanitizer.unwrapSafeHTML(htmlObj);
|
||||
var htmlObj = cache.retrieve("greeting");
|
||||
domNode.innerHTML = Sanitizer.unwrapSafeHTML(htmlObj);
|
||||
}
|
||||
```
|
||||
|
||||
# That still does not solve my problem
|
||||
|
||||
It might very well be the case that there's a bug in our linter rule.
|
||||
|
||||
[Please file an issue.](https://github.com/mozilla/eslint-plugin-no-unsanitized/issues/new)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# method
|
||||
The *method* rule in *eslint-plugin-no-unsanitized* perform basic security
|
||||
|
||||
The _method_ rule in _eslint-plugin-no-unsanitized_ perform basic security
|
||||
checks for function calls. The idea of these checks is to ensure that certain insecure
|
||||
coding patterns are avoided in your codebase. We encourage developers
|
||||
to use HTML sanitizers or escapers to mitigate those insecure patterns.
|
||||
|
||||
## Unsafe call to insertAdjacentHTML, document.write or document.writeln
|
||||
|
||||
This error message suggests that you are using an unsafe coding
|
||||
pattern. Please do not simply call functions like `insertAdjacentHTML` with a
|
||||
variable parameter, as this might cause Cross-Site Scripting (XSS)
|
||||
|
@ -12,6 +14,7 @@ vulnerabilities. We encourage you to construct DOM nodes using `createElement`
|
|||
and changing their attributes (e.g., `textContent`, `classList`) instead.
|
||||
|
||||
### Further Reading
|
||||
* Advanced guidance on [Fixing rule violations](fixing-violations.md)
|
||||
* This rule has some [customization](customization.md) options that allow you
|
||||
to add or remove functions that should not be called
|
||||
|
||||
- Advanced guidance on [Fixing rule violations](fixing-violations.md)
|
||||
- This rule has some [customization](customization.md) options that allow you
|
||||
to add or remove functions that should not be called
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# property
|
||||
The *property* rule in *eslint-plugin-no-unsanitized* perform basic security
|
||||
|
||||
The _property_ rule in _eslint-plugin-no-unsanitized_ perform basic security
|
||||
checks for property assignments. The idea of these checks is to ensure that
|
||||
certain insecure coding patterns are avoided in your codebase. We encourage
|
||||
developers to use HTML sanitizers or escapers to mitigate those insecure
|
||||
patterns.
|
||||
|
||||
## Unsafe assignment to innerHTML or outerHTML
|
||||
## Unsafe assignment to innerHTML or outerHTML
|
||||
|
||||
This error message suggests that you are using an unsafe coding
|
||||
pattern. Please do not simply assign variables to `innertHTML`,
|
||||
as this might cause Cross-Site Scripting (XSS) vulnerabilities.
|
||||
|
@ -13,6 +15,7 @@ We encourage you to construct DOM nodes using `createElement`
|
|||
and changing their attributes (e.g., `textContent`, `classList`) instead.
|
||||
|
||||
### Further Reading
|
||||
* Advanced guidance on [Fixing rule violations](fixing-violations.md)
|
||||
* This rule has some [customization](customization.md) options that allow you
|
||||
to add or remove functions that should not be called
|
||||
|
||||
- Advanced guidance on [Fixing rule violations](fixing-violations.md)
|
||||
- This rule has some [customization](customization.md) options that allow you
|
||||
to add or remove functions that should not be called
|
||||
|
|
40
index.js
40
index.js
|
@ -1,7 +1,7 @@
|
|||
module.exports = {
|
||||
rules: {
|
||||
"property": require("./lib/rules/property"),
|
||||
"method": require("./lib/rules/method")
|
||||
property: require("./lib/rules/property"),
|
||||
method: require("./lib/rules/method"),
|
||||
},
|
||||
configs: {
|
||||
DOM: {
|
||||
|
@ -9,46 +9,38 @@ module.exports = {
|
|||
rules: {
|
||||
"no-unsanitized/property": [
|
||||
"error",
|
||||
{},
|
||||
{
|
||||
},
|
||||
{
|
||||
|
||||
// Check unsafe assignment to innerHTML
|
||||
innerHTML: {},
|
||||
|
||||
// Check unsafe assignment to outerHTML
|
||||
outerHTML: {},
|
||||
}
|
||||
},
|
||||
],
|
||||
"no-unsanitized/method": [
|
||||
"error",
|
||||
{},
|
||||
{
|
||||
},
|
||||
{
|
||||
|
||||
// check second parameter to .insertAdjacentHTML()
|
||||
insertAdjacentHTML: {
|
||||
properties: [1]
|
||||
properties: [1],
|
||||
},
|
||||
|
||||
// check first parameter to .write(), as long as the preceeding object matches the regex "document"
|
||||
write: {
|
||||
objectMatches: [
|
||||
"document"
|
||||
],
|
||||
properties: [0]
|
||||
objectMatches: ["document"],
|
||||
properties: [0],
|
||||
},
|
||||
|
||||
// check first parameter to .writeLn(), as long as the preceeding object matches the regex "document"
|
||||
writeln: {
|
||||
objectMatches: [
|
||||
"document"
|
||||
],
|
||||
properties: [0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
objectMatches: ["document"],
|
||||
properties: [0],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -49,53 +49,83 @@ RuleHelper.prototype = {
|
|||
an inner/outerHTML assignment or the 2nd parameter to insertAdjacentTML
|
||||
*/
|
||||
|
||||
|
||||
switch(expression.type) {
|
||||
case "Literal":
|
||||
/* surely, someone could have an evil literal in there, but that"s malice
|
||||
switch (expression.type) {
|
||||
case "Literal":
|
||||
/* surely, someone could have an evil literal in there, but that"s malice
|
||||
we can just check for unsafe coding practice, not outright malice
|
||||
example literal "<script>eval(location.hash.slice(1)</script>"
|
||||
(it"s the task of the tagger-function to be the gateway here.)
|
||||
*/
|
||||
allowed = true;
|
||||
break;
|
||||
case "TemplateElement":
|
||||
// Raw text from a template
|
||||
allowed = true;
|
||||
break;
|
||||
case "TemplateLiteral":
|
||||
|
||||
// check only the ${..} expressions
|
||||
allowed = this.allowedExpression(expression.expressions, escapeObject, details);
|
||||
break;
|
||||
case "TaggedTemplateExpression":
|
||||
allowed = this.isAllowedCallExpression(expression.tag, escapeObject.taggedTemplates || VALID_ESCAPERS);
|
||||
break;
|
||||
case "CallExpression":
|
||||
allowed = this.isAllowedCallExpression(expression.callee, escapeObject.methods || VALID_UNWRAPPERS);
|
||||
break;
|
||||
case "BinaryExpression":
|
||||
allowed = ((this.allowedExpression(expression.left, escapeObject, details))
|
||||
&& (this.allowedExpression(expression.right, escapeObject, details)));
|
||||
break;
|
||||
case "TSAsExpression":
|
||||
// TSAsExpressions contain the raw javascript value in 'expression'
|
||||
allowed = this.allowedExpression(expression.expression, escapeObject, details);
|
||||
break;
|
||||
case "TypeCastExpression":
|
||||
allowed = this.allowedExpression(expression.expression, escapeObject, details);
|
||||
break;
|
||||
case "Identifier":
|
||||
allowed = this.isAllowedIdentifier(expression, escapeObject, details);
|
||||
break;
|
||||
default:
|
||||
|
||||
// everything that doesn't match is considered unsafe:
|
||||
allowed = false;
|
||||
break;
|
||||
allowed = true;
|
||||
break;
|
||||
case "TemplateElement":
|
||||
// Raw text from a template
|
||||
allowed = true;
|
||||
break;
|
||||
case "TemplateLiteral":
|
||||
// check only the ${..} expressions
|
||||
allowed = this.allowedExpression(
|
||||
expression.expressions,
|
||||
escapeObject,
|
||||
details
|
||||
);
|
||||
break;
|
||||
case "TaggedTemplateExpression":
|
||||
allowed = this.isAllowedCallExpression(
|
||||
expression.tag,
|
||||
escapeObject.taggedTemplates || VALID_ESCAPERS
|
||||
);
|
||||
break;
|
||||
case "CallExpression":
|
||||
allowed = this.isAllowedCallExpression(
|
||||
expression.callee,
|
||||
escapeObject.methods || VALID_UNWRAPPERS
|
||||
);
|
||||
break;
|
||||
case "BinaryExpression":
|
||||
allowed =
|
||||
this.allowedExpression(
|
||||
expression.left,
|
||||
escapeObject,
|
||||
details
|
||||
) &&
|
||||
this.allowedExpression(
|
||||
expression.right,
|
||||
escapeObject,
|
||||
details
|
||||
);
|
||||
break;
|
||||
case "TSAsExpression":
|
||||
// TSAsExpressions contain the raw javascript value in 'expression'
|
||||
allowed = this.allowedExpression(
|
||||
expression.expression,
|
||||
escapeObject,
|
||||
details
|
||||
);
|
||||
break;
|
||||
case "TypeCastExpression":
|
||||
allowed = this.allowedExpression(
|
||||
expression.expression,
|
||||
escapeObject,
|
||||
details
|
||||
);
|
||||
break;
|
||||
case "Identifier":
|
||||
allowed = this.isAllowedIdentifier(
|
||||
expression,
|
||||
escapeObject,
|
||||
details
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// everything that doesn't match is considered unsafe:
|
||||
allowed = false;
|
||||
break;
|
||||
}
|
||||
if (Array.isArray(expression)) {
|
||||
allowed = expression.every((e) => this.allowedExpression(e, escapeObject, details));
|
||||
allowed = expression.every((e) =>
|
||||
this.allowedExpression(e, escapeObject, details)
|
||||
);
|
||||
}
|
||||
return allowed;
|
||||
},
|
||||
|
@ -115,7 +145,6 @@ RuleHelper.prototype = {
|
|||
* @returns {boolean} Returns whether the Identifier is deemed safe.
|
||||
*/
|
||||
isAllowedIdentifier(expression, escapeObject, details) {
|
||||
|
||||
// respect the custom config property `variableTracing`:
|
||||
if (!this.ruleChecks["variableTracing"]) {
|
||||
return false;
|
||||
|
@ -127,11 +156,13 @@ RuleHelper.prototype = {
|
|||
let allowed = false;
|
||||
|
||||
// If we can't get info on the variable, we just can't allow it
|
||||
if (!variableInfo ||
|
||||
if (
|
||||
!variableInfo ||
|
||||
!variableInfo.defs ||
|
||||
variableInfo.defs.length == 0 ||
|
||||
!variableInfo.references ||
|
||||
variableInfo.references.length == 0) {
|
||||
variableInfo.references.length == 0
|
||||
) {
|
||||
// FIXME Fix/Adjust towards a helpful message here and update tests accordingly.
|
||||
// details.message = `Variable ${expression.name} considered unsafe: variable initialization not found`;
|
||||
return false;
|
||||
|
@ -144,9 +175,12 @@ RuleHelper.prototype = {
|
|||
// identifier wasn't declared as a variable
|
||||
// e.g., it shows up as a parameter to an
|
||||
// ArrowFunctionExpression, FunctionDeclaration or FunctionExpression
|
||||
const {line, column} = def.node.loc.start;
|
||||
if ((def.node.type === "FunctionDeclaration") || (def.node.type == "ArrowFunctionExpression") || (def.node.type === "FunctionExpression"))
|
||||
{
|
||||
const { line, column } = def.node.loc.start;
|
||||
if (
|
||||
def.node.type === "FunctionDeclaration" ||
|
||||
def.node.type == "ArrowFunctionExpression" ||
|
||||
def.node.type === "FunctionExpression"
|
||||
) {
|
||||
details.message = `Variable '${expression.name}' declared as function parameter, which is considered unsafe. '${def.node.type}' at ${line}:${column}`;
|
||||
} else {
|
||||
details.message = `Variable '${expression.name}' initialized with unknown declaration '${def.node.type}' at ${line}:${column}`;
|
||||
|
@ -154,7 +188,7 @@ RuleHelper.prototype = {
|
|||
definedAsAllowed = false;
|
||||
break;
|
||||
}
|
||||
if ((def.kind !== "let") && (def.kind !== "const")) {
|
||||
if (def.kind !== "let" && def.kind !== "const") {
|
||||
// We do not allow for identifiers declared with "var", as they can be overridden in a
|
||||
// way that is hard for us to follow (e.g., assignments to globalThis[theirNameAsString]).
|
||||
definedAsAllowed = false;
|
||||
|
@ -165,11 +199,14 @@ RuleHelper.prototype = {
|
|||
const varInitAs = def.node.init;
|
||||
|
||||
// When the variable is only declared but not initialized, `init` is `null`.
|
||||
if (varInitAs && !this.allowedExpression(varInitAs, escapeObject, details)) {
|
||||
if (
|
||||
varInitAs &&
|
||||
!this.allowedExpression(varInitAs, escapeObject, details)
|
||||
) {
|
||||
// if one variable definition is considered unsafe, all are.
|
||||
// NB: order of definition is unclear. See issue #168.
|
||||
if (!details.message) {
|
||||
const {line, column} = varInitAs.loc.start;
|
||||
const { line, column } = varInitAs.loc.start;
|
||||
details.message = `Variable '${expression.name}' initialized with unsafe value at ${line}:${column}`;
|
||||
}
|
||||
definedAsAllowed = false;
|
||||
|
@ -186,7 +223,10 @@ RuleHelper.prototype = {
|
|||
|
||||
// With no write variable references, if it was defined as allowed
|
||||
// then we should consider it safe.
|
||||
if (variableInfo.references.filter(ref => ref.isWrite()).length === 0) {
|
||||
if (
|
||||
variableInfo.references.filter((ref) => ref.isWrite())
|
||||
.length === 0
|
||||
) {
|
||||
allWritingRefsAllowed = true;
|
||||
}
|
||||
|
||||
|
@ -198,9 +238,15 @@ RuleHelper.prototype = {
|
|||
// if one is unsafe we'll consider all unsafe.
|
||||
// this is because code occurring doesn't guarantee it being executed
|
||||
// due to dynamic behavior if-conditions and such
|
||||
if (!this.allowedExpression(writeExpr, escapeObject, details)) {
|
||||
if (
|
||||
!this.allowedExpression(
|
||||
writeExpr,
|
||||
escapeObject,
|
||||
details
|
||||
)
|
||||
) {
|
||||
if (!details.message) {
|
||||
const {line, column} = writeExpr.loc.start;
|
||||
const { line, column } = writeExpr.loc.start;
|
||||
details.message = `Variable '${expression.name}' reassigned with unsafe value at ${line}:${column}`;
|
||||
}
|
||||
allWritingRefsAllowed = false;
|
||||
|
@ -244,30 +290,35 @@ RuleHelper.prototype = {
|
|||
let methodName;
|
||||
let objectName;
|
||||
switch (node.type) {
|
||||
case "Identifier":
|
||||
methodName = node.name;
|
||||
break;
|
||||
case "MemberExpression":
|
||||
methodName = node.property.name;
|
||||
objectName = node.object.name || this.context.getSource(node.object);
|
||||
break;
|
||||
case "ConditionalExpression":
|
||||
case "CallExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
methodName = "";
|
||||
break;
|
||||
case "AssignmentExpression":
|
||||
methodName = this.normalizeMethodCall(node.right);
|
||||
break;
|
||||
case "Import":
|
||||
methodName = "import";
|
||||
break;
|
||||
default:
|
||||
this.reportUnsupported(node, "Unexpected callable", `unexpected ${node.type} in normalizeMethodCall`);
|
||||
case "Identifier":
|
||||
methodName = node.name;
|
||||
break;
|
||||
case "MemberExpression":
|
||||
methodName = node.property.name;
|
||||
objectName =
|
||||
node.object.name || this.context.getSource(node.object);
|
||||
break;
|
||||
case "ConditionalExpression":
|
||||
case "CallExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
methodName = "";
|
||||
break;
|
||||
case "AssignmentExpression":
|
||||
methodName = this.normalizeMethodCall(node.right);
|
||||
break;
|
||||
case "Import":
|
||||
methodName = "import";
|
||||
break;
|
||||
default:
|
||||
this.reportUnsupported(
|
||||
node,
|
||||
"Unexpected callable",
|
||||
`unexpected ${node.type} in normalizeMethodCall`
|
||||
);
|
||||
}
|
||||
return {
|
||||
objectName,
|
||||
methodName
|
||||
methodName,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -302,8 +353,11 @@ RuleHelper.prototype = {
|
|||
let matched = false;
|
||||
|
||||
// Allow methods named "import":
|
||||
if (normalizedMethodCall.methodName === "import"
|
||||
&& node.callee && node.callee.type === "MemberExpression") {
|
||||
if (
|
||||
normalizedMethodCall.methodName === "import" &&
|
||||
node.callee &&
|
||||
node.callee.type === "MemberExpression"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -319,7 +373,10 @@ RuleHelper.prototype = {
|
|||
}
|
||||
|
||||
// If we do have object filters and the call is a function then it should not be checked
|
||||
if ("objectName" in normalizedMethodCall && normalizedMethodCall.objectName) {
|
||||
if (
|
||||
"objectName" in normalizedMethodCall &&
|
||||
normalizedMethodCall.objectName
|
||||
) {
|
||||
for (const objectMatch of objectMatches) {
|
||||
const match = new RegExp(objectMatch, "gi");
|
||||
if (normalizedMethodCall.objectName.match(match)) {
|
||||
|
@ -344,31 +401,46 @@ RuleHelper.prototype = {
|
|||
let childRuleChecks = Object.assign({}, this.context.options[1]);
|
||||
const ruleCheckOutput = {};
|
||||
|
||||
if (!("defaultDisable" in parentRuleChecks)
|
||||
|| !parentRuleChecks.defaultDisable) {
|
||||
childRuleChecks = Object.assign({}, defaultRuleChecks, childRuleChecks);
|
||||
if (
|
||||
!("defaultDisable" in parentRuleChecks) ||
|
||||
!parentRuleChecks.defaultDisable
|
||||
) {
|
||||
childRuleChecks = Object.assign(
|
||||
{},
|
||||
defaultRuleChecks,
|
||||
childRuleChecks
|
||||
);
|
||||
}
|
||||
|
||||
// default to variable back tracing enabled.
|
||||
ruleCheckOutput["variableTracing"] = true;
|
||||
if ("variableTracing" in parentRuleChecks) {
|
||||
ruleCheckOutput["variableTracing"] = !!parentRuleChecks["variableTracing"];
|
||||
ruleCheckOutput["variableTracing"] =
|
||||
!!parentRuleChecks["variableTracing"];
|
||||
}
|
||||
|
||||
// If we have defined child rules lets ignore default rules
|
||||
Object.keys(childRuleChecks).forEach((ruleCheckKey) => {
|
||||
// However if they have missing keys merge with default
|
||||
const ruleCheck = Object.assign(
|
||||
"defaultDisable" in parentRuleChecks ? {} :
|
||||
{
|
||||
escape: {
|
||||
taggedTemplates: ["Sanitizer.escapeHTML", "escapeHTML"],
|
||||
methods: ["Sanitizer.unwrapSafeHTML", "unwrapSafeHTML"]
|
||||
}
|
||||
},
|
||||
"defaultDisable" in parentRuleChecks
|
||||
? {}
|
||||
: {
|
||||
escape: {
|
||||
taggedTemplates: [
|
||||
"Sanitizer.escapeHTML",
|
||||
"escapeHTML",
|
||||
],
|
||||
methods: [
|
||||
"Sanitizer.unwrapSafeHTML",
|
||||
"unwrapSafeHTML",
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultRuleChecks[ruleCheckKey],
|
||||
parentRuleChecks,
|
||||
childRuleChecks[ruleCheckKey]);
|
||||
childRuleChecks[ruleCheckKey]
|
||||
);
|
||||
ruleCheckOutput[ruleCheckKey] = ruleCheck;
|
||||
});
|
||||
|
||||
|
@ -387,7 +459,10 @@ RuleHelper.prototype = {
|
|||
if (Object.prototype.hasOwnProperty.call(this.ruleChecks, methodName)) {
|
||||
const ruleCheck = this.ruleChecks[methodName];
|
||||
if (!Array.isArray(ruleCheck.properties)) {
|
||||
this.context.report(node, `Method check requires properties array in eslint rule ${methodName}`);
|
||||
this.context.report(
|
||||
node,
|
||||
`Method check requires properties array in eslint rule ${methodName}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
ruleCheck.properties.forEach((propertyId) => {
|
||||
|
@ -398,15 +473,23 @@ RuleHelper.prototype = {
|
|||
return;
|
||||
}
|
||||
const details = {};
|
||||
if (this.shouldCheckMethodCall(node, ruleCheck.objectMatches)
|
||||
&& !this.allowedExpression(argument, ruleCheck.escape, details)) {
|
||||
if (
|
||||
this.shouldCheckMethodCall(node, ruleCheck.objectMatches) &&
|
||||
!this.allowedExpression(argument, ruleCheck.escape, details)
|
||||
) {
|
||||
// Include the additional details if available (e.g. name of a disallowed variable
|
||||
// and the position of the expression that made it disallowed).
|
||||
if (details.message) {
|
||||
this.context.report(node, `Unsafe call to ${this.getCodeName(node.callee)} for argument ${propertyId} (${details.message})`);
|
||||
this.context.report(
|
||||
node,
|
||||
`Unsafe call to ${this.getCodeName(node.callee)} for argument ${propertyId} (${details.message})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.context.report(node, `Unsafe call to ${this.getCodeName(node.callee)} for argument ${propertyId}`);
|
||||
this.context.report(
|
||||
node,
|
||||
`Unsafe call to ${this.getCodeName(node.callee)} for argument ${propertyId}`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -418,26 +501,41 @@ RuleHelper.prototype = {
|
|||
* @returns {undefined} Does not return
|
||||
*/
|
||||
checkProperty(node) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.ruleChecks, node.left.property.name)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
this.ruleChecks,
|
||||
node.left.property.name
|
||||
)
|
||||
) {
|
||||
const ruleCheck = this.ruleChecks[node.left.property.name];
|
||||
const details = {};
|
||||
if (!this.allowedExpression(node.right, ruleCheck.escape, details)) {
|
||||
if (
|
||||
!this.allowedExpression(node.right, ruleCheck.escape, details)
|
||||
) {
|
||||
// Include the additional details if available (e.g. name of a disallowed variable
|
||||
// and the position of the expression that made it disallowed).
|
||||
if (details.message) {
|
||||
this.context.report(node, `Unsafe assignment to ${node.left.property.name} (${details.message})`);
|
||||
this.context.report(
|
||||
node,
|
||||
`Unsafe assignment to ${node.left.property.name} (${details.message})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.context.report(node, `Unsafe assignment to ${node.left.property.name}`);
|
||||
this.context.report(
|
||||
node,
|
||||
`Unsafe assignment to ${node.left.property.name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reportUnsupported(node, reason, errorTitle) {
|
||||
const bugPath = `https://github.com/mozilla/eslint-plugin-no-unsanitized/issues/new?title=${encodeURIComponent(errorTitle)}`;
|
||||
this.context.report(node, `Error in no-unsanitized: ${reason}. Please report a minimal code snippet to the developers at ${bugPath}`);
|
||||
}
|
||||
|
||||
this.context.report(
|
||||
node,
|
||||
`Error in no-unsanitized: ${reason}. Please report a minimal code snippet to the developers at ${bugPath}`
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = RuleHelper;
|
||||
|
|
|
@ -11,45 +11,38 @@ const RuleHelper = require("../ruleHelper");
|
|||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
const defaultRuleChecks = {
|
||||
|
||||
// check second parameter to .insertAdjacentHTML()
|
||||
insertAdjacentHTML: {
|
||||
properties: [1]
|
||||
properties: [1],
|
||||
},
|
||||
|
||||
|
||||
// check first parameter of import()
|
||||
import: {
|
||||
properties: [0]
|
||||
properties: [0],
|
||||
},
|
||||
|
||||
// check first parameter to createContextualFragment()
|
||||
createContextualFragment: {
|
||||
properties: [0]
|
||||
properties: [0],
|
||||
},
|
||||
|
||||
// check first parameter to .write(), as long as the preceeding object matches the regex "document"
|
||||
write: {
|
||||
objectMatches: [
|
||||
"document"
|
||||
],
|
||||
properties: [0]
|
||||
objectMatches: ["document"],
|
||||
properties: [0],
|
||||
},
|
||||
|
||||
// check first parameter to .writeLn(), as long as the preceeding object matches the regex "document"
|
||||
writeln: {
|
||||
objectMatches: [
|
||||
"document"
|
||||
],
|
||||
properties: [0]
|
||||
objectMatches: ["document"],
|
||||
properties: [0],
|
||||
},
|
||||
|
||||
// check first parameter to `setHTMLUnsafe()`
|
||||
setHTMLUnsafe: {
|
||||
properties: [0]
|
||||
}
|
||||
properties: [0],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -59,7 +52,10 @@ const defaultRuleChecks = {
|
|||
* @returns {undefined} Does not return
|
||||
*/
|
||||
function checkImport(ruleHelper, importExpr) {
|
||||
const fakeCall = {callee: {type: "Import"}, arguments: [importExpr.source]};
|
||||
const fakeCall = {
|
||||
callee: { type: "Import" },
|
||||
arguments: [importExpr.source],
|
||||
};
|
||||
Object.assign(fakeCall, importExpr);
|
||||
ruleHelper.checkMethod(fakeCall);
|
||||
}
|
||||
|
@ -72,84 +68,88 @@ function checkImport(ruleHelper, importExpr) {
|
|||
* @returns {undefined} Does not return
|
||||
*/
|
||||
function checkCallExpression(ruleHelper, callExpr, node) {
|
||||
switch(node.type) {
|
||||
case "Identifier":
|
||||
case "MemberExpression":
|
||||
if (callExpr.arguments && callExpr.arguments.length > 0) {
|
||||
ruleHelper.checkMethod(callExpr);
|
||||
}
|
||||
break;
|
||||
switch (node.type) {
|
||||
case "Identifier":
|
||||
case "MemberExpression":
|
||||
if (callExpr.arguments && callExpr.arguments.length > 0) {
|
||||
ruleHelper.checkMethod(callExpr);
|
||||
}
|
||||
break;
|
||||
|
||||
case "TSNonNullExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.expression;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.expression);
|
||||
break;
|
||||
}
|
||||
|
||||
case "TaggedTemplateExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.tag;
|
||||
const expressions = node.quasi.expressions;
|
||||
const strings = node.quasi.quasis;
|
||||
newCallExpr.arguments = [strings, ...expressions];
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case "TypeCastExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.expression;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.expression);
|
||||
break;
|
||||
}
|
||||
|
||||
case "AssignmentExpression":
|
||||
if (node.right.type === "MemberExpression") {
|
||||
case "TSNonNullExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.right;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.right);
|
||||
newCallExpr.callee = node.expression;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.expression);
|
||||
break;
|
||||
}
|
||||
checkCallExpression(ruleHelper, callExpr, node.right);
|
||||
break;
|
||||
|
||||
case "Import":
|
||||
ruleHelper.checkMethod(callExpr);
|
||||
break;
|
||||
case "TaggedTemplateExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.tag;
|
||||
const expressions = node.quasi.expressions;
|
||||
const strings = node.quasi.quasis;
|
||||
newCallExpr.arguments = [strings, ...expressions];
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case "SequenceExpression": {
|
||||
// the return value of a SequenceExpression is the last expression.
|
||||
// So, we create a new mock CallExpression with the actually called
|
||||
// ... expression as the callee node and pass it to checkMethod()
|
||||
case "TypeCastExpression": {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.expression;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.expression);
|
||||
break;
|
||||
}
|
||||
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
const idx = node.expressions.length - 1;
|
||||
const called = node.expressions[idx];
|
||||
newCallExpr.callee = called;
|
||||
ruleHelper.checkMethod(newCallExpr);
|
||||
break;
|
||||
}
|
||||
case "AssignmentExpression":
|
||||
if (node.right.type === "MemberExpression") {
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
newCallExpr.callee = node.right;
|
||||
checkCallExpression(ruleHelper, newCallExpr, node.right);
|
||||
break;
|
||||
}
|
||||
checkCallExpression(ruleHelper, callExpr, node.right);
|
||||
break;
|
||||
|
||||
case "TSAsExpression":
|
||||
break;
|
||||
case "Import":
|
||||
ruleHelper.checkMethod(callExpr);
|
||||
break;
|
||||
|
||||
// those are fine:
|
||||
case "LogicalExpression": // Should we scan these? issue #62.
|
||||
case "ConditionalExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression":
|
||||
case "Super":
|
||||
case "CallExpression":
|
||||
case "ThisExpression":
|
||||
case "NewExpression":
|
||||
case "TSTypeAssertion":
|
||||
case "AwaitExpression": // see issue #122
|
||||
break;
|
||||
case "SequenceExpression": {
|
||||
// the return value of a SequenceExpression is the last expression.
|
||||
// So, we create a new mock CallExpression with the actually called
|
||||
// ... expression as the callee node and pass it to checkMethod()
|
||||
|
||||
// If we don't cater for this expression throw an error
|
||||
default:
|
||||
ruleHelper.reportUnsupported(node, "Unexpected Callee", `Unsupported Callee of type ${node.type} for CallExpression`);
|
||||
const newCallExpr = Object.assign({}, callExpr);
|
||||
const idx = node.expressions.length - 1;
|
||||
const called = node.expressions[idx];
|
||||
newCallExpr.callee = called;
|
||||
ruleHelper.checkMethod(newCallExpr);
|
||||
break;
|
||||
}
|
||||
|
||||
case "TSAsExpression":
|
||||
break;
|
||||
|
||||
// those are fine:
|
||||
case "LogicalExpression": // Should we scan these? issue #62.
|
||||
case "ConditionalExpression":
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression":
|
||||
case "Super":
|
||||
case "CallExpression":
|
||||
case "ThisExpression":
|
||||
case "NewExpression":
|
||||
case "TSTypeAssertion":
|
||||
case "AwaitExpression": // see issue #122
|
||||
break;
|
||||
|
||||
// If we don't cater for this expression throw an error
|
||||
default:
|
||||
ruleHelper.reportUnsupported(
|
||||
node,
|
||||
"Unexpected Callee",
|
||||
`Unsupported Callee of type ${node.type} for CallExpression`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ module.exports = {
|
|||
docs: {
|
||||
description: "ESLint rule to disallow unsanitized method calls",
|
||||
category: "possible-errors",
|
||||
url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/method.md"
|
||||
url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/method.md",
|
||||
},
|
||||
/* schema statement TBD until we have options
|
||||
schema: [
|
||||
|
@ -190,5 +190,5 @@ module.exports = {
|
|||
checkCallExpression(ruleHelper, newCallExpr, node.tag);
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -11,25 +11,22 @@ const RuleHelper = require("../ruleHelper");
|
|||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
const defaultRuleChecks = {
|
||||
|
||||
// Check unsafe assignment to innerHTML
|
||||
innerHTML: {
|
||||
},
|
||||
innerHTML: {},
|
||||
|
||||
// Check unsafe assignment to outerHTML
|
||||
outerHTML: {
|
||||
}
|
||||
outerHTML: {},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
docs: {
|
||||
description: "ESLint rule to disallow unsanitized property assignment",
|
||||
description:
|
||||
"ESLint rule to disallow unsanitized property assignment",
|
||||
category: "possible-errors",
|
||||
url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/property.md"
|
||||
url: "https://github.com/mozilla/eslint-plugin-no-unsanitized/tree/master/docs/rules/property.md",
|
||||
},
|
||||
/* schema statement TBD until we have options
|
||||
schema: [
|
||||
|
@ -45,7 +42,19 @@ module.exports = {
|
|||
// This list explicitly doesn't check ["=", "+="] or any newer operators that have not been reviewed
|
||||
// - https://github.com/estree/estree/blob/master/es5.md#assignmentoperator
|
||||
// - https://github.com/estree/estree/blob/master/es2016.md#assignmentoperator
|
||||
const PERMITTED_OPERATORS = ["-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "|=", "^=", "&=", "**="];
|
||||
const PERMITTED_OPERATORS = [
|
||||
"-=",
|
||||
"*=",
|
||||
"/=",
|
||||
"%=",
|
||||
"<<=",
|
||||
">>=",
|
||||
">>>=",
|
||||
"|=",
|
||||
"^=",
|
||||
"&=",
|
||||
"**=",
|
||||
];
|
||||
|
||||
// operators to match against, such as X.innerHTML += foo
|
||||
const CHECK_REQUIRED_OPERATORS = ["=", "+=", "||=", "&&=", "??="];
|
||||
|
@ -57,13 +66,20 @@ module.exports = {
|
|||
if ("property" in node.left) {
|
||||
// If we don't have an operator we safely ignore
|
||||
if (PERMITTED_OPERATORS.indexOf(node.operator) === -1) {
|
||||
if (CHECK_REQUIRED_OPERATORS.indexOf(node.operator) === -1) {
|
||||
ruleHelper.reportUnsupported(node, "Unexpected Assignment", `Unsupported Operator ${encodeURIComponent(node.operator)} for AssignmentExpression`);
|
||||
if (
|
||||
CHECK_REQUIRED_OPERATORS.indexOf(node.operator) ===
|
||||
-1
|
||||
) {
|
||||
ruleHelper.reportUnsupported(
|
||||
node,
|
||||
"Unexpected Assignment",
|
||||
`Unsupported Operator ${encodeURIComponent(node.operator)} for AssignmentExpression`
|
||||
);
|
||||
}
|
||||
ruleHelper.checkProperty(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -16,8 +16,10 @@
|
|||
"@babel/plugin-syntax-flow": "^7.16.0",
|
||||
"@typescript-eslint/parser": "^5.2.0",
|
||||
"eslint": "^8.1.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"mocha": "^9.2.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "3.2.5",
|
||||
"typescript": "^3.9.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -41,7 +43,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"test": "nyc mocha tests/rules/",
|
||||
"lint": "eslint ."
|
||||
"lint": "eslint . && prettier --check ."
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
tabWidth: 4,
|
||||
trailingComma: "es5",
|
||||
};
|
|
@ -6,53 +6,41 @@
|
|||
*/
|
||||
|
||||
exports.parse = () => ({
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 5,
|
||||
"loc": {},
|
||||
"comments": [],
|
||||
"errors": [],
|
||||
"range": [
|
||||
0,
|
||||
5
|
||||
],
|
||||
"tokens": [],
|
||||
"body": [
|
||||
type: "Program",
|
||||
start: 0,
|
||||
end: 5,
|
||||
loc: {},
|
||||
comments: [],
|
||||
errors: [],
|
||||
range: [0, 5],
|
||||
tokens: [],
|
||||
body: [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"loc": {
|
||||
"start": 0,
|
||||
"end": 5
|
||||
type: "ExpressionStatement",
|
||||
loc: {
|
||||
start: 0,
|
||||
end: 5,
|
||||
},
|
||||
"range": [
|
||||
0,
|
||||
5
|
||||
],
|
||||
"expression": {
|
||||
"type": "CallExpression",
|
||||
"loc": {
|
||||
"start": 0,
|
||||
"end": 5
|
||||
range: [0, 5],
|
||||
expression: {
|
||||
type: "CallExpression",
|
||||
loc: {
|
||||
start: 0,
|
||||
end: 5,
|
||||
},
|
||||
"range": [
|
||||
0,
|
||||
5
|
||||
],
|
||||
"callee": {
|
||||
"type": "FantasyCallee",
|
||||
"loc": {
|
||||
"start": 0,
|
||||
"end": 3
|
||||
range: [0, 5],
|
||||
callee: {
|
||||
type: "FantasyCallee",
|
||||
loc: {
|
||||
start: 0,
|
||||
end: 3,
|
||||
},
|
||||
"range": [
|
||||
0,
|
||||
3
|
||||
],
|
||||
"name": "§fantasyCallee§"
|
||||
range: [0, 3],
|
||||
name: "§fantasyCallee§",
|
||||
},
|
||||
"arguments": []
|
||||
}
|
||||
}
|
||||
arguments: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
"sourceType": "module"
|
||||
sourceType: "module",
|
||||
});
|
||||
|
|
|
@ -6,133 +6,112 @@
|
|||
*/
|
||||
|
||||
exports.parse = () => ({
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 5,
|
||||
"loc": {},
|
||||
"comments": [],
|
||||
"errors": [],
|
||||
"range": [
|
||||
0,
|
||||
5
|
||||
],
|
||||
"tokens": [],
|
||||
"body": [
|
||||
type: "Program",
|
||||
start: 0,
|
||||
end: 5,
|
||||
loc: {},
|
||||
comments: [],
|
||||
errors: [],
|
||||
range: [0, 5],
|
||||
tokens: [],
|
||||
body: [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"loc": {
|
||||
"start": 0,
|
||||
"end": 5
|
||||
type: "ExpressionStatement",
|
||||
loc: {
|
||||
start: 0,
|
||||
end: 5,
|
||||
},
|
||||
"range": [
|
||||
0,
|
||||
5
|
||||
],
|
||||
"expression": {
|
||||
"type": "AssignmentExpression",
|
||||
"start": 1,
|
||||
"end": 6,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 1
|
||||
range: [0, 5],
|
||||
expression: {
|
||||
type: "AssignmentExpression",
|
||||
start: 1,
|
||||
end: 6,
|
||||
loc: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
end: {
|
||||
line: 1,
|
||||
column: 6,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
}
|
||||
},
|
||||
"range": [
|
||||
1,
|
||||
6
|
||||
],
|
||||
"operator": "§=",
|
||||
"left": {
|
||||
"type": "MemberExpression",
|
||||
"start": 1,
|
||||
"end": 4,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 1
|
||||
range: [1, 6],
|
||||
operator: "§=",
|
||||
left: {
|
||||
type: "MemberExpression",
|
||||
start: 1,
|
||||
end: 4,
|
||||
loc: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
}
|
||||
},
|
||||
"range": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"object": {
|
||||
"type": "Identifier",
|
||||
"start": 1,
|
||||
"end": 2,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 2
|
||||
},
|
||||
"identifierName": "a"
|
||||
end: {
|
||||
line: 1,
|
||||
column: 4,
|
||||
},
|
||||
"range": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "a",
|
||||
},
|
||||
"computed": false,
|
||||
"property": {
|
||||
"type": "Identifier",
|
||||
"start": 3,
|
||||
"end": 4,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 3
|
||||
range: [1, 4],
|
||||
object: {
|
||||
type: "Identifier",
|
||||
start: 1,
|
||||
end: 2,
|
||||
loc: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 4
|
||||
end: {
|
||||
line: 1,
|
||||
column: 2,
|
||||
},
|
||||
"identifierName": "innerHTML"
|
||||
identifierName: "a",
|
||||
},
|
||||
"range": [
|
||||
3,
|
||||
4
|
||||
],
|
||||
"name": "a",
|
||||
range: [1, 2],
|
||||
name: "a",
|
||||
},
|
||||
"optional": false,
|
||||
computed: false,
|
||||
property: {
|
||||
type: "Identifier",
|
||||
start: 3,
|
||||
end: 4,
|
||||
loc: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 3,
|
||||
},
|
||||
end: {
|
||||
line: 1,
|
||||
column: 4,
|
||||
},
|
||||
identifierName: "innerHTML",
|
||||
},
|
||||
range: [3, 4],
|
||||
name: "a",
|
||||
},
|
||||
optional: false,
|
||||
},
|
||||
"right": {
|
||||
"type": "Identifier",
|
||||
"start": 5,
|
||||
"end": 6,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 5
|
||||
right: {
|
||||
type: "Identifier",
|
||||
start: 5,
|
||||
end: 6,
|
||||
loc: {
|
||||
start: {
|
||||
line: 1,
|
||||
column: 5,
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
end: {
|
||||
line: 1,
|
||||
column: 6,
|
||||
},
|
||||
"identifierName": "b"
|
||||
identifierName: "b",
|
||||
},
|
||||
"range": [
|
||||
5,
|
||||
6
|
||||
],
|
||||
"name": "b",
|
||||
range: [5, 6],
|
||||
name: "b",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
],
|
||||
"sourceType": "module"
|
||||
sourceType: "module",
|
||||
});
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -17,8 +17,8 @@ const PATH_TO_TYPESCRIPT_ESLINT = `${process.cwd()}/node_modules/@typescript-esl
|
|||
const PARSER_OPTIONS_FOR_FLOW = {
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
plugins: ["@babel/plugin-syntax-flow"]
|
||||
}
|
||||
plugins: ["@babel/plugin-syntax-flow"],
|
||||
},
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -28,7 +28,6 @@ const PARSER_OPTIONS_FOR_FLOW = {
|
|||
const eslintTester = new RuleTester();
|
||||
|
||||
eslintTester.run("property", rule, {
|
||||
|
||||
// Examples of code that should not trigger the rule
|
||||
// XXX this does not find z['innerHTML'] and the like.
|
||||
|
||||
|
@ -36,131 +35,132 @@ eslintTester.run("property", rule, {
|
|||
// tests for innerHTML equals
|
||||
{
|
||||
code: "a.innerHTML = '';",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "a.innerHTML *= 'test';",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "c.innerHTML = ``;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "g.innerHTML = Sanitizer.escapeHTML``;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "h.innerHTML = Sanitizer.escapeHTML`foo`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "i.innerHTML = Sanitizer.escapeHTML`foo${bar}baz`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// tests for innerHTML update (+= operator)
|
||||
{
|
||||
code: "a.innerHTML += '';",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "b.innerHTML += \"\";",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
code: 'b.innerHTML += "";',
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "c.innerHTML += ``;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "g.innerHTML += Sanitizer.escapeHTML``;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "h.innerHTML += Sanitizer.escapeHTML`foo`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "i.innerHTML += Sanitizer.escapeHTML`foo${bar}baz`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "i.innerHTML += Sanitizer.unwrapSafeHTML(htmlSnippet)",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "i.outerHTML += Sanitizer.unwrapSafeHTML(htmlSnippet)",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let c; c = 123; a.innerHTML = `${c}`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let c; a.innerHTML = `${c}`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
|
||||
// (binary) expressions
|
||||
{
|
||||
code: "x.innerHTML = `foo`+`bar`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "y.innerHTML = '<span>' + 5 + '</span>';",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "document.writeln(Sanitizer.escapeHTML`<em>${evil}</em>`);",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// template string expression tests
|
||||
{
|
||||
code: "u.innerHTML = `<span>${'lulz'}</span>`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "v.innerHTML = `<span>${'lulz'}</span>${55}`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "w.innerHTML = `<span>${'lulz'+'meh'}</span>`;",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// testing unwrapSafeHTML spread
|
||||
{
|
||||
code: "this.imeList.innerHTML = Sanitizer.unwrapSafeHTML(...listHtml);",
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// Native method (Check customize code doesn't include these)
|
||||
{
|
||||
code: "document.toString = evil;"
|
||||
code: "document.toString = evil;",
|
||||
},
|
||||
{ // issue 108: adding tests for custom escaper
|
||||
{
|
||||
// issue 108: adding tests for custom escaper
|
||||
code: "w.innerHTML = templateEscaper`<em>${evil}</em>`;",
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
options: [
|
||||
{
|
||||
escape: {
|
||||
taggedTemplates: ["templateEscaper"]
|
||||
}
|
||||
}
|
||||
]
|
||||
taggedTemplates: ["templateEscaper"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ // issue 108: adding tests for custom escaper
|
||||
{
|
||||
// issue 108: adding tests for custom escaper
|
||||
code: "w.innerHTML = DOMPurify.sanitize('<em>${evil}</em>');",
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
options: [
|
||||
{
|
||||
escape: {
|
||||
methods: ["DOMPurify.sanitize"]
|
||||
}
|
||||
}
|
||||
]
|
||||
methods: ["DOMPurify.sanitize"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Typescript support valid cases
|
||||
|
@ -171,7 +171,7 @@ eslintTester.run("property", rule, {
|
|||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "module",
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "(<HTMLElement>items[i](args)).innerHTML = 'rawstring';",
|
||||
|
@ -179,7 +179,7 @@ eslintTester.run("property", rule, {
|
|||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "module",
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "lol.innerHTML = (5 as string);",
|
||||
|
@ -199,10 +199,10 @@ eslintTester.run("property", rule, {
|
|||
options: [
|
||||
{
|
||||
escape: {
|
||||
methods: ["DOMPurify.sanitize"]
|
||||
}
|
||||
}
|
||||
]
|
||||
methods: ["DOMPurify.sanitize"],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Flow support cases
|
||||
|
@ -266,56 +266,58 @@ eslintTester.run("property", rule, {
|
|||
{
|
||||
code: "m.innerHTML = htmlString;",
|
||||
errors: [
|
||||
{ message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression" }
|
||||
]
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "a.innerHTML += htmlString;",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "a.innerHTML += template.toHtml();",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "m.outerHTML = htmlString;",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to outerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "t.innerHTML = `<span>${name}</span>`;",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "t.innerHTML = `<span>${'foobar'}</span>${evil}`;",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// (binary) expressions
|
||||
|
@ -324,30 +326,31 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "node.innerHTML = '<span>'+ htmlInput + '</span>';",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// bug https://bugzilla.mozilla.org/show_bug.cgi?id=1198200
|
||||
{
|
||||
code: "title.innerHTML = _('WB_LT_TIPS_S_SEARCH'," +
|
||||
" {value0:engine});",
|
||||
code:
|
||||
"title.innerHTML = _('WB_LT_TIPS_S_SEARCH'," +
|
||||
" {value0:engine});",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1192595
|
||||
|
@ -356,49 +359,49 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "x.innerHTML = Sanitizer.escapeHTML(`evil`)",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "y.innerHTML = ((arrow_function)=>null)`some HTML`",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "y.innerHTML = ((arrow_function)=>null)`some HTML`",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "y.innerHTML = ((arrow_function)=>null)`some HTML`",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// testing unwrapSafeHTML spread sanitizer typo
|
||||
|
@ -407,10 +410,10 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// the previous override for manual review and legacy code is now invalid
|
||||
|
@ -419,20 +422,20 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "function foo() { return this().innerHTML = evil; };",
|
||||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// issue 154: Adding tests for TaggedTemplateExpression callee https://jestjs.io/docs/api#2-describeeachtablename-fn-timeout
|
||||
|
@ -440,17 +443,21 @@ eslintTester.run("property", rule, {
|
|||
code: "describe.each`table${m.innerHTML = htmlString}`(name, fn, timeout)",
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
errors: [
|
||||
{ message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression" }
|
||||
]
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "describe.each`table${t.innerHTML = `<span>${name}</span>`}`(name, fn, timeout)",
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
errors: [
|
||||
{ message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression" }
|
||||
]
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Typescript support cases
|
||||
|
@ -464,9 +471,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "(x as HTMLElement).innerHTML = htmlString",
|
||||
|
@ -478,9 +485,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "lol.innerHTML = (foo as string);",
|
||||
|
@ -492,9 +499,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Flow support cases
|
||||
|
@ -505,9 +512,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "node.innerHTML = (foo: string);",
|
||||
|
@ -516,9 +523,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "(items[i](args): HTMLElement).innerHTML = unsafe;",
|
||||
|
@ -527,9 +534,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
]
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ES2020 support cases
|
||||
|
@ -538,8 +545,8 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parser: PATH_TO_BABEL_ESLINT,
|
||||
},
|
||||
|
@ -548,8 +555,8 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parser: PATH_TO_BABEL_ESLINT,
|
||||
},
|
||||
|
@ -558,8 +565,8 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parser: PATH_TO_BABEL_ESLINT,
|
||||
},
|
||||
|
@ -570,10 +577,10 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// Ensure normalizeMethodCall expects a CallExpression with a ConditionalExpression callee.
|
||||
|
@ -582,10 +589,10 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
|
||||
// Explicitly cover behavior on new unexpected operators.
|
||||
|
@ -594,30 +601,32 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: "Unsafe assignment to innerHTML",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let copies = '<b>safe</b>'; copies = suddenlyUnsafe; y.innerHTML = copies;",
|
||||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
message:
|
||||
/Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let copies = '<b>safe</b>'; if (monday) { copies = badness }; y.innerHTML = copies;",
|
||||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
message:
|
||||
/Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: `let copies = "<b>safe</b>";
|
||||
|
@ -628,11 +637,12 @@ eslintTester.run("property", rule, {
|
|||
`,
|
||||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
message:
|
||||
/Unsafe assignment to innerHTML \(Variable 'copies' reassigned with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: `let obj = { prop: "<b>safe</b>" };
|
||||
|
@ -642,34 +652,36 @@ eslintTester.run("property", rule, {
|
|||
`,
|
||||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML \(Variable 'copies' initialized with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
message:
|
||||
/Unsafe assignment to innerHTML \(Variable 'copies' initialized with unsafe value at \d+:\d+\)/,
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let msg = '<b>safe</b>'; let altMsg = 'also cool'; if (monday) { msg = altMsg; }; y.innerHTML = msg;",
|
||||
options: [
|
||||
{
|
||||
variableTracing: false
|
||||
}
|
||||
variableTracing: false,
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: "a.innerHTML §= b;",
|
||||
errors: [
|
||||
{
|
||||
message: "Error in no-unsanitized: Unexpected Assignment. Please report a minimal code snippet to the developers at https://github.com/mozilla/eslint-plugin-no-unsanitized/issues/new?title=Unsupported%20Operator%20%25C2%25A7%253D%20for%20AssignmentExpression",
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
message:
|
||||
"Error in no-unsanitized: Unexpected Assignment. Please report a minimal code snippet to the developers at https://github.com/mozilla/eslint-plugin-no-unsanitized/issues/new?title=Unsupported%20Operator%20%25C2%25A7%253D%20for%20AssignmentExpression",
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parser: require.resolve("../parsers/fantasy-operator"),
|
||||
},
|
||||
|
@ -688,10 +700,10 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
parserOptions: { ecmaVersion: 6 }
|
||||
parserOptions: { ecmaVersion: 6 },
|
||||
},
|
||||
{
|
||||
code: "let c; c = 'apparently-safe'; functionCall(c); n.innerHTML = c.property;",
|
||||
|
@ -699,9 +711,9 @@ eslintTester.run("property", rule, {
|
|||
errors: [
|
||||
{
|
||||
message: /Unsafe assignment to innerHTML/,
|
||||
type: "AssignmentExpression"
|
||||
}
|
||||
type: "AssignmentExpression",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
],
|
||||
});
|
||||
|
|
1588
yarn.lock
1588
yarn.lock
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче