This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 664
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rome_js_analyze):
noDangerouslySetInnerHtml
rule (#3207)
- Loading branch information
Showing
19 changed files
with
415 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
135 changes: 135 additions & 0 deletions
135
crates/rome_js_analyze/src/semantic_analyzers/nursery/no_dangerously_set_inner_html.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
use crate::semantic_services::Semantic; | ||
use crate::utils::{is_react_create_element, PossibleCreateElement}; | ||
use rome_analyze::context::RuleContext; | ||
use rome_analyze::{declare_rule, Rule, RuleCategory, RuleDiagnostic}; | ||
use rome_console::codespan::Severity; | ||
use rome_console::markup; | ||
use rome_js_syntax::{ | ||
JsAnyExpression, JsCallExpression, JsLiteralMemberName, JsxAnyAttributeName, JsxAttribute, | ||
}; | ||
use rome_rowan::{declare_node_union, AstNode}; | ||
|
||
declare_rule! { | ||
/// Prevent the usage of dangerous JSX props | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```jsx,expect_diagnostic | ||
/// function createMarkup() { | ||
/// return { __html: 'child' } | ||
/// } | ||
/// <div dangerouslySetInnerHTML={createMarkup()}></div> | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// React.createElement('div', { | ||
/// dangerouslySetInnerHTML: { __html: 'child' } | ||
/// }); | ||
/// ``` | ||
pub(crate) NoDangerouslySetInnerHtml { | ||
version: "0.10.0", | ||
name: "noDangerouslySetInnerHtml", | ||
recommended: false, | ||
} | ||
} | ||
|
||
declare_node_union! { | ||
pub(crate) JsAnyCreateElement = JsxAttribute | JsCallExpression | ||
} | ||
|
||
pub(crate) enum NoDangerState { | ||
Attribute(JsxAttribute), | ||
Property(JsLiteralMemberName), | ||
} | ||
|
||
impl Rule for NoDangerouslySetInnerHtml { | ||
const CATEGORY: RuleCategory = RuleCategory::Lint; | ||
|
||
type Query = Semantic<JsAnyCreateElement>; | ||
type State = NoDangerState; | ||
type Signals = Option<Self::State>; | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
let model = ctx.model(); | ||
match node { | ||
JsAnyCreateElement::JsxAttribute(jsx_attribute) => { | ||
let name = jsx_attribute.name().ok()?; | ||
match name { | ||
JsxAnyAttributeName::JsxName(jsx_name) => { | ||
if jsx_name.syntax().text_trimmed() == "dangerouslySetInnerHTML" { | ||
return Some(NoDangerState::Attribute(jsx_attribute.clone())); | ||
} | ||
} | ||
JsxAnyAttributeName::JsxNamespaceName(_) => return None, | ||
} | ||
} | ||
JsAnyCreateElement::JsCallExpression(call_expression) => { | ||
let callee = call_expression.callee().ok()?; | ||
let is_create_element = match callee { | ||
JsAnyExpression::JsStaticMemberExpression(static_member) => { | ||
is_react_create_element(PossibleCreateElement::from(static_member), model) | ||
} | ||
JsAnyExpression::JsIdentifierExpression(identifier_expression) => { | ||
is_react_create_element( | ||
PossibleCreateElement::from(identifier_expression), | ||
model, | ||
) | ||
} | ||
_ => return None, | ||
}?; | ||
|
||
if is_create_element { | ||
// if we are inside a create element call, we inspect the second argument, which | ||
// should be an object expression. We look for a member that has as name | ||
// "dangerouslySetInnerHTML" | ||
let arguments = call_expression.arguments().ok()?.args(); | ||
let mut arguments = arguments.into_iter(); | ||
if let (Some(_), Some(second_argument)) = (arguments.next(), arguments.next()) { | ||
let second_argument = second_argument.ok()?; | ||
let object_expression = second_argument | ||
.as_js_any_expression()? | ||
.as_js_object_expression()?; | ||
let members = object_expression.members(); | ||
for member in members { | ||
let member = member.ok()?; | ||
let property_member = | ||
member.as_js_property_object_member()?.name().ok()?; | ||
let name = property_member.as_js_literal_member_name()?; | ||
|
||
if name.syntax().text_trimmed() == "dangerouslySetInnerHTML" { | ||
return Some(NoDangerState::Property(name.clone())); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
fn diagnostic(_ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
let text_range = match state { | ||
NoDangerState::Attribute(jsx_attribute) => { | ||
let name = jsx_attribute.name().ok()?; | ||
name.syntax().text_trimmed_range() | ||
} | ||
NoDangerState::Property(property) => property.syntax().text_trimmed_range(), | ||
}; | ||
|
||
let diagnostic = RuleDiagnostic::new( | ||
text_range, | ||
markup! { | ||
"Avoid passing content using the "<Emphasis>"dangerouslySetInnerHTML"</Emphasis>" prop." | ||
} | ||
.to_owned(), | ||
).footer( | ||
Severity::Warning, | ||
"Setting content using code can expose users to cross-site scripting (XSS) attacks", | ||
); | ||
Some(diagnostic) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
crates/rome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/createElementBinding.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React, { createElement } from "react"; | ||
|
||
React.createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); | ||
|
||
createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); |
43 changes: 43 additions & 0 deletions
43
...ome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/createElementBinding.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: createElementBinding.js | ||
--- | ||
# Input | ||
```js | ||
import React, { createElement } from "react"; | ||
React.createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); | ||
createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); | ||
``` | ||
|
||
# Diagnostics | ||
``` | ||
warning[nursery/noDangerouslySetInnerHtml]: Avoid passing content using the dangerouslySetInnerHTML prop. | ||
┌─ createElementBinding.js:4:5 | ||
│ | ||
4 │ dangerouslySetInnerHTML: { __html: 'child' } | ||
│ ----------------------- | ||
= warning: Setting content using code can expose users to cross-site scripting (XSS) attacks | ||
``` | ||
|
||
``` | ||
warning[nursery/noDangerouslySetInnerHtml]: Avoid passing content using the dangerouslySetInnerHTML prop. | ||
┌─ createElementBinding.js:8:5 | ||
│ | ||
8 │ dangerouslySetInnerHTML: { __html: 'child' } | ||
│ ----------------------- | ||
= warning: Setting content using code can expose users to cross-site scripting (XSS) attacks | ||
``` | ||
|
||
|
5 changes: 5 additions & 0 deletions
5
crates/rome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/insideJsx.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// invalid | ||
let a = <div dangerouslySetInnerHTML={{ __html: 'child' }} /> | ||
|
||
// valid | ||
let b = <div foo="" /> |
27 changes: 27 additions & 0 deletions
27
crates/rome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/insideJsx.jsx.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: insideJsx.jsx | ||
--- | ||
# Input | ||
```js | ||
// invalid | ||
let a = <div dangerouslySetInnerHTML={{ __html: 'child' }} /> | ||
// valid | ||
let b = <div foo="" /> | ||
``` | ||
|
||
# Diagnostics | ||
``` | ||
warning[nursery/noDangerouslySetInnerHtml]: Avoid passing content using the dangerouslySetInnerHTML prop. | ||
┌─ insideJsx.jsx:2:14 | ||
│ | ||
2 │ let a = <div dangerouslySetInnerHTML={{ __html: 'child' }} /> | ||
│ ----------------------- | ||
= warning: Setting content using code can expose users to cross-site scripting (XSS) attacks | ||
``` | ||
|
||
|
3 changes: 3 additions & 0 deletions
3
crates/rome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/reactCreateElement.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
React.createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); |
25 changes: 25 additions & 0 deletions
25
.../rome_js_analyze/tests/specs/nursery/noDangerouslySetInnerHtml/reactCreateElement.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: reactCreateElement.js | ||
--- | ||
# Input | ||
```js | ||
React.createElement('div', { | ||
dangerouslySetInnerHTML: { __html: 'child' } | ||
}); | ||
``` | ||
|
||
# Diagnostics | ||
``` | ||
warning[nursery/noDangerouslySetInnerHtml]: Avoid passing content using the dangerouslySetInnerHTML prop. | ||
┌─ reactCreateElement.js:2:5 | ||
│ | ||
2 │ dangerouslySetInnerHTML: { __html: 'child' } | ||
│ ----------------------- | ||
= warning: Setting content using code can expose users to cross-site scripting (XSS) attacks | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.