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.
Files changed (63) hide show
  1. package/cjs/S125/rule.js +8 -7
  2. package/cjs/S7790/rule.js +21 -1
  3. package/cjs/{S3854 → S8441}/generated-meta.js +5 -5
  4. package/cjs/{S3854 → S8441}/meta.js +4 -2
  5. package/cjs/S8441/rule.js +132 -0
  6. package/cjs/helpers/ancestor.js +11 -0
  7. package/cjs/helpers/module-ts.js +7 -2
  8. package/cjs/plugin-rules.js +450 -454
  9. package/docs/arrow-function-convention.md +4 -4
  10. package/docs/class-name.md +3 -3
  11. package/docs/comment-regex.md +5 -5
  12. package/docs/content-length.md +4 -4
  13. package/docs/cyclomatic-complexity.md +3 -3
  14. package/docs/expression-complexity.md +3 -3
  15. package/docs/file-header.md +4 -4
  16. package/docs/function-name.md +3 -3
  17. package/docs/max-lines-per-function.md +3 -3
  18. package/docs/max-lines.md +3 -3
  19. package/docs/max-union-size.md +3 -3
  20. package/docs/nested-control-flow.md +3 -3
  21. package/docs/new-operator-misuse.md +3 -3
  22. package/docs/no-duplicate-string.md +4 -4
  23. package/docs/no-hardcoded-passwords.md +3 -3
  24. package/docs/no-hardcoded-secrets.md +4 -4
  25. package/docs/no-implicit-dependencies.md +3 -3
  26. package/docs/no-intrusive-permissions.md +3 -3
  27. package/docs/no-nested-functions.md +3 -3
  28. package/docs/{code-eval.md → no-session-cookies-on-static-assets.md} +2 -2
  29. package/docs/regex-complexity.md +3 -3
  30. package/docs/variable-name.md +3 -3
  31. package/package.json +1 -37
  32. package/types/S125/rule.d.ts +1 -1
  33. package/types/{S1523 → S8441}/generated-meta.d.ts +2 -2
  34. package/types/S8441/meta.d.ts +3 -0
  35. package/types/helpers/ancestor.d.ts +2 -0
  36. package/types/plugin-rules.d.ts +1 -6
  37. package/cjs/S1523/generated-meta.js +0 -51
  38. package/cjs/S1523/meta.js +0 -21
  39. package/cjs/S1523/rule.js +0 -105
  40. package/cjs/S3723/config.js +0 -25
  41. package/cjs/S3723/generated-meta.js +0 -51
  42. package/cjs/S3723/index.js +0 -21
  43. package/cjs/S3723/meta.js +0 -37
  44. package/cjs/S3723/rule.js +0 -64
  45. package/cjs/S3854/index.js +0 -21
  46. package/cjs/S3854/rule.js +0 -68
  47. package/cjs/external/core.js +0 -23
  48. package/docs/enforce-trailing-comma.md +0 -25
  49. package/docs/super-invocation.md +0 -7
  50. package/types/S1523/meta.d.ts +0 -2
  51. package/types/S3723/config.d.ts +0 -3
  52. package/types/S3723/generated-meta.d.ts +0 -17
  53. package/types/S3723/index.d.ts +0 -1
  54. package/types/S3723/meta.d.ts +0 -4
  55. package/types/S3723/rule.d.ts +0 -8
  56. package/types/S3854/generated-meta.d.ts +0 -17
  57. package/types/S3854/index.d.ts +0 -1
  58. package/types/S3854/meta.d.ts +0 -2
  59. package/types/S3854/rule.d.ts +0 -2
  60. package/types/external/core.d.ts +0 -1
  61. /package/cjs/{S1523 → S8441}/index.js +0 -0
  62. /package/types/{S1523 → S8441}/index.d.ts +0 -0
  63. /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
- const code = new eslint_1.SourceCode(value, program);
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 && templatingFqns.has(fqn) && isQuestionable(callExpression)) {
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/S3854/javascript
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: '"super()" should be invoked appropriately',
39
+ description: 'Static Assets should not serve session cookies',
40
40
  recommended: true,
41
- url: 'https://sonarsource.github.io/rspec/#/rspec/S3854/javascript',
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 = 'S3854';
49
- exports.scope = 'Main';
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 = 'super-invocation';
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
+ };
@@ -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
+ }
@@ -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, or self-referential declaration (e.g., `module` in CommonJS)
112
- if (isCompilerModule(identifierSymbol) || !declaration || declaration === node) {
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
  }