-
-
Notifications
You must be signed in to change notification settings - Fork 456
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lint/noGlobalEval): add rule (#1133)
Co-authored-by: Victorien Elvinger <victorien@elvinger.fr>
- Loading branch information
1 parent
c98a030
commit b88f8b7
Showing
19 changed files
with
1,023 additions
and
70 deletions.
There are no files selected for viewing
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
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
17 changes: 0 additions & 17 deletions
17
crates/biome_cli/tests/snapshots/main_commands_explain/explain_logs.snap.new
This file was deleted.
Oops, something went wrong.
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.
93 changes: 93 additions & 0 deletions
93
crates/biome_js_analyze/src/semantic_analyzers/nursery/no_global_eval.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,93 @@ | ||
use crate::semantic_services::Semantic; | ||
use biome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic}; | ||
use biome_console::markup; | ||
use biome_js_syntax::{global_identifier, AnyJsExpression}; | ||
use biome_rowan::AstNode; | ||
|
||
declare_rule! { | ||
/// Disallow the use of global `eval()`. | ||
/// | ||
/// The `eval()` function evaluates the passed string as a _JavaScript_ code. | ||
/// The executed code can access and mutate variables in the scope where the function is called. | ||
/// | ||
/// The use of `eval()` exposes to [security risks and performance issues](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!). | ||
/// If the executed code is somehow affected by a malicious party, | ||
/// then you may end up executing malicious code with the privileges of the caller. | ||
/// Moreover, changing variables in the caller's scope is expensive in modern _JavaScript_ interpreters. | ||
/// | ||
/// Source: https://eslint.org/docs/latest/rules/no-eval | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// eval("var a = 0"); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// (0, globalThis.eval)("var a = 0") | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// f(eval); | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// const aliasedEval = eval; | ||
/// ``` | ||
/// | ||
/// ### Valid | ||
/// | ||
/// ```cjs | ||
/// function f(eval) { | ||
/// eval("let a = 0;"); | ||
/// } | ||
/// ``` | ||
/// | ||
/// The rule is not able to detect cases where the global object is aliased: | ||
/// | ||
/// ```js | ||
/// let foo = globalThis; | ||
/// foo.eval("let a = 0;"); | ||
/// ``` | ||
pub(crate) NoGlobalEval { | ||
version: "next", | ||
name: "noGlobalEval", | ||
recommended: true, | ||
} | ||
} | ||
|
||
impl Rule for NoGlobalEval { | ||
type Query = Semantic<AnyJsExpression>; | ||
type State = (); | ||
type Signals = Option<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
let model = ctx.model(); | ||
let (reference, name) = global_identifier(node)?; | ||
if name.text() != "eval" { | ||
return None; | ||
} | ||
model.binding(&reference).is_none().then_some(()) | ||
} | ||
|
||
fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> { | ||
let node = ctx.query(); | ||
Some(RuleDiagnostic::new( | ||
rule_category!(), | ||
node.range(), | ||
markup! { | ||
<Emphasis>"eval()"</Emphasis>" exposes to security risks and performance issues." | ||
}, | ||
).note(markup! { | ||
"See the "<Hyperlink href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!">"MDN web docs"</Hyperlink>" for more details." | ||
}) | ||
.note( | ||
markup! { | ||
"Refactor the code so that it doesn't need to call "<Emphasis>"eval()"</Emphasis>"." | ||
})) | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
crates/biome_js_analyze/tests/specs/nursery/noGlobalEval/invalid.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,73 @@ | ||
var foo = "foo"; | ||
eval(foo); | ||
|
||
eval("foo"); | ||
|
||
(0, eval)("foo"); | ||
|
||
(0, window.eval)("foo"); | ||
|
||
(0, window["eval"])("foo"); | ||
|
||
var EVAL = eval; | ||
EVAL("foo"); | ||
|
||
var EVAL = this.eval; | ||
EVAL("foo"); | ||
|
||
("use strict"); | ||
var EVAL = this.eval; | ||
EVAL("foo"); | ||
|
||
() => { | ||
this.eval("foo"); | ||
}; | ||
|
||
() => { | ||
"use strict"; | ||
this.eval("foo"); | ||
}; | ||
|
||
("use strict"); | ||
() => { | ||
this.eval("foo"); | ||
}; | ||
|
||
() => { | ||
"use strict"; | ||
() => { | ||
this.eval("foo"); | ||
}; | ||
}; | ||
|
||
(function (exe) { | ||
exe("foo"); | ||
})(eval); | ||
|
||
window.eval("foo"); | ||
|
||
window.window.eval("foo"); | ||
|
||
window.window["eval"]("foo"); | ||
|
||
this.eval("foo"); | ||
|
||
("use strict"); | ||
this.eval("foo"); | ||
|
||
function foo() { | ||
this.eval("foo"); | ||
} | ||
|
||
var EVAL = globalThis.eval; | ||
EVAL("foo"); | ||
|
||
globalThis.eval("foo"); | ||
|
||
globalThis.globalThis.eval("foo"); | ||
|
||
globalThis.globalThis["eval"]("foo"); | ||
|
||
(0, globalThis.eval)("foo"); | ||
|
||
(0, globalThis["eval"])("foo"); |
Oops, something went wrong.