Skip to content

Commit 773f5b0

Browse files
authored
feat(lint): add variable assignment tracking to noDocumentCookie rule (#4255)
1 parent 8b3c102 commit 773f5b0

File tree

5 files changed

+115
-18
lines changed

5 files changed

+115
-18
lines changed

crates/biome_js_analyze/src/lint/nursery/no_document_cookie.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use biome_analyze::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnosti
22
use biome_console::markup;
33
use biome_js_semantic::SemanticModel;
44
use biome_js_syntax::{
5-
global_identifier, AnyJsAssignment, AnyJsExpression, JsAssignmentExpression,
5+
binding_ext::AnyJsBindingDeclaration, global_identifier, static_value::StaticValue,
6+
AnyJsAssignment, AnyJsExpression, JsAssignmentExpression, JsReferenceIdentifier,
67
};
78
use biome_rowan::AstNode;
89

@@ -57,17 +58,37 @@ declare_lint_rule! {
5758
}
5859
}
5960

61+
fn identifier_is_global_document(
62+
reference: &JsReferenceIdentifier,
63+
name: &StaticValue,
64+
model: &SemanticModel,
65+
) -> bool {
66+
// Check identifier is `document` and global
67+
name.text() == "document" && model.binding(reference).is_none()
68+
}
69+
6070
/// Check `expr` is `document`
6171
fn is_global_document(expr: &AnyJsExpression, model: &SemanticModel) -> Option<()> {
6272
let (reference, name) = global_identifier(expr)?;
6373

64-
// Check identifier is `document`
65-
if name.text() != "document" {
66-
return None;
67-
};
68-
69-
// TODO: Verify that the variable is assigned the global `document` to be closer to the original rule.
70-
model.binding(&reference).is_none().then_some(())
74+
// `expr` is global document
75+
if identifier_is_global_document(&reference, &name, model) {
76+
Some(())
77+
} else {
78+
// Check binding declaration recursively
79+
let bind = model.binding(&reference)?;
80+
let decl = bind.tree().declaration()?;
81+
let decl = decl.parent_binding_pattern_declaration().unwrap_or(decl);
82+
match decl {
83+
// const foo = documnet;
84+
AnyJsBindingDeclaration::JsVariableDeclarator(declarator) => {
85+
let initializer = declarator.initializer()?;
86+
let right_expr = initializer.expression().ok()?;
87+
is_global_document(&right_expr, model)
88+
}
89+
_ => None,
90+
}
91+
}
7192
}
7293

7394
/// Check member is `cookie`
@@ -110,6 +131,7 @@ impl Rule for NoDocumentCookie {
110131

111132
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
112133
let node = ctx.query();
134+
113135
let left = node.left().ok()?;
114136

115137
let any_assignment = left.as_any_js_assignment()?;

crates/biome_js_analyze/tests/specs/nursery/noDocumentCookie/invalid.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,20 @@ document.cookie += ";foo=bar"
44
window.document.cookie = "foo=bar";
55
globalThis.window.document.cookie = "foo=bar";
66

7-
document["cookie"] = "foo=bar"
7+
document["cookie"] = "foo=bar"
8+
9+
function document_is_global1(){
10+
const doc = document;
11+
doc.cookie = "bar=foo"
12+
}
13+
14+
function document_is_global2(){
15+
const foo = window.document;
16+
const bar = foo;
17+
bar.cookie = "foo=bar";
18+
}
19+
20+
const global_doc = globalThis.document;
21+
function document_is_global3(){
22+
global_doc.cookie = "foo=bar";
23+
}

crates/biome_js_analyze/tests/specs/nursery/noDocumentCookie/invalid.js.snap

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ window.document.cookie = "foo=bar";
1111
globalThis.window.document.cookie = "foo=bar";
1212
1313
document["cookie"] = "foo=bar"
14+
15+
function document_is_global1(){
16+
const doc = document;
17+
doc.cookie = "bar=foo"
18+
}
19+
20+
function document_is_global2(){
21+
const foo = window.document;
22+
const bar = foo;
23+
bar.cookie = "foo=bar";
24+
}
25+
26+
const global_doc = globalThis.document;
27+
function document_is_global3(){
28+
global_doc.cookie = "foo=bar";
29+
}
1430
```
1531

1632
# Diagnostics
@@ -87,6 +103,58 @@ invalid.js:7:1 lint/nursery/noDocumentCookie ━━━━━━━━━━━
87103
6 │
88104
> 7 │ document["cookie"] = "foo=bar"
89105
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
106+
8 │
107+
9 │ function document_is_global1(){
108+
109+
i Consider using the Cookie Store API.
110+
111+
112+
```
113+
114+
```
115+
invalid.js:11:5 lint/nursery/noDocumentCookie ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
116+
117+
! Direct assigning to document.cookie is not recommended.
118+
119+
9function document_is_global1(){
120+
10const doc = document;
121+
> 11doc.cookie = "bar=foo"
122+
^^^^^^^^^^^^^^^^^^^^^^
123+
12 │ }
124+
13
125+
126+
i Consider using the Cookie Store API.
127+
128+
129+
```
130+
131+
```
132+
invalid.js:17:5 lint/nursery/noDocumentCookie ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
133+
134+
! Direct assigning to document.cookie is not recommended.
135+
136+
15const foo = window.document;
137+
16const bar = foo;
138+
> 17bar.cookie = "foo=bar";
139+
^^^^^^^^^^^^^^^^^^^^^^
140+
18}
141+
19 │
142+
143+
i Consider using the Cookie Store API.
144+
145+
146+
```
147+
148+
```
149+
invalid.js:22:5 lint/nursery/noDocumentCookie ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
150+
151+
! Direct assigning to document.cookie is not recommended.
152+
153+
20 │ const global_doc = globalThis.document;
154+
21 │ function document_is_global3(){
155+
> 22global_doc.cookie = "foo=bar";
156+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
157+
23}
90158
91159
i Consider using the Cookie Store API.
92160

crates/biome_js_analyze/tests/specs/nursery/noDocumentCookie/valid.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,3 @@ cookieStore
1515
function document_is_not_global1(document){
1616
document.cookie = "bar=foo"
1717
}
18-
19-
function document_is_not_global2(){
20-
const document = "foo";
21-
document.cookie = "bar=foo"
22-
}

crates/biome_js_analyze/tests/specs/nursery/noDocumentCookie/valid.js.snap

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,4 @@ function document_is_not_global1(document){
2222
document.cookie = "bar=foo"
2323
}
2424
25-
function document_is_not_global2(){
26-
const document = "foo";
27-
document.cookie = "bar=foo"
28-
}
2925
```

0 commit comments

Comments
 (0)