1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use crate::rule_prelude::*;
use ast::{CallExpr, DotExpr};
declare_lint! {
#[derive(Default)]
NoPrototypeBuiltins,
errors,
"no-prototype-builtins"
}
const CHECKED_PROPS: [&str; 3] = ["hasOwnProperty", "isPrototypeOf", "propertyIsEnumberable"];
#[typetag::serde]
impl CstRule for NoPrototypeBuiltins {
fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
let expr = node.try_to::<CallExpr>()?;
let lhs = expr.callee()?.syntax().try_to::<DotExpr>()?;
let prop = lhs.prop()?;
let object = lhs.object()?;
if CHECKED_PROPS.contains(&prop.text().as_str()) {
let mut err = ctx.err(self.name(), format!("do not access the object property `{}` directly from `{}`", prop.text(), object.text()))
.primary(expr.range(), "");
err = suggestion(prop.text(), object.text(), expr, err);
ctx.add_err(err);
}
None
}
}
fn suggestion(prop: String, object: String, expr: CallExpr, err: DiagnosticBuilder) -> DiagnosticBuilder {
let arg = if let Some(arg) = expr.arguments().and_then(|args| args.args().next()) {
format!(", {}", arg.text())
} else {
"".to_string()
};
let suggestion_expr = format!("Object.prototype.{}.call({}{})", prop, object, arg);
err.note(format!("help: use this instead: `{}`", color(&suggestion_expr)))
.note("note: the method may be shadowed and cause random bugs and denial of service vulnerabilities")
}
rule_tests! {
NoPrototypeBuiltins::default(),
err: {
"foo.hasOwnProperty(\"bar\");",
"foo.isPrototypeOf(\"bar\");",
"foo.propertyIsEnumberable(\"bar\");",
"foo.bar.baz.hasOwnProperty(\"bar\");"
},
ok: {
"Object.prototype.hasOwnProperty.call(foo, 'bar');",
"Object.prototype.isPrototypeOf.call(foo, 'bar');",
"Object.prototype.propertyIsEnumberable.call(foo, 'bar');",
"Object.prototype.hasOwnProperty.apply(foo, ['bar']);",
"Object.prototype.isPrototypeOf.apply(foo, ['bar']);",
"Object.prototype.propertyIsEnumberable.apply(foo, ['bar']);",
"hasOwnProperty(foo, 'bar');",
"isPrototypeOf(foo, 'bar');",
"propertyIsEnumberable(foo, 'bar');",
"({}.hasOwnProperty.call(foo, 'bar'));",
"({}.isPrototypeOf.call(foo, 'bar'));",
"({}.propertyIsEnumberable.call(foo, 'bar'));",
"({}.hasOwnProperty.apply(foo, ['bar']));",
"({}.isPrototypeOf.apply(foo, ['bar']));",
"({}.propertyIsEnumberable.apply(foo, ['bar']));"
}
}