Skip to content

Commit d05b230

Browse files
Shinonilesya7lpomerleau
authored
New rule 'no unsupported Node APIs in SSR-able components' (#167)
* New rule 'no-unguarded risky Node APIs in SSR-able components' * Block the risky Node API in SSR-able components * Change 'risky' to 'unsupported' * Update docs/rules/no-unsupported-node-api-in-ssrable-components.md Co-authored-by: Laura <49494194+lpomerleau@users.noreply.github.com> * Addressing the feedback * rename the rule --------- Co-authored-by: lturanscaia <lturanscaia@salesforce.com> Co-authored-by: Laura <49494194+lpomerleau@users.noreply.github.com>
1 parent e963a3e commit d05b230

File tree

5 files changed

+248
-8
lines changed

5 files changed

+248
-8
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,15 @@ To choose from three configuration settings, install the [`eslint-config-lwc`](h
9090

9191
### Best practices
9292

93-
| Rule ID | Description | Fixable |
94-
| ------------------------------------------------------------------------ | --------------------------------------------------------- | ------- |
95-
| [lwc/no-async-operation](./docs/rules/no-async-operation.md) | restrict usage of async operations | |
96-
| [lwc/no-dupe-class-members](./docs/rules/no-dupe-class-members.md) | disallow duplicate class members | |
97-
| [lwc/no-inner-html](./docs/rules/no-inner-html.md) | disallow usage of `innerHTML` | |
98-
| [lwc/no-template-children](./docs/rules/no-template-children.md) | prevent accessing the immediate children of this.template | |
99-
| [lwc/no-leaky-event-listeners](./docs/rules/no-leaky-event-listeners.md) | prevent event listeners from leaking memory | |
100-
| [lwc/prefer-custom-event](./docs/rules/prefer-custom-event.md) | suggest usage of `CustomEvent` over `Event` constructor | |
93+
| Rule ID | Description | Fixable |
94+
| ------------------------------------------------------------------------------ | ---------------------------------------------------------- | ------- |
95+
| [lwc/no-async-operation](./docs/rules/no-async-operation.md) | restrict usage of async operations | |
96+
| [lwc/no-dupe-class-members](./docs/rules/no-dupe-class-members.md) | disallow duplicate class members | |
97+
| [lwc/no-inner-html](./docs/rules/no-inner-html.md) | disallow usage of `innerHTML` | |
98+
| [lwc/no-template-children](./docs/rules/no-template-children.md) | prevent accessing the immediate children of this.template | |
99+
| [lwc/no-leaky-event-listeners](./docs/rules/no-leaky-event-listeners.md) | prevent event listeners from leaking memory | |
100+
| [lwc/prefer-custom-event](./docs/rules/prefer-custom-event.md) | suggest usage of `CustomEvent` over `Event` constructor | |
101+
| [lwc/ssr-no-unsupported-node-api](./docs/rules/ssr-no-unsupported-node-api.md) | disallow unsupported Node API calls in SSR-able components | |
101102

102103
### Compat performance
103104

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Disallow Node API Calls in SSR Context (`lwc/ssr-no-unsupported-node-api`)
2+
3+
This rule disallows the use of unsupported Node API calls within components that may run during server-side rendering. These APIs are not available in client-side rendering environments and can lead to serious issues when used without proper safeguards. To avoid unexpected behavior and security vulnerabilities, certain problematic Node APIs should not be used in SSR contexts.
4+
5+
## Blocked Node APIs
6+
7+
The following Node APIs are disallowed in SSR contexts:
8+
9+
- `require`
10+
- `fs`
11+
- `child_process`
12+
- `worker_threads`
13+
- `perf_hooks`
14+
15+
## Rule Details
16+
17+
The purpose of this rule is to prevent the use of dangerous Node API calls in SSR contexts. If you must perform these operations, ensure they are only executed in environments where they are supported.
18+
19+
### Example of **incorrect** code:
20+
21+
```js
22+
const fs = require('fs');
23+
24+
if (import.meta.env.SSR) {
25+
// unsupported Node API call within SSR context
26+
fs.writeFileSync('file.txt', 'data');
27+
}
28+
```
29+
30+
### Example of **incorrect** code:
31+
32+
```js
33+
// Do not use Node APIs
34+
```

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const rules = {
3232
'no-restricted-browser-globals-during-ssr': require('./rules/no-restricted-browser-globals-during-ssr'),
3333
'no-unsupported-ssr-properties': require('./rules/no-unsupported-ssr-properties'),
3434
'no-node-env-in-ssr': require('./rules/no-node-env-in-ssr'),
35+
'ssr-no-unsupported-node-api': require('./rules/ssr-no-unsupported-node-api'),
3536
'no-host-mutation-in-connected-callback': require('./rules/no-host-mutation-in-connected-callback'),
3637
};
3738

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright (c) 2024, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: MIT
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6+
*/
7+
'use strict';
8+
9+
const { docUrl } = require('../util/doc-url');
10+
11+
module.exports = {
12+
meta: {
13+
type: 'problem',
14+
docs: {
15+
description: 'Disallow unsupported Node API calls in SSR-able components.',
16+
category: 'LWC',
17+
url: docUrl('ssr-no-unsupported-node-api'),
18+
recommended: true,
19+
},
20+
messages: {
21+
unsupportedNodeApi:
22+
'The unsupported Node API calls are not allowed in SSR-able components.',
23+
},
24+
schema: [],
25+
},
26+
create(context) {
27+
const containsUnsupportedApiCall = (body) =>
28+
/require|fs|node:fs|child_process|node:child_process|worker_threads|node:worker_threads|perf_hooks|node:perf_hooks/.test(
29+
body,
30+
);
31+
32+
return {
33+
Program(node) {
34+
const unsupportedApiCalls = [];
35+
36+
node.body.forEach((statement) => {
37+
const statementText = context.getSourceCode().getText(statement);
38+
if (containsUnsupportedApiCall(statementText)) {
39+
unsupportedApiCalls.push(statement);
40+
}
41+
});
42+
43+
unsupportedApiCalls.forEach((apiCallNode) => {
44+
context.report({
45+
node: apiCallNode,
46+
messageId: 'unsupportedNodeApi',
47+
});
48+
});
49+
},
50+
};
51+
},
52+
};
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (c) 2024, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: MIT
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6+
*/
7+
'use strict';
8+
9+
const { testRule, testTypeScript } = require('../shared');
10+
11+
testRule('ssr-no-unsupported-node-api', {
12+
valid: [
13+
{
14+
code: `
15+
// Safe code without the unsupported Node API calls
16+
if (import.meta.env.SSR) {
17+
// Safe code
18+
}
19+
`,
20+
},
21+
],
22+
invalid: [
23+
{
24+
code: `
25+
const fs = require('fs');
26+
27+
// the unsupported Node API call
28+
fs.writeFileSync('file.txt', 'data');
29+
`,
30+
errors: [
31+
{
32+
message:
33+
'The unsupported Node API calls are not allowed in SSR-able components.',
34+
line: 2,
35+
},
36+
{
37+
message:
38+
'The unsupported Node API calls are not allowed in SSR-able components.',
39+
line: 5,
40+
},
41+
],
42+
},
43+
{
44+
code: `
45+
require('something');
46+
`,
47+
errors: [
48+
{
49+
message:
50+
'The unsupported Node API calls are not allowed in SSR-able components.',
51+
},
52+
],
53+
},
54+
{
55+
code: `
56+
const fs = require('node:fs');
57+
58+
// Unsupported Node API call with node: prefix
59+
fs.writeFileSync('file.txt', 'data');
60+
`,
61+
errors: [
62+
{
63+
message:
64+
'The unsupported Node API calls are not allowed in SSR-able components.',
65+
line: 2,
66+
},
67+
{
68+
message:
69+
'The unsupported Node API calls are not allowed in SSR-able components.',
70+
line: 5,
71+
},
72+
],
73+
},
74+
{
75+
code: `
76+
require('node:child_process');
77+
`,
78+
errors: [
79+
{
80+
message:
81+
'The unsupported Node API calls are not allowed in SSR-able components.',
82+
},
83+
],
84+
},
85+
],
86+
});
87+
88+
testTypeScript('ssr-no-unsupported-node-api', {
89+
valid: [
90+
{
91+
code: `
92+
// Safe code without the unsupported Node API calls
93+
if (import.meta.env.SSR) {
94+
// Safe code
95+
}
96+
`,
97+
},
98+
],
99+
invalid: [
100+
{
101+
code: `
102+
const fs = require('fs');
103+
104+
// the unsupported Node API call
105+
fs.writeFileSync('file.txt', 'data');
106+
`,
107+
errors: [
108+
{
109+
message:
110+
'The unsupported Node API calls are not allowed in SSR-able components.',
111+
line: 2,
112+
},
113+
{
114+
message:
115+
'The unsupported Node API calls are not allowed in SSR-able components.',
116+
line: 5,
117+
},
118+
],
119+
},
120+
{
121+
code: `
122+
const fs = require('node:fs');
123+
124+
// the unsupported Node API call with node: prefix in TypeScript
125+
fs.writeFileSync('file.txt', 'data');
126+
`,
127+
errors: [
128+
{
129+
message:
130+
'The unsupported Node API calls are not allowed in SSR-able components.',
131+
line: 2,
132+
},
133+
{
134+
message:
135+
'The unsupported Node API calls are not allowed in SSR-able components.',
136+
line: 5,
137+
},
138+
],
139+
},
140+
{
141+
code: `
142+
require('node:child_process');
143+
`,
144+
errors: [
145+
{
146+
message:
147+
'The unsupported Node API calls are not allowed in SSR-able components.',
148+
},
149+
],
150+
},
151+
],
152+
});

0 commit comments

Comments
 (0)