Skip to content

Commit 56102b5

Browse files
authored
fix: false positives for static expressions in detect-non-literal-fs-filename, detect-child-process, detect-non-literal-regexp, and detect-non-literal-require (#109)
* fix: false positives for static expressions in detect-non-literal-fs-filename, detect-child-process, detect-non-literal-regexp, and detect-non-literal-require * remove dupe test * add tests
1 parent 75e1e9d commit 56102b5

12 files changed

+622
-43
lines changed

rules/detect-child-process.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'use strict';
77

88
const { getImportAccessPath } = require('../utils/import-utils');
9+
const { isStaticExpression } = require('../utils/is-static-expression');
910
const childProcessPackageNames = ['child_process', 'node:child_process'];
1011

1112
//------------------------------------------------------------------------------
@@ -41,7 +42,13 @@ module.exports = {
4142
}
4243

4344
// Reports non-literal `exec()` calls.
44-
if (!node.arguments.length || node.arguments[0].type === 'Literal') {
45+
if (
46+
!node.arguments.length ||
47+
isStaticExpression({
48+
node: node.arguments[0],
49+
scope: context.getScope(),
50+
})
51+
) {
4552
return;
4653
}
4754
const pathInfo = getImportAccessPath({

rules/detect-non-literal-fs-filename.js

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,7 @@ const funcNames = Object.keys(fsMetaData);
1010
const fsPackageNames = ['fs', 'node:fs', 'fs/promises', 'node:fs/promises', 'fs-extra'];
1111

1212
const { getImportAccessPath } = require('../utils/import-utils');
13-
14-
//------------------------------------------------------------------------------
15-
// Utils
16-
//------------------------------------------------------------------------------
17-
18-
function getIndices(node, argMeta) {
19-
return (argMeta || []).filter((argIndex) => node.arguments[argIndex].type !== 'Literal');
20-
}
21-
22-
function generateReport({ context, node, packageName, methodName, indices }) {
23-
if (!indices || indices.length === 0) {
24-
return;
25-
}
26-
context.report({ node, message: `Found ${methodName} from package "${packageName}" with non literal argument at index ${indices.join(',')}` });
27-
}
13+
const { isStaticExpression } = require('../utils/is-static-expression');
2814

2915
//------------------------------------------------------------------------------
3016
// Rule Definition
@@ -87,15 +73,23 @@ module.exports = {
8773
}
8874
const packageName = pathInfo.packageName;
8975

90-
const indices = getIndices(node, fsMetaData[fnName]);
91-
92-
generateReport({
93-
context,
94-
node,
95-
packageName,
96-
methodName: fnName,
97-
indices,
98-
});
76+
const indices = [];
77+
for (const index of fsMetaData[fnName] || []) {
78+
if (index >= node.arguments.length) {
79+
continue;
80+
}
81+
const argument = node.arguments[index];
82+
if (isStaticExpression({ node: argument, scope: context.getScope() })) {
83+
continue;
84+
}
85+
indices.push(index);
86+
}
87+
if (indices.length) {
88+
context.report({
89+
node,
90+
message: `Found ${fnName} from package "${packageName}" with non literal argument at index ${indices.join(',')}`,
91+
});
92+
}
9993
},
10094
};
10195
},

rules/detect-non-literal-regexp.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
'use strict';
77

8+
const { isStaticExpression } = require('../utils/is-static-expression');
9+
810
//------------------------------------------------------------------------------
911
// Rule Definition
1012
//------------------------------------------------------------------------------
@@ -24,7 +26,14 @@ module.exports = {
2426
NewExpression: function (node) {
2527
if (node.callee.name === 'RegExp') {
2628
const args = node.arguments;
27-
if (args && args.length > 0 && args[0].type !== 'Literal') {
29+
if (
30+
args &&
31+
args.length > 0 &&
32+
!isStaticExpression({
33+
node: args[0],
34+
scope: context.getScope(),
35+
})
36+
) {
2837
return context.report({ node: node, message: 'Found non-literal argument to RegExp Constructor' });
2938
}
3039
}

rules/detect-non-literal-require.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
'use strict';
77

8+
const { isStaticExpression } = require('../utils/is-static-expression');
9+
810
//------------------------------------------------------------------------------
911
// Rule Definition
1012
//------------------------------------------------------------------------------
@@ -25,8 +27,12 @@ module.exports = {
2527
if (node.callee.name === 'require') {
2628
const args = node.arguments;
2729
if (
28-
(args && args.length > 0 && args[0].type === 'TemplateLiteral' && args[0].expressions.length > 0) ||
29-
(args[0].type !== 'TemplateLiteral' && args[0].type !== 'Literal')
30+
args &&
31+
args.length > 0 &&
32+
!isStaticExpression({
33+
node: args[0],
34+
scope: context.getScope(),
35+
})
3036
) {
3137
return context.report({ node: node, message: 'Found non-literal argument in require' });
3238
}

test/detect-child-process.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ tester.run(ruleName, rule, {
5050
function fn () {
5151
require('child_process').spawn(str)
5252
}`,
53+
`
54+
var child_process = require('child_process');
55+
var FOO = 'ls';
56+
child_process.exec(FOO);`,
57+
`
58+
import child_process from 'child_process';
59+
const FOO = 'ls';
60+
child_process.exec(FOO);`,
5361
],
5462
invalid: [
5563
{

test/detect-non-literal-fs-filename.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const RuleTester = require('eslint').RuleTester;
44
const tester = new RuleTester({
55
parserOptions: {
6-
ecmaVersion: 6,
6+
ecmaVersion: 13,
77
sourceType: 'module',
88
},
99
});
@@ -24,6 +24,51 @@ tester.run(ruleName, require(`../rules/${ruleName}`), {
2424
code: `var something = require('fs').readFile, readFile = require('foo').readFile;
2525
readFile(c);`,
2626
},
27+
{
28+
code: `
29+
import { promises as fsp } from 'fs';
30+
import fs from 'fs';
31+
import path from 'path';
32+
33+
const index = await fsp.readFile(path.resolve(__dirname, './index.html'), 'utf-8');
34+
const key = fs.readFileSync(path.join(__dirname, './ssl.key'));
35+
await fsp.writeFile(path.resolve(__dirname, './sitemap.xml'), sitemap);`,
36+
globals: {
37+
__dirname: 'readonly',
38+
},
39+
},
40+
{
41+
code: `
42+
import fs from 'fs';
43+
import path from 'path';
44+
const dirname = path.dirname(__filename)
45+
const key = fs.readFileSync(path.resolve(dirname, './index.html'));`,
46+
globals: {
47+
__filename: 'readonly',
48+
},
49+
},
50+
{
51+
code: `
52+
import fs from 'fs';
53+
const key = fs.readFileSync(\`\${process.cwd()}/path/to/foo.json\`);`,
54+
globals: {
55+
process: 'readonly',
56+
},
57+
},
58+
`
59+
import fs from 'fs';
60+
import path from 'path';
61+
import url from 'url';
62+
const dirname = path.dirname(url.fileURLToPath(import.meta.url));
63+
const html = fs.readFileSync(path.resolve(dirname, './index.html'), 'utf-8');`,
64+
{
65+
code: `
66+
import fs from 'fs';
67+
const pkg = fs.readFileSync(require.resolve('eslint/package.json'), 'utf-8');`,
68+
globals: {
69+
require: 'readonly',
70+
},
71+
},
2772
],
2873
invalid: [
2974
/// requires
@@ -141,5 +186,15 @@ tester.run(ruleName, require(`../rules/${ruleName}`), {
141186
code: "var fs = require('fs');\nfunction foo () {\nvar { readFile: something } = fs.promises;\nsomething(filename);\n}",
142187
errors: [{ message: 'Found readFile from package "fs" with non literal argument at index 0' }],
143188
},
189+
{
190+
code: `
191+
import fs from 'fs';
192+
import path from 'path';
193+
const key = fs.readFileSync(path.resolve(__dirname, foo));`,
194+
globals: {
195+
__filename: 'readonly',
196+
},
197+
errors: [{ message: 'Found readFileSync from package "fs" with non literal argument at index 0' }],
198+
},
144199
],
145200
});

test/detect-non-literal-regexp.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ const ruleName = 'detect-non-literal-regexp';
77
const invalid = "var a = new RegExp(c, 'i')";
88

99
tester.run(ruleName, require(`../rules/${ruleName}`), {
10-
valid: [{ code: "var a = new RegExp('ab+c', 'i')" }],
10+
valid: [
11+
{ code: "var a = new RegExp('ab+c', 'i')" },
12+
{
13+
code: `
14+
var source = 'ab+c'
15+
var a = new RegExp(source, 'i')`,
16+
},
17+
],
1118
invalid: [
1219
{
1320
code: invalid,

test/detect-non-literal-require.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,21 @@ const tester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
77
const ruleName = 'detect-non-literal-require';
88

99
tester.run(ruleName, require(`../rules/${ruleName}`), {
10-
valid: [{ code: "var a = require('b')" }, { code: 'var a = require(`b`)' }],
10+
valid: [
11+
{ code: "var a = require('b')" },
12+
{ code: 'var a = require(`b`)' },
13+
{
14+
code: `
15+
const d = 'debounce'
16+
var a = require(\`lodash/\${d}\`)`,
17+
},
18+
{
19+
code: "const utils = require(__dirname + '/utils');",
20+
globals: {
21+
__dirname: 'readonly',
22+
},
23+
},
24+
],
1125
invalid: [
1226
{
1327
code: 'var a = require(c)',

0 commit comments

Comments
 (0)