зеркало из
1
0
Форкнуть 0

Switch to prettier for formatting; drop obsolete ESLint formatting rules. (#238)

This commit is contained in:
Mark Banner 2024-04-22 10:41:30 +01:00 коммит произвёл GitHub
Родитель 64cbbae24e
Коммит ea2f6debda
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
22 изменённых файлов: 1880 добавлений и 8296 удалений

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

@ -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"]
}

50
.github/workflows/ci.yml поставляемый
Просмотреть файл

@ -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.
<!--

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

@ -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/).

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

@ -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

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

@ -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);
}
}
}
},
};
}
},
};

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

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

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

@ -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",

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

@ -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

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