mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(linter/eslint): implement no-new-func (#5360)
Related to #479 --------- Co-authored-by: Don Isaac <donald.isaac@gmail.com>
This commit is contained in:
parent
35f03db464
commit
1967c6730b
3 changed files with 207 additions and 0 deletions
|
|
@ -81,6 +81,7 @@ mod eslint {
|
|||
pub mod no_loss_of_precision;
|
||||
pub mod no_multi_str;
|
||||
pub mod no_new;
|
||||
pub mod no_new_func;
|
||||
pub mod no_new_native_nonconstructor;
|
||||
pub mod no_new_wrappers;
|
||||
pub mod no_nonoctal_decimal_escape;
|
||||
|
|
@ -524,6 +525,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::no_iterator,
|
||||
eslint::no_loss_of_precision,
|
||||
eslint::no_new,
|
||||
eslint::no_new_func,
|
||||
eslint::no_new_wrappers,
|
||||
eslint::no_nonoctal_decimal_escape,
|
||||
eslint::no_obj_calls,
|
||||
|
|
|
|||
143
crates/oxc_linter/src/rules/eslint/no_new_func.rs
Normal file
143
crates/oxc_linter/src/rules/eslint/no_new_func.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
use oxc_ast::{ast::IdentifierReference, AstKind};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
fn no_new_func(span: Span) -> OxcDiagnostic {
|
||||
OxcDiagnostic::warn("The Function constructor is eval.").with_label(span)
|
||||
}
|
||||
|
||||
fn is_global_function_reference(ctx: &LintContext, id: &IdentifierReference) -> bool {
|
||||
if let Some(reference_id) = id.reference_id() {
|
||||
return id.name == "Function" && ctx.symbols().is_global_reference(reference_id);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct NoNewFunc;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
///
|
||||
/// The rule disallow `new` operators with the `Function` object.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Using `new Function` or `Function` can lead to code that is difficult to understand and maintain. It can introduce security risks similar to those associated with `eval` because it generates a new function from a string of code, which can be a vector for injection attacks. Additionally, it impacts performance negatively as these functions are not optimized by the JavaScript engine.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// Examples of **incorrect** code for this rule:
|
||||
/// ```js
|
||||
/// var x = new Function("a", "b", "return a + b");
|
||||
/// var x = Function("a", "b", "return a + b");
|
||||
/// var x = Function.call(null, "a", "b", "return a + b");
|
||||
/// var x = Function.apply(null, ["a", "b", "return a + b"]);
|
||||
/// var x = Function.bind(null, "a", "b", "return a + b")();
|
||||
/// var f = Function.bind(null, "a", "b", "return a + b");
|
||||
/// ```
|
||||
///
|
||||
/// Examples of **correct** code for this rule:
|
||||
/// ```js
|
||||
/// let x = function (a, b) {
|
||||
/// return a + b;
|
||||
/// };
|
||||
/// ```
|
||||
NoNewFunc,
|
||||
style
|
||||
);
|
||||
|
||||
impl Rule for NoNewFunc {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
let id_and_span = match node.kind() {
|
||||
AstKind::NewExpression(new_expr) => {
|
||||
let Some(id) = new_expr.callee.get_identifier_reference() else {
|
||||
return;
|
||||
};
|
||||
|
||||
Some((id, new_expr.span))
|
||||
}
|
||||
AstKind::CallExpression(call_expr) => {
|
||||
let Some(obj_id) = call_expr.callee.get_identifier_reference() else {
|
||||
return;
|
||||
};
|
||||
|
||||
Some((obj_id, call_expr.span))
|
||||
}
|
||||
AstKind::MemberExpression(mem_expr) => {
|
||||
let parent: Option<&AstNode<'a>> =
|
||||
ctx.nodes().iter_parents(node.id()).skip(1).find(|node| {
|
||||
!matches!(
|
||||
node.kind(),
|
||||
AstKind::ChainExpression(_) | AstKind::ParenthesizedExpression(_)
|
||||
)
|
||||
});
|
||||
|
||||
let Some(AstKind::CallExpression(parent_call_expr)) = parent.map(AstNode::kind)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(static_property_name) = &mem_expr.static_property_name() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !["apply", "bind", "call"].contains(static_property_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(obj_id) = mem_expr.object().get_identifier_reference() else {
|
||||
return;
|
||||
};
|
||||
|
||||
Some((obj_id, parent_call_expr.span))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((id, span)) = id_and_span {
|
||||
if is_global_function_reference(ctx, id) {
|
||||
ctx.diagnostic(no_new_func(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
r#"var a = new _function("b", "c", "return b+c");"#,
|
||||
r#"var a = _function("b", "c", "return b+c");"#,
|
||||
"class Function {}; new Function()",
|
||||
"const fn = () => { class Function {}; new Function() }",
|
||||
"function Function() {}; Function()",
|
||||
"var fn = function () { function Function() {}; Function() }",
|
||||
"var x = function Function() { Function(); }",
|
||||
"call(Function)",
|
||||
"new Class(Function)",
|
||||
"foo[Function]()",
|
||||
"foo(Function.bind)",
|
||||
"Function.toString()",
|
||||
"Function[call]()",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
r#"var a = new Function("b", "c", "return b+c");"#,
|
||||
r#"var a = Function("b", "c", "return b+c");"#,
|
||||
r#"var a = Function.call(null, "b", "c", "return b+c");"#,
|
||||
r#"var a = Function.apply(null, ["b", "c", "return b+c"]);"#,
|
||||
r#"var a = Function.bind(null, "b", "c", "return b+c")();"#,
|
||||
r#"var a = Function.bind(null, "b", "c", "return b+c");"#,
|
||||
r#"var a = Function["call"](null, "b", "c", "return b+c");"#,
|
||||
r#"var a = (Function?.call)(null, "b", "c", "return b+c");"#,
|
||||
"const fn = () => { class Function {} }; new Function('', '')",
|
||||
"var fn = function () { function Function() {} }; Function('', '')",
|
||||
];
|
||||
|
||||
Tester::new(NoNewFunc::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
62
crates/oxc_linter/src/snapshots/no_new_func.snap
Normal file
62
crates/oxc_linter/src/snapshots/no_new_func.snap
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
---
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = new Function("b", "c", "return b+c");
|
||||
· ────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function("b", "c", "return b+c");
|
||||
· ────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function.call(null, "b", "c", "return b+c");
|
||||
· ───────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function.apply(null, ["b", "c", "return b+c"]);
|
||||
· ──────────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function.bind(null, "b", "c", "return b+c")();
|
||||
· ───────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function.bind(null, "b", "c", "return b+c");
|
||||
· ───────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = Function["call"](null, "b", "c", "return b+c");
|
||||
· ──────────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:9]
|
||||
1 │ var a = (Function?.call)(null, "b", "c", "return b+c");
|
||||
· ──────────────────────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:41]
|
||||
1 │ const fn = () => { class Function {} }; new Function('', '')
|
||||
· ────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint(no-new-func): The Function constructor is eval.
|
||||
╭─[no_new_func.tsx:1:50]
|
||||
1 │ var fn = function () { function Function() {} }; Function('', '')
|
||||
· ────────────────
|
||||
╰────
|
||||
Loading…
Reference in a new issue