eslint-plugin-sonarjs 3.0.7 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/S125/rule.js +8 -7
- package/cjs/S7790/rule.js +21 -1
- package/cjs/{S3854 → S8441}/generated-meta.js +5 -5
- package/cjs/{S3854 → S8441}/meta.js +4 -2
- package/cjs/S8441/rule.js +132 -0
- package/cjs/helpers/ancestor.js +11 -0
- package/cjs/helpers/module-ts.js +7 -2
- package/cjs/plugin-rules.js +450 -454
- package/docs/arrow-function-convention.md +4 -4
- package/docs/class-name.md +3 -3
- package/docs/comment-regex.md +5 -5
- package/docs/content-length.md +4 -4
- package/docs/cyclomatic-complexity.md +3 -3
- package/docs/expression-complexity.md +3 -3
- package/docs/file-header.md +4 -4
- package/docs/function-name.md +3 -3
- package/docs/max-lines-per-function.md +3 -3
- package/docs/max-lines.md +3 -3
- package/docs/max-union-size.md +3 -3
- package/docs/nested-control-flow.md +3 -3
- package/docs/new-operator-misuse.md +3 -3
- package/docs/no-duplicate-string.md +4 -4
- package/docs/no-hardcoded-passwords.md +3 -3
- package/docs/no-hardcoded-secrets.md +4 -4
- package/docs/no-implicit-dependencies.md +3 -3
- package/docs/no-intrusive-permissions.md +3 -3
- package/docs/no-nested-functions.md +3 -3
- package/docs/{code-eval.md → no-session-cookies-on-static-assets.md} +2 -2
- package/docs/regex-complexity.md +3 -3
- package/docs/variable-name.md +3 -3
- package/package.json +1 -37
- package/types/S125/rule.d.ts +1 -1
- package/types/{S1523 → S8441}/generated-meta.d.ts +2 -2
- package/types/S8441/meta.d.ts +3 -0
- package/types/helpers/ancestor.d.ts +2 -0
- package/types/plugin-rules.d.ts +1 -6
- package/cjs/S1523/generated-meta.js +0 -51
- package/cjs/S1523/meta.js +0 -21
- package/cjs/S1523/rule.js +0 -105
- package/cjs/S3723/config.js +0 -25
- package/cjs/S3723/generated-meta.js +0 -51
- package/cjs/S3723/index.js +0 -21
- package/cjs/S3723/meta.js +0 -37
- package/cjs/S3723/rule.js +0 -64
- package/cjs/S3854/index.js +0 -21
- package/cjs/S3854/rule.js +0 -68
- package/cjs/external/core.js +0 -23
- package/docs/enforce-trailing-comma.md +0 -25
- package/docs/super-invocation.md +0 -7
- package/types/S1523/meta.d.ts +0 -2
- package/types/S3723/config.d.ts +0 -3
- package/types/S3723/generated-meta.d.ts +0 -17
- package/types/S3723/index.d.ts +0 -1
- package/types/S3723/meta.d.ts +0 -4
- package/types/S3723/rule.d.ts +0 -8
- package/types/S3854/generated-meta.d.ts +0 -17
- package/types/S3854/index.d.ts +0 -1
- package/types/S3854/meta.d.ts +0 -2
- package/types/S3854/rule.d.ts +0 -2
- package/types/external/core.d.ts +0 -1
- /package/cjs/{S1523 → S8441}/index.js +0 -0
- /package/types/{S1523 → S8441}/index.d.ts +0 -0
- /package/types/{S1523 → S8441}/rule.d.ts +0 -0
package/cjs/S125/rule.js
CHANGED
|
@@ -54,7 +54,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
54
54
|
};
|
|
55
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
56
|
exports.rule = void 0;
|
|
57
|
-
const eslint_1 = require("eslint");
|
|
58
57
|
const index_js_1 = require("../helpers/index.js");
|
|
59
58
|
const meta = __importStar(require("./generated-meta.js"));
|
|
60
59
|
const index_js_2 = require("../helpers/recognizers/index.js");
|
|
@@ -135,7 +134,7 @@ exports.rule = {
|
|
|
135
134
|
};
|
|
136
135
|
},
|
|
137
136
|
};
|
|
138
|
-
function isExpressionExclusion(statement, value, program) {
|
|
137
|
+
function isExpressionExclusion(statement, value, program, context) {
|
|
139
138
|
if (statement.type === 'ExpressionStatement') {
|
|
140
139
|
const expression = statement.expression;
|
|
141
140
|
if (expression.type === 'Identifier' ||
|
|
@@ -144,18 +143,20 @@ function isExpressionExclusion(statement, value, program) {
|
|
|
144
143
|
isExcludedLiteral(expression)) {
|
|
145
144
|
return true;
|
|
146
145
|
}
|
|
147
|
-
// Only construct SourceCode when we need getLastToken
|
|
148
|
-
|
|
146
|
+
// Only construct SourceCode when we need getLastToken.
|
|
147
|
+
// Access the constructor from context to avoid a static runtime import of 'eslint'.
|
|
148
|
+
const SourceCodeClass = context.sourceCode.constructor;
|
|
149
|
+
const code = new SourceCodeClass(value, program);
|
|
149
150
|
return !code.getLastToken(statement, token => token.value === ';');
|
|
150
151
|
}
|
|
151
152
|
return false;
|
|
152
153
|
}
|
|
153
|
-
function isExclusion(parsedBody, value, program) {
|
|
154
|
+
function isExclusion(parsedBody, value, program, context) {
|
|
154
155
|
if (parsedBody.length === 1) {
|
|
155
156
|
const singleStatement = parsedBody[0];
|
|
156
157
|
return (EXCLUDED_STATEMENTS.has(singleStatement.type) ||
|
|
157
158
|
isReturnThrowExclusion(singleStatement) ||
|
|
158
|
-
isExpressionExclusion(singleStatement, value, program));
|
|
159
|
+
isExpressionExclusion(singleStatement, value, program, context));
|
|
159
160
|
}
|
|
160
161
|
return false;
|
|
161
162
|
}
|
|
@@ -174,7 +175,7 @@ function containsCode(value, context) {
|
|
|
174
175
|
const parser = context.languageOptions?.parserOptions?.parser ?? context.languageOptions?.parser;
|
|
175
176
|
const result = 'parse' in parser ? parser.parse(value, options) : parser.parseForESLint(value, options).ast;
|
|
176
177
|
const program = result;
|
|
177
|
-
return program.body.length > 0 && !isExclusion(program.body, value, program);
|
|
178
|
+
return program.body.length > 0 && !isExclusion(program.body, value, program, context);
|
|
178
179
|
}
|
|
179
180
|
catch {
|
|
180
181
|
return false;
|
package/cjs/S7790/rule.js
CHANGED
|
@@ -70,7 +70,10 @@ exports.rule = {
|
|
|
70
70
|
CallExpression: (node) => {
|
|
71
71
|
const callExpression = node;
|
|
72
72
|
const fqn = (0, index_js_1.getFullyQualifiedName)(context, callExpression);
|
|
73
|
-
if (fqn &&
|
|
73
|
+
if (fqn &&
|
|
74
|
+
templatingFqns.has(fqn) &&
|
|
75
|
+
!isCallingFunctionResult(context, callExpression) &&
|
|
76
|
+
isQuestionable(callExpression)) {
|
|
74
77
|
context.report({
|
|
75
78
|
messageId: 'reviewDynamicTemplate',
|
|
76
79
|
node: callExpression.callee,
|
|
@@ -80,6 +83,23 @@ exports.rule = {
|
|
|
80
83
|
};
|
|
81
84
|
},
|
|
82
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Returns true when the callee is a variable holding the result of a prior call,
|
|
88
|
+
* e.g. `const fn = pug.compile(tpl); fn(data);` — fn(data) is not a direct
|
|
89
|
+
* templating call, so we should not flag it again.
|
|
90
|
+
*/
|
|
91
|
+
function isCallingFunctionResult(context, callExpression) {
|
|
92
|
+
const callee = callExpression.callee;
|
|
93
|
+
if (callee.type !== 'Identifier') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const variable = (0, index_js_1.getVariableFromScope)(context.sourceCode.getScope(callee), callee.name);
|
|
97
|
+
if (!variable || variable.defs.some(def => def.type === 'ImportBinding')) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const writeRef = (0, index_js_1.getUniqueWriteReference)(variable);
|
|
101
|
+
return writeRef?.type === 'CallExpression';
|
|
102
|
+
}
|
|
83
103
|
function isQuestionable(node, index = 0) {
|
|
84
104
|
const args = node.arguments;
|
|
85
105
|
const templateString = args[index];
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* You should have received a copy of the Sonar Source-Available License
|
|
16
16
|
* along with this program; if not, see https://sonarsource.com/license/ssal/
|
|
17
17
|
*/
|
|
18
|
-
// https://sonarsource.github.io/rspec/#/rspec/
|
|
18
|
+
// https://sonarsource.github.io/rspec/#/rspec/S8441/javascript
|
|
19
19
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
20
|
if (k2 === undefined) k2 = k;
|
|
21
21
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -36,16 +36,16 @@ __exportStar(require("./meta.js"), exports);
|
|
|
36
36
|
exports.meta = {
|
|
37
37
|
type: 'problem',
|
|
38
38
|
docs: {
|
|
39
|
-
description: '
|
|
39
|
+
description: 'Static Assets should not serve session cookies',
|
|
40
40
|
recommended: true,
|
|
41
|
-
url: 'https://sonarsource.github.io/rspec/#/rspec/
|
|
41
|
+
url: 'https://sonarsource.github.io/rspec/#/rspec/S8441/javascript',
|
|
42
42
|
requiresTypeChecking: false,
|
|
43
43
|
},
|
|
44
44
|
fixable: undefined,
|
|
45
45
|
deprecated: false,
|
|
46
46
|
defaultOptions: [],
|
|
47
47
|
};
|
|
48
|
-
exports.sonarKey = '
|
|
49
|
-
exports.scope = '
|
|
48
|
+
exports.sonarKey = 'S8441';
|
|
49
|
+
exports.scope = 'All';
|
|
50
50
|
exports.languages = ['js', 'ts'];
|
|
51
51
|
exports.requiredDependency = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.eslintId = exports.implementation = void 0;
|
|
3
|
+
exports.hasSecondaries = exports.eslintId = exports.implementation = void 0;
|
|
4
4
|
/*
|
|
5
5
|
* SonarQube JavaScript Plugin
|
|
6
6
|
* Copyright (C) 2011-2025 SonarSource Sàrl
|
|
@@ -17,5 +17,7 @@ exports.eslintId = exports.implementation = void 0;
|
|
|
17
17
|
* You should have received a copy of the Sonar Source-Available License
|
|
18
18
|
* along with this program; if not, see https://sonarsource.com/license/ssal/
|
|
19
19
|
*/
|
|
20
|
+
// https://sonarsource.github.io/rspec/#/rspec/S8441/javascript
|
|
20
21
|
exports.implementation = 'original';
|
|
21
|
-
exports.eslintId = '
|
|
22
|
+
exports.eslintId = 'no-session-cookies-on-static-assets';
|
|
23
|
+
exports.hasSecondaries = true;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* SonarQube JavaScript Plugin
|
|
4
|
+
* Copyright (C) 2011-2025 SonarSource Sàrl
|
|
5
|
+
* mailto:info AT sonarsource DOT com
|
|
6
|
+
*
|
|
7
|
+
* This program is free software; you can redistribute it and/or
|
|
8
|
+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
13
|
+
* See the Sonar Source-Available License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the Sonar Source-Available License
|
|
16
|
+
* along with this program; if not, see https://sonarsource.com/license/ssal/
|
|
17
|
+
*/
|
|
18
|
+
// https://sonarsource.github.io/rspec/#/rspec/S8441/javascript
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.rule = void 0;
|
|
54
|
+
const index_js_1 = require("../helpers/index.js");
|
|
55
|
+
// If a rule has a schema, use this to extract it.
|
|
56
|
+
// import { FromSchema } from 'json-schema-to-ts';
|
|
57
|
+
const meta = __importStar(require("./generated-meta.js"));
|
|
58
|
+
const messages = {
|
|
59
|
+
sessionSecondaryLocation: 'Session middleware declared here.',
|
|
60
|
+
moveStaticBeforeSession: 'Move this static middleware before the session middleware.',
|
|
61
|
+
};
|
|
62
|
+
// Extend this list to support additional session-cookie middlewares.
|
|
63
|
+
const SESSION_MIDDLEWARES = ['express-session', 'cookie-session'];
|
|
64
|
+
const STATIC_MIDDLEWARES = ['express.static'];
|
|
65
|
+
exports.rule = {
|
|
66
|
+
meta: (0, index_js_1.generateMeta)(meta, { messages }),
|
|
67
|
+
create(context) {
|
|
68
|
+
let app = null;
|
|
69
|
+
let lastSessionMiddleware = null;
|
|
70
|
+
const scopeStack = [];
|
|
71
|
+
function isMiddleware(context, node, names) {
|
|
72
|
+
if (node.type !== 'CallExpression') {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const fqn = (0, index_js_1.getFullyQualifiedName)(context, node);
|
|
76
|
+
return fqn !== null && names.includes(fqn);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
Program() {
|
|
80
|
+
app = null;
|
|
81
|
+
lastSessionMiddleware = null;
|
|
82
|
+
scopeStack.length = 0;
|
|
83
|
+
},
|
|
84
|
+
':function'(node) {
|
|
85
|
+
scopeStack.push({ app, lastSessionMiddleware });
|
|
86
|
+
const injectedApp = index_js_1.Express.attemptFindAppInjection(node, context, node);
|
|
87
|
+
if (injectedApp) {
|
|
88
|
+
app = injectedApp;
|
|
89
|
+
lastSessionMiddleware = null;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
':function:exit'() {
|
|
93
|
+
const previous = scopeStack.pop();
|
|
94
|
+
if (previous) {
|
|
95
|
+
app = previous.app;
|
|
96
|
+
lastSessionMiddleware = previous.lastSessionMiddleware;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
VariableDeclarator(node) {
|
|
100
|
+
const varDecl = node;
|
|
101
|
+
const instantiatedApp = index_js_1.Express.attemptFindAppInstantiation(varDecl, context);
|
|
102
|
+
if (instantiatedApp) {
|
|
103
|
+
app = instantiatedApp;
|
|
104
|
+
lastSessionMiddleware = null;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
CallExpression(node) {
|
|
108
|
+
if (!app) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const callExpr = node;
|
|
112
|
+
if (!(0, index_js_1.isMethodInvocation)(callExpr, app.name, 'use', 1)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const flattenedArgs = (0, index_js_1.flattenArgs)(context, callExpr.arguments);
|
|
116
|
+
for (const middlewareNode of flattenedArgs) {
|
|
117
|
+
if (isMiddleware(context, middlewareNode, SESSION_MIDDLEWARES)) {
|
|
118
|
+
lastSessionMiddleware = callExpr;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (lastSessionMiddleware && isMiddleware(context, middlewareNode, STATIC_MIDDLEWARES)) {
|
|
122
|
+
(0, index_js_1.report)(context, {
|
|
123
|
+
node: callExpr,
|
|
124
|
+
messageId: 'moveStaticBeforeSession',
|
|
125
|
+
message: messages.moveStaticBeforeSession,
|
|
126
|
+
}, [(0, index_js_1.toSecondaryLocation)(lastSessionMiddleware, messages.sessionSecondaryLocation)]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
};
|
package/cjs/helpers/ancestor.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.ancestorsChain = ancestorsChain;
|
|
|
7
7
|
exports.getParent = getParent;
|
|
8
8
|
exports.getNodeParent = getNodeParent;
|
|
9
9
|
exports.childrenOf = childrenOf;
|
|
10
|
+
exports.isTsAncestor = isTsAncestor;
|
|
10
11
|
const ast_js_1 = require("./ast.js");
|
|
11
12
|
function findFirstMatchingLocalAncestor(node, predicate) {
|
|
12
13
|
return localAncestorsChain(node).find(predicate);
|
|
@@ -76,3 +77,13 @@ function childrenOf(node, visitorKeys) {
|
|
|
76
77
|
}
|
|
77
78
|
return children.filter(Boolean);
|
|
78
79
|
}
|
|
80
|
+
function isTsAncestor(candidate, node) {
|
|
81
|
+
let current = node.parent;
|
|
82
|
+
while (current) {
|
|
83
|
+
if (current === candidate) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
current = current.parent;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
package/cjs/helpers/module-ts.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.getFullyQualifiedNameTS = getFullyQualifiedNameTS;
|
|
|
21
21
|
* along with this program; if not, see https://sonarsource.com/license/ssal/
|
|
22
22
|
*/
|
|
23
23
|
const typescript_1 = __importDefault(require("typescript"));
|
|
24
|
+
const ancestor_js_1 = require("./ancestor.js");
|
|
24
25
|
const module_js_1 = require("./module.js");
|
|
25
26
|
function getFullyQualifiedNameTS(services, rootNode) {
|
|
26
27
|
const result = [];
|
|
@@ -108,8 +109,12 @@ function getFullyQualifiedNameTS(services, rootNode) {
|
|
|
108
109
|
case typescript_1.default.SyntaxKind.Identifier: {
|
|
109
110
|
const identifierSymbol = services.program.getTypeChecker().getSymbolAtLocation(node);
|
|
110
111
|
const declaration = identifierSymbol?.declarations?.at(0);
|
|
111
|
-
// Handle: no symbol info, compiler module,
|
|
112
|
-
|
|
112
|
+
// Handle: no symbol info, compiler module, self-referential declaration (e.g., `module` in CommonJS),
|
|
113
|
+
// or declaration that contains the root node (e.g., `const geo = geo(request)` where import is shadowed)
|
|
114
|
+
if (isCompilerModule(identifierSymbol) ||
|
|
115
|
+
!declaration ||
|
|
116
|
+
declaration === node ||
|
|
117
|
+
(0, ancestor_js_1.isTsAncestor)(declaration, rootNode)) {
|
|
113
118
|
result.push(node.text);
|
|
114
119
|
return returnResult();
|
|
115
120
|
}
|