truecourse 0.5.7 → 0.5.8-windows.2
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/README.md +27 -1
- package/cli.mjs +1970 -181
- package/package.json +1 -1
- package/public/assets/{index-DaPvUT-_.js → index-Bw6ucreX.js} +13 -13
- package/public/index.html +1 -1
- package/server.mjs +1776 -116
- package/skills/truecourse/truecourse-analyze/SKILL.md +8 -0
- package/skills/truecourse/truecourse-hooks/SKILL.md +3 -3
package/server.mjs
CHANGED
|
@@ -40750,7 +40750,12 @@ var init_language_config = __esm({
|
|
|
40750
40750
|
classQuery: `
|
|
40751
40751
|
(class_declaration) @class
|
|
40752
40752
|
(class) @class
|
|
40753
|
-
|
|
40753
|
+
`,
|
|
40754
|
+
// Skip pre-built/minified bundles. They have no analytical value (build
|
|
40755
|
+
// artifacts of source already in the repo or vendored libs) and a single
|
|
40756
|
+
// ~1MB minified line can blow past V8's max string length when the LLM
|
|
40757
|
+
// context router extracts thousands of arrow_function bodies from it.
|
|
40758
|
+
ignorePatterns: ["**/*.min.js", "**/*.min.cjs", "**/*.min.mjs"]
|
|
40754
40759
|
};
|
|
40755
40760
|
PYTHON_CONFIG = {
|
|
40756
40761
|
name: "python",
|
|
@@ -40793,9 +40798,55 @@ var init_language_config = __esm({
|
|
|
40793
40798
|
});
|
|
40794
40799
|
|
|
40795
40800
|
// packages/analyzer/dist/file-discovery.js
|
|
40796
|
-
import { existsSync as existsSync2, readFileSync, readdirSync, statSync as statSync2 } from "fs";
|
|
40801
|
+
import { existsSync as existsSync2, openSync, readFileSync, readSync, readdirSync, statSync as statSync2, closeSync } from "fs";
|
|
40797
40802
|
import { execFileSync } from "child_process";
|
|
40798
40803
|
import { join, relative, resolve } from "path";
|
|
40804
|
+
function looksLikeBuildArtifact(absPath) {
|
|
40805
|
+
const slash = absPath.lastIndexOf("/");
|
|
40806
|
+
const basename2 = slash >= 0 ? absPath.slice(slash + 1) : absPath;
|
|
40807
|
+
return /\.(bundle|production|development)\.(js|cjs|mjs|jsx|ts|tsx|mts|cts)$/i.test(basename2);
|
|
40808
|
+
}
|
|
40809
|
+
function looksMinified(absPath) {
|
|
40810
|
+
const dotIdx = absPath.lastIndexOf(".");
|
|
40811
|
+
if (dotIdx < 0)
|
|
40812
|
+
return false;
|
|
40813
|
+
const ext2 = absPath.slice(dotIdx);
|
|
40814
|
+
if (!SNIFFED_EXTS.has(ext2))
|
|
40815
|
+
return false;
|
|
40816
|
+
if (looksLikeBuildArtifact(absPath))
|
|
40817
|
+
return true;
|
|
40818
|
+
let size;
|
|
40819
|
+
try {
|
|
40820
|
+
size = statSync2(absPath).size;
|
|
40821
|
+
} catch {
|
|
40822
|
+
return false;
|
|
40823
|
+
}
|
|
40824
|
+
if (size < MIN_SIZE_FOR_SNIFF)
|
|
40825
|
+
return false;
|
|
40826
|
+
let fd = null;
|
|
40827
|
+
try {
|
|
40828
|
+
fd = openSync(absPath, "r");
|
|
40829
|
+
const buf = Buffer.alloc(SNIFF_BYTES);
|
|
40830
|
+
const read = readSync(fd, buf, 0, SNIFF_BYTES, 0);
|
|
40831
|
+
let newlines = 0;
|
|
40832
|
+
for (let i = 0; i < read; i++) {
|
|
40833
|
+
if (buf[i] === 10)
|
|
40834
|
+
newlines++;
|
|
40835
|
+
if (newlines >= MIN_NEWLINES)
|
|
40836
|
+
return false;
|
|
40837
|
+
}
|
|
40838
|
+
return true;
|
|
40839
|
+
} catch {
|
|
40840
|
+
return false;
|
|
40841
|
+
} finally {
|
|
40842
|
+
if (fd !== null) {
|
|
40843
|
+
try {
|
|
40844
|
+
closeSync(fd);
|
|
40845
|
+
} catch {
|
|
40846
|
+
}
|
|
40847
|
+
}
|
|
40848
|
+
}
|
|
40849
|
+
}
|
|
40799
40850
|
function isInsideGitWorkTree(dir) {
|
|
40800
40851
|
try {
|
|
40801
40852
|
const out = execFileSync("git", ["-C", dir, "rev-parse", "--is-inside-work-tree"], {
|
|
@@ -40843,8 +40894,11 @@ function discoverFilesViaGit(dir) {
|
|
|
40843
40894
|
}
|
|
40844
40895
|
if (!isFile)
|
|
40845
40896
|
continue;
|
|
40846
|
-
if (detectLanguage(abs))
|
|
40847
|
-
|
|
40897
|
+
if (!detectLanguage(abs))
|
|
40898
|
+
continue;
|
|
40899
|
+
if (looksMinified(abs))
|
|
40900
|
+
continue;
|
|
40901
|
+
out.push(abs);
|
|
40848
40902
|
}
|
|
40849
40903
|
return out;
|
|
40850
40904
|
}
|
|
@@ -40933,8 +40987,11 @@ function discoverFilesViaWalker(dir) {
|
|
|
40933
40987
|
if (stat.isDirectory()) {
|
|
40934
40988
|
traverse(fullPath);
|
|
40935
40989
|
} else if (stat.isFile()) {
|
|
40936
|
-
if (detectLanguage(fullPath))
|
|
40937
|
-
|
|
40990
|
+
if (!detectLanguage(fullPath))
|
|
40991
|
+
continue;
|
|
40992
|
+
if (looksMinified(fullPath))
|
|
40993
|
+
continue;
|
|
40994
|
+
files.push(fullPath);
|
|
40938
40995
|
}
|
|
40939
40996
|
}
|
|
40940
40997
|
} catch {
|
|
@@ -40949,12 +41006,16 @@ function discoverFiles(dir) {
|
|
|
40949
41006
|
return viaGit;
|
|
40950
41007
|
return discoverFilesViaWalker(dir);
|
|
40951
41008
|
}
|
|
40952
|
-
var import_ignore;
|
|
41009
|
+
var import_ignore, SNIFFED_EXTS, MIN_SIZE_FOR_SNIFF, SNIFF_BYTES, MIN_NEWLINES;
|
|
40953
41010
|
var init_file_discovery = __esm({
|
|
40954
41011
|
"packages/analyzer/dist/file-discovery.js"() {
|
|
40955
41012
|
"use strict";
|
|
40956
41013
|
import_ignore = __toESM(require_ignore(), 1);
|
|
40957
41014
|
init_language_config();
|
|
41015
|
+
SNIFFED_EXTS = /* @__PURE__ */ new Set([".js", ".jsx", ".cjs", ".mjs", ".ts", ".tsx", ".mts", ".cts"]);
|
|
41016
|
+
MIN_SIZE_FOR_SNIFF = 15 * 1024;
|
|
41017
|
+
SNIFF_BYTES = 8 * 1024;
|
|
41018
|
+
MIN_NEWLINES = 4;
|
|
40958
41019
|
}
|
|
40959
41020
|
});
|
|
40960
41021
|
|
|
@@ -42724,6 +42785,15 @@ var init_typescript = __esm({
|
|
|
42724
42785
|
|
|
42725
42786
|
// packages/analyzer/dist/extractors/languages/javascript.js
|
|
42726
42787
|
import { Query as Query3 } from "web-tree-sitter";
|
|
42788
|
+
function isNestedInFunction2(node2) {
|
|
42789
|
+
let current = node2.parent;
|
|
42790
|
+
while (current) {
|
|
42791
|
+
if (FUNCTION_NODE_TYPES2.has(current.type))
|
|
42792
|
+
return true;
|
|
42793
|
+
current = current.parent;
|
|
42794
|
+
}
|
|
42795
|
+
return false;
|
|
42796
|
+
}
|
|
42727
42797
|
function extractFunctionName2(node2) {
|
|
42728
42798
|
const nameNode = node2.childForFieldName("name");
|
|
42729
42799
|
if (nameNode?.text)
|
|
@@ -42731,6 +42801,8 @@ function extractFunctionName2(node2) {
|
|
|
42731
42801
|
const parent = node2.parent;
|
|
42732
42802
|
if (!parent)
|
|
42733
42803
|
return "anonymous";
|
|
42804
|
+
if (isNestedInFunction2(node2))
|
|
42805
|
+
return "anonymous";
|
|
42734
42806
|
if (parent.type === "variable_declarator") {
|
|
42735
42807
|
const varName = parent.childForFieldName("name");
|
|
42736
42808
|
if (varName?.text)
|
|
@@ -42749,25 +42821,15 @@ function extractFunctionName2(node2) {
|
|
|
42749
42821
|
return prop.text;
|
|
42750
42822
|
}
|
|
42751
42823
|
}
|
|
42752
|
-
if (parent.type === "arguments") {
|
|
42753
|
-
const callNode = parent.parent;
|
|
42754
|
-
if (callNode?.type === "call_expression") {
|
|
42755
|
-
const callee = callNode.childForFieldName("function");
|
|
42756
|
-
if (callee?.type === "member_expression") {
|
|
42757
|
-
const method = callee.childForFieldName("property");
|
|
42758
|
-
if (method?.text)
|
|
42759
|
-
return `${method.text}_handler`;
|
|
42760
|
-
}
|
|
42761
|
-
}
|
|
42762
|
-
}
|
|
42763
42824
|
return "anonymous";
|
|
42764
42825
|
}
|
|
42765
42826
|
function isExported2(node2) {
|
|
42766
42827
|
let current = node2.parent;
|
|
42767
42828
|
while (current) {
|
|
42768
|
-
if (current.type === "export_statement")
|
|
42829
|
+
if (current.type === "export_statement")
|
|
42769
42830
|
return true;
|
|
42770
|
-
|
|
42831
|
+
if (FUNCTION_NODE_TYPES2.has(current.type))
|
|
42832
|
+
return false;
|
|
42771
42833
|
current = current.parent;
|
|
42772
42834
|
}
|
|
42773
42835
|
return false;
|
|
@@ -43112,12 +43174,22 @@ function extractJavaScriptExports(tree, _filePath) {
|
|
|
43112
43174
|
}
|
|
43113
43175
|
return exports;
|
|
43114
43176
|
}
|
|
43177
|
+
var FUNCTION_NODE_TYPES2;
|
|
43115
43178
|
var init_javascript = __esm({
|
|
43116
43179
|
"packages/analyzer/dist/extractors/languages/javascript.js"() {
|
|
43117
43180
|
"use strict";
|
|
43118
43181
|
init_language_config();
|
|
43119
43182
|
init_parser();
|
|
43120
43183
|
init_common();
|
|
43184
|
+
FUNCTION_NODE_TYPES2 = /* @__PURE__ */ new Set([
|
|
43185
|
+
"function_declaration",
|
|
43186
|
+
"function",
|
|
43187
|
+
"function_expression",
|
|
43188
|
+
"arrow_function",
|
|
43189
|
+
"generator_function_declaration",
|
|
43190
|
+
"generator_function",
|
|
43191
|
+
"method_definition"
|
|
43192
|
+
]);
|
|
43121
43193
|
}
|
|
43122
43194
|
});
|
|
43123
43195
|
|
|
@@ -43150,7 +43222,7 @@ function computePythonFunctionMetrics(node2) {
|
|
|
43150
43222
|
walkNesting(bodyNode, 0);
|
|
43151
43223
|
return { lineCount, statementCount, maxNestingDepth: maxNestingDepth2 };
|
|
43152
43224
|
}
|
|
43153
|
-
function
|
|
43225
|
+
function isNestedInFunction3(node2) {
|
|
43154
43226
|
let current = node2.parent;
|
|
43155
43227
|
while (current) {
|
|
43156
43228
|
if (current.type === "function_definition")
|
|
@@ -43217,7 +43289,7 @@ function extractPythonFunctions(tree, filePath) {
|
|
|
43217
43289
|
const name = node2.childForFieldName("name")?.text;
|
|
43218
43290
|
if (!name)
|
|
43219
43291
|
return;
|
|
43220
|
-
if (
|
|
43292
|
+
if (isNestedInFunction3(node2))
|
|
43221
43293
|
return;
|
|
43222
43294
|
const paramsNode = node2.childForFieldName("parameters");
|
|
43223
43295
|
const params = extractPythonParameters(paramsNode);
|
|
@@ -63082,6 +63154,19 @@ function isPropertyAccess(node2) {
|
|
|
63082
63154
|
}
|
|
63083
63155
|
if (parent.type === "keyword_argument" && parent.childForFieldName("name")?.id === node2.id)
|
|
63084
63156
|
return true;
|
|
63157
|
+
if (parent.type === "jsx_attribute" && parent.namedChild(0)?.id === node2.id)
|
|
63158
|
+
return true;
|
|
63159
|
+
if (parent.type === "jsx_opening_element" || parent.type === "jsx_closing_element" || parent.type === "jsx_self_closing_element") {
|
|
63160
|
+
const tagName = parent.childForFieldName("name");
|
|
63161
|
+
if (tagName?.id === node2.id) {
|
|
63162
|
+
const text = node2.text;
|
|
63163
|
+
if (text.length > 0) {
|
|
63164
|
+
const first2 = text[0];
|
|
63165
|
+
if (first2 === first2.toLowerCase() || text.includes("-"))
|
|
63166
|
+
return true;
|
|
63167
|
+
}
|
|
63168
|
+
}
|
|
63169
|
+
}
|
|
63085
63170
|
return false;
|
|
63086
63171
|
}
|
|
63087
63172
|
function isJsDeclarationPosition(node2) {
|
|
@@ -63115,6 +63200,10 @@ function isJsDeclarationPosition(node2) {
|
|
|
63115
63200
|
}
|
|
63116
63201
|
if (parent.type === "catch_clause")
|
|
63117
63202
|
return true;
|
|
63203
|
+
if (parent.type === "for_in_statement" && parent.childForFieldName("left")?.id === node2.id)
|
|
63204
|
+
return true;
|
|
63205
|
+
if (parent.type === "arrow_function" && parent.childForFieldName("parameter")?.id === node2.id)
|
|
63206
|
+
return true;
|
|
63118
63207
|
return false;
|
|
63119
63208
|
}
|
|
63120
63209
|
function isPyDeclarationPosition(node2) {
|
|
@@ -63254,9 +63343,9 @@ function buildScopeTree(rootNode, language) {
|
|
|
63254
63343
|
return;
|
|
63255
63344
|
}
|
|
63256
63345
|
if ((parent.type === "function_declaration" || parent.type === "generator_function_declaration") && parent.childForFieldName("name")?.id === node2.id) {
|
|
63257
|
-
const
|
|
63258
|
-
if (!
|
|
63259
|
-
createVariable(node2.text, "function", node2,
|
|
63346
|
+
const enclosing = scope.parent ? findFunctionScope(scope.parent) : scope;
|
|
63347
|
+
if (!enclosing.variables.has(node2.text)) {
|
|
63348
|
+
createVariable(node2.text, "function", node2, enclosing, false);
|
|
63260
63349
|
}
|
|
63261
63350
|
return;
|
|
63262
63351
|
}
|
|
@@ -63276,6 +63365,13 @@ function buildScopeTree(rootNode, language) {
|
|
|
63276
63365
|
}
|
|
63277
63366
|
return;
|
|
63278
63367
|
}
|
|
63368
|
+
if (parent.type === "arrow_function" && parent.childForFieldName("parameter")?.id === node2.id) {
|
|
63369
|
+
const funcScope = nodeToScope.get(parent.id);
|
|
63370
|
+
if (funcScope && !funcScope.variables.has(node2.text)) {
|
|
63371
|
+
createVariable(node2.text, "parameter", node2, funcScope, false);
|
|
63372
|
+
}
|
|
63373
|
+
return;
|
|
63374
|
+
}
|
|
63279
63375
|
if (parent.type === "rest_pattern" || parent.type === "rest_element") {
|
|
63280
63376
|
const funcNode = findFunctionAncestor(node2);
|
|
63281
63377
|
if (funcNode) {
|
|
@@ -63298,6 +63394,68 @@ function buildScopeTree(rootNode, language) {
|
|
|
63298
63394
|
return;
|
|
63299
63395
|
}
|
|
63300
63396
|
}
|
|
63397
|
+
if (parent.type === "array_pattern" || parent.type === "object_pattern") {
|
|
63398
|
+
if (isInFormalParameters(node2)) {
|
|
63399
|
+
const funcNode = findFunctionAncestor(node2);
|
|
63400
|
+
if (funcNode) {
|
|
63401
|
+
const funcScope = nodeToScope.get(funcNode.id);
|
|
63402
|
+
if (funcScope && !funcScope.variables.has(node2.text)) {
|
|
63403
|
+
createVariable(node2.text, "parameter", node2, funcScope, false);
|
|
63404
|
+
}
|
|
63405
|
+
}
|
|
63406
|
+
return;
|
|
63407
|
+
}
|
|
63408
|
+
let ancestor = parent.parent;
|
|
63409
|
+
let declarator = null;
|
|
63410
|
+
let forIn = null;
|
|
63411
|
+
while (ancestor) {
|
|
63412
|
+
if (ancestor.type === "variable_declarator") {
|
|
63413
|
+
declarator = ancestor;
|
|
63414
|
+
break;
|
|
63415
|
+
}
|
|
63416
|
+
if (ancestor.type === "for_in_statement" && ancestor.childForFieldName("left")?.id === parent.id) {
|
|
63417
|
+
forIn = ancestor;
|
|
63418
|
+
break;
|
|
63419
|
+
}
|
|
63420
|
+
if (ancestor.type === "function_declaration" || ancestor.type === "function_expression" || ancestor.type === "arrow_function" || ancestor.type === "method_definition" || ancestor.type === "program")
|
|
63421
|
+
break;
|
|
63422
|
+
ancestor = ancestor.parent;
|
|
63423
|
+
}
|
|
63424
|
+
if (declarator) {
|
|
63425
|
+
const grandparent = declarator.parent;
|
|
63426
|
+
if (grandparent?.type === "variable_declaration") {
|
|
63427
|
+
const targetScope = findFunctionScope(scope);
|
|
63428
|
+
if (!targetScope.variables.has(node2.text)) {
|
|
63429
|
+
createVariable(node2.text, "var", node2, targetScope, false);
|
|
63430
|
+
}
|
|
63431
|
+
} else if (grandparent?.type === "lexical_declaration") {
|
|
63432
|
+
const keyword = grandparent.child(0)?.text;
|
|
63433
|
+
const kind = keyword === "const" ? "const" : "let";
|
|
63434
|
+
if (!scope.variables.has(node2.text)) {
|
|
63435
|
+
createVariable(node2.text, kind, node2, scope, false);
|
|
63436
|
+
}
|
|
63437
|
+
}
|
|
63438
|
+
return;
|
|
63439
|
+
}
|
|
63440
|
+
if (forIn) {
|
|
63441
|
+
const kindText = forIn.childForFieldName("kind")?.text;
|
|
63442
|
+
const kind = kindText === "var" ? "var" : kindText === "const" ? "const" : kindText === "let" ? "let" : "for-variable";
|
|
63443
|
+
const targetScope = kind === "var" ? findFunctionScope(scope) : scope;
|
|
63444
|
+
if (!targetScope.variables.has(node2.text)) {
|
|
63445
|
+
createVariable(node2.text, kind, node2, targetScope, false);
|
|
63446
|
+
}
|
|
63447
|
+
return;
|
|
63448
|
+
}
|
|
63449
|
+
}
|
|
63450
|
+
if (parent.type === "for_in_statement" && parent.childForFieldName("left")?.id === node2.id) {
|
|
63451
|
+
const kindText = parent.childForFieldName("kind")?.text;
|
|
63452
|
+
const kind = kindText === "var" ? "var" : kindText === "const" ? "const" : kindText === "let" ? "let" : "for-variable";
|
|
63453
|
+
const targetScope = kind === "var" ? findFunctionScope(scope) : scope;
|
|
63454
|
+
if (!targetScope.variables.has(node2.text)) {
|
|
63455
|
+
createVariable(node2.text, kind, node2, targetScope, false);
|
|
63456
|
+
}
|
|
63457
|
+
return;
|
|
63458
|
+
}
|
|
63301
63459
|
if (parent.type === "shorthand_property_identifier_pattern") {
|
|
63302
63460
|
if (isInFormalParameters(node2)) {
|
|
63303
63461
|
const funcNode = findFunctionAncestor(node2);
|
|
@@ -63638,16 +63796,27 @@ function buildScopeTree(rootNode, language) {
|
|
|
63638
63796
|
if (isJs && node2.type === "shorthand_property_identifier_pattern") {
|
|
63639
63797
|
let ancestor = node2.parent;
|
|
63640
63798
|
let declarator = null;
|
|
63799
|
+
let isParam = false;
|
|
63800
|
+
let funcNode = null;
|
|
63641
63801
|
while (ancestor) {
|
|
63642
63802
|
if (ancestor.type === "variable_declarator") {
|
|
63643
63803
|
declarator = ancestor;
|
|
63644
63804
|
break;
|
|
63645
63805
|
}
|
|
63646
|
-
if (ancestor.type === "formal_parameters" || ancestor.type === "function_declaration" || ancestor.type === "arrow_function" || ancestor.type === "method_definition") {
|
|
63806
|
+
if (ancestor.type === "formal_parameters" || ancestor.type === "function_declaration" || ancestor.type === "function_expression" || ancestor.type === "arrow_function" || ancestor.type === "method_definition" || ancestor.type === "generator_function_declaration" || ancestor.type === "generator_function") {
|
|
63807
|
+
isParam = true;
|
|
63808
|
+
funcNode = ancestor.type === "formal_parameters" ? ancestor.parent : ancestor;
|
|
63647
63809
|
break;
|
|
63648
63810
|
}
|
|
63649
63811
|
ancestor = ancestor.parent;
|
|
63650
63812
|
}
|
|
63813
|
+
if (isParam && funcNode) {
|
|
63814
|
+
const funcScope = nodeToScope.get(funcNode.id);
|
|
63815
|
+
const name = node2.text;
|
|
63816
|
+
if (funcScope && !funcScope.variables.has(name)) {
|
|
63817
|
+
createVariable(name, "parameter", node2, funcScope, false);
|
|
63818
|
+
}
|
|
63819
|
+
}
|
|
63651
63820
|
if (declarator) {
|
|
63652
63821
|
const grandparent = declarator.parent;
|
|
63653
63822
|
const name = node2.text;
|
|
@@ -63717,7 +63886,7 @@ var init_known_globals = __esm({
|
|
|
63717
63886
|
"packages/analyzer/dist/data-flow/known-globals.js"() {
|
|
63718
63887
|
"use strict";
|
|
63719
63888
|
JS_GLOBALS = /* @__PURE__ */ new Set([
|
|
63720
|
-
// Browser
|
|
63889
|
+
// Browser — global namespaces
|
|
63721
63890
|
"window",
|
|
63722
63891
|
"document",
|
|
63723
63892
|
"navigator",
|
|
@@ -63725,19 +63894,109 @@ var init_known_globals = __esm({
|
|
|
63725
63894
|
"history",
|
|
63726
63895
|
"localStorage",
|
|
63727
63896
|
"sessionStorage",
|
|
63897
|
+
"screen",
|
|
63898
|
+
"performance",
|
|
63899
|
+
"crypto",
|
|
63900
|
+
"caches",
|
|
63901
|
+
"indexedDB",
|
|
63902
|
+
"matchMedia",
|
|
63903
|
+
// Browser — networking / fetch
|
|
63728
63904
|
"fetch",
|
|
63729
63905
|
"XMLHttpRequest",
|
|
63730
63906
|
"WebSocket",
|
|
63907
|
+
"EventSource",
|
|
63731
63908
|
"URL",
|
|
63732
63909
|
"URLSearchParams",
|
|
63910
|
+
"Headers",
|
|
63911
|
+
"Request",
|
|
63912
|
+
"Response",
|
|
63913
|
+
"FormData",
|
|
63914
|
+
"Blob",
|
|
63915
|
+
"File",
|
|
63916
|
+
"FileReader",
|
|
63917
|
+
"FileList",
|
|
63918
|
+
"AbortController",
|
|
63919
|
+
"AbortSignal",
|
|
63920
|
+
"ReadableStream",
|
|
63921
|
+
"WritableStream",
|
|
63922
|
+
"TransformStream",
|
|
63923
|
+
// Browser — events
|
|
63924
|
+
"Event",
|
|
63925
|
+
"CustomEvent",
|
|
63926
|
+
"EventTarget",
|
|
63927
|
+
"MessageEvent",
|
|
63928
|
+
"ErrorEvent",
|
|
63929
|
+
"CloseEvent",
|
|
63930
|
+
"ProgressEvent",
|
|
63931
|
+
"PointerEvent",
|
|
63932
|
+
"KeyboardEvent",
|
|
63933
|
+
"MouseEvent",
|
|
63934
|
+
"TouchEvent",
|
|
63935
|
+
"DragEvent",
|
|
63936
|
+
"WheelEvent",
|
|
63937
|
+
"FocusEvent",
|
|
63938
|
+
"InputEvent",
|
|
63939
|
+
"StorageEvent",
|
|
63940
|
+
"PopStateEvent",
|
|
63941
|
+
"HashChangeEvent",
|
|
63942
|
+
"BeforeUnloadEvent",
|
|
63943
|
+
// Browser — DOM
|
|
63944
|
+
"Node",
|
|
63945
|
+
"Element",
|
|
63946
|
+
"HTMLElement",
|
|
63947
|
+
"HTMLInputElement",
|
|
63948
|
+
"HTMLButtonElement",
|
|
63949
|
+
"HTMLFormElement",
|
|
63950
|
+
"HTMLSelectElement",
|
|
63951
|
+
"HTMLTextAreaElement",
|
|
63952
|
+
"HTMLAnchorElement",
|
|
63953
|
+
"HTMLImageElement",
|
|
63954
|
+
"HTMLCanvasElement",
|
|
63955
|
+
"HTMLVideoElement",
|
|
63956
|
+
"HTMLAudioElement",
|
|
63957
|
+
"HTMLDivElement",
|
|
63958
|
+
"HTMLSpanElement",
|
|
63959
|
+
"HTMLScriptElement",
|
|
63960
|
+
"HTMLStyleElement",
|
|
63961
|
+
"HTMLTemplateElement",
|
|
63962
|
+
"HTMLIFrameElement",
|
|
63963
|
+
"HTMLDialogElement",
|
|
63964
|
+
"DocumentFragment",
|
|
63965
|
+
"ShadowRoot",
|
|
63966
|
+
"Text",
|
|
63967
|
+
"Comment",
|
|
63968
|
+
"Range",
|
|
63969
|
+
"Selection",
|
|
63970
|
+
"MutationObserver",
|
|
63971
|
+
"IntersectionObserver",
|
|
63972
|
+
"ResizeObserver",
|
|
63973
|
+
"PerformanceObserver",
|
|
63974
|
+
"NodeList",
|
|
63975
|
+
"HTMLCollection",
|
|
63976
|
+
"DOMTokenList",
|
|
63977
|
+
"CanvasRenderingContext2D",
|
|
63978
|
+
"OffscreenCanvas",
|
|
63979
|
+
// Browser — timers / scheduling
|
|
63733
63980
|
"setTimeout",
|
|
63734
63981
|
"setInterval",
|
|
63735
63982
|
"clearTimeout",
|
|
63736
63983
|
"clearInterval",
|
|
63737
63984
|
"requestAnimationFrame",
|
|
63985
|
+
"cancelAnimationFrame",
|
|
63986
|
+
"requestIdleCallback",
|
|
63987
|
+
"cancelIdleCallback",
|
|
63988
|
+
// Browser — dialogs
|
|
63738
63989
|
"alert",
|
|
63739
63990
|
"confirm",
|
|
63740
63991
|
"prompt",
|
|
63992
|
+
// Browser — workers / messaging
|
|
63993
|
+
"Worker",
|
|
63994
|
+
"SharedWorker",
|
|
63995
|
+
"ServiceWorker",
|
|
63996
|
+
"BroadcastChannel",
|
|
63997
|
+
"MessageChannel",
|
|
63998
|
+
"MessagePort",
|
|
63999
|
+
"postMessage",
|
|
63741
64000
|
// Node.js
|
|
63742
64001
|
"process",
|
|
63743
64002
|
"global",
|
|
@@ -64111,8 +64370,13 @@ function buildDataFlowContext(rootNode, language) {
|
|
|
64111
64370
|
continue;
|
|
64112
64371
|
if (v.useSites.length === 0)
|
|
64113
64372
|
continue;
|
|
64373
|
+
if (v.declarationNode.parent?.type === "named_expression")
|
|
64374
|
+
continue;
|
|
64375
|
+
const directUseSites = v.useSites.filter((u) => !isInNestedFunctionScope(u.scope, v.scope));
|
|
64376
|
+
if (directUseSites.length === 0)
|
|
64377
|
+
continue;
|
|
64114
64378
|
const declPos = v.declarationNode.startIndex;
|
|
64115
|
-
const earliestUse = Math.min(...
|
|
64379
|
+
const earliestUse = Math.min(...directUseSites.map((u) => u.node.startIndex));
|
|
64116
64380
|
if (earliestUse < declPos) {
|
|
64117
64381
|
result.push(v);
|
|
64118
64382
|
}
|
|
@@ -64120,6 +64384,17 @@ function buildDataFlowContext(rootNode, language) {
|
|
|
64120
64384
|
cachedUsedBeforeDefined = result;
|
|
64121
64385
|
return result;
|
|
64122
64386
|
}
|
|
64387
|
+
function isInNestedFunctionScope(useScope, declScope) {
|
|
64388
|
+
if (useScope === declScope)
|
|
64389
|
+
return false;
|
|
64390
|
+
let cursor = useScope;
|
|
64391
|
+
while (cursor && cursor !== declScope) {
|
|
64392
|
+
if (cursor.kind === "function")
|
|
64393
|
+
return true;
|
|
64394
|
+
cursor = cursor.parent;
|
|
64395
|
+
}
|
|
64396
|
+
return false;
|
|
64397
|
+
}
|
|
64123
64398
|
function unusedVariables() {
|
|
64124
64399
|
if (cachedUnused)
|
|
64125
64400
|
return cachedUnused;
|
|
@@ -64703,7 +64978,72 @@ var init_insecure_cookie = __esm({
|
|
|
64703
64978
|
});
|
|
64704
64979
|
|
|
64705
64980
|
// packages/analyzer/dist/rules/security/visitors/javascript/disabled-auto-escaping.js
|
|
64706
|
-
|
|
64981
|
+
function isStaticStringLiteral(node2) {
|
|
64982
|
+
if (node2.type === "string")
|
|
64983
|
+
return true;
|
|
64984
|
+
if (node2.type === "template_string") {
|
|
64985
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
64986
|
+
const child = node2.namedChild(i);
|
|
64987
|
+
if (child?.type === "template_substitution")
|
|
64988
|
+
return false;
|
|
64989
|
+
}
|
|
64990
|
+
return true;
|
|
64991
|
+
}
|
|
64992
|
+
return false;
|
|
64993
|
+
}
|
|
64994
|
+
function isEscapeCall(node2) {
|
|
64995
|
+
if (node2.type !== "call_expression")
|
|
64996
|
+
return false;
|
|
64997
|
+
const fn = node2.childForFieldName("function");
|
|
64998
|
+
if (!fn)
|
|
64999
|
+
return false;
|
|
65000
|
+
if (fn.type === "identifier")
|
|
65001
|
+
return ESCAPE_HELPER_NAMES.has(fn.text);
|
|
65002
|
+
if (fn.type === "member_expression") {
|
|
65003
|
+
const prop = fn.childForFieldName("property");
|
|
65004
|
+
if (prop && ESCAPE_HELPER_NAMES.has(prop.text))
|
|
65005
|
+
return true;
|
|
65006
|
+
const obj = fn.childForFieldName("object");
|
|
65007
|
+
if (obj?.type === "identifier" && /purify/i.test(obj.text))
|
|
65008
|
+
return true;
|
|
65009
|
+
}
|
|
65010
|
+
return false;
|
|
65011
|
+
}
|
|
65012
|
+
function isFullyEscapedRhs(node2) {
|
|
65013
|
+
if (node2.type === "string")
|
|
65014
|
+
return true;
|
|
65015
|
+
if (node2.type === "number")
|
|
65016
|
+
return true;
|
|
65017
|
+
if (node2.type === "true" || node2.type === "false" || node2.type === "null" || node2.type === "undefined")
|
|
65018
|
+
return true;
|
|
65019
|
+
if (isEscapeCall(node2))
|
|
65020
|
+
return true;
|
|
65021
|
+
if (node2.type === "template_string") {
|
|
65022
|
+
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
65023
|
+
const child = node2.namedChild(i);
|
|
65024
|
+
if (!child)
|
|
65025
|
+
continue;
|
|
65026
|
+
if (child.type === "template_substitution") {
|
|
65027
|
+
const inner = child.namedChild(0);
|
|
65028
|
+
if (!inner || !isFullyEscapedRhs(inner))
|
|
65029
|
+
return false;
|
|
65030
|
+
}
|
|
65031
|
+
}
|
|
65032
|
+
return true;
|
|
65033
|
+
}
|
|
65034
|
+
if (node2.type === "binary_expression") {
|
|
65035
|
+
const op = node2.children.find((c) => c.text === "+");
|
|
65036
|
+
if (!op)
|
|
65037
|
+
return false;
|
|
65038
|
+
const lhs = node2.childForFieldName("left");
|
|
65039
|
+
const rhs = node2.childForFieldName("right");
|
|
65040
|
+
if (!lhs || !rhs)
|
|
65041
|
+
return false;
|
|
65042
|
+
return isFullyEscapedRhs(lhs) && isFullyEscapedRhs(rhs);
|
|
65043
|
+
}
|
|
65044
|
+
return false;
|
|
65045
|
+
}
|
|
65046
|
+
var disabledAutoEscapingVisitor, ESCAPE_HELPER_NAMES;
|
|
64707
65047
|
var init_disabled_auto_escaping = __esm({
|
|
64708
65048
|
"packages/analyzer/dist/rules/security/visitors/javascript/disabled-auto-escaping.js"() {
|
|
64709
65049
|
"use strict";
|
|
@@ -64724,6 +65064,11 @@ var init_disabled_auto_escaping = __esm({
|
|
|
64724
65064
|
if (left?.type === "member_expression") {
|
|
64725
65065
|
const prop = left.childForFieldName("property");
|
|
64726
65066
|
if (prop?.text === "innerHTML") {
|
|
65067
|
+
const right = node2.childForFieldName("right");
|
|
65068
|
+
if (right && isStaticStringLiteral(right))
|
|
65069
|
+
return null;
|
|
65070
|
+
if (right && isFullyEscapedRhs(right))
|
|
65071
|
+
return null;
|
|
64727
65072
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Disabled auto-escaping", "Direct innerHTML assignment can lead to XSS vulnerabilities.", sourceCode, "Use textContent instead of innerHTML, or sanitize input with DOMPurify.");
|
|
64728
65073
|
}
|
|
64729
65074
|
}
|
|
@@ -64731,6 +65076,19 @@ var init_disabled_auto_escaping = __esm({
|
|
|
64731
65076
|
return null;
|
|
64732
65077
|
}
|
|
64733
65078
|
};
|
|
65079
|
+
ESCAPE_HELPER_NAMES = /* @__PURE__ */ new Set([
|
|
65080
|
+
"esc",
|
|
65081
|
+
"escape",
|
|
65082
|
+
"escapeHtml",
|
|
65083
|
+
"escapeHTML",
|
|
65084
|
+
"escapeHtmlEntities",
|
|
65085
|
+
"htmlEscape",
|
|
65086
|
+
"htmlEntities",
|
|
65087
|
+
"sanitize",
|
|
65088
|
+
"sanitizeHtml",
|
|
65089
|
+
"sanitizeHTML",
|
|
65090
|
+
"purify"
|
|
65091
|
+
]);
|
|
64734
65092
|
}
|
|
64735
65093
|
});
|
|
64736
65094
|
|
|
@@ -68345,6 +68703,18 @@ var init_javascript3 = __esm({
|
|
|
68345
68703
|
});
|
|
68346
68704
|
|
|
68347
68705
|
// packages/analyzer/dist/rules/security/visitors/python/sql-injection.js
|
|
68706
|
+
function hasStringOperand(binNode) {
|
|
68707
|
+
for (let i = 0; i < binNode.namedChildCount; i++) {
|
|
68708
|
+
const child = binNode.namedChild(i);
|
|
68709
|
+
if (!child)
|
|
68710
|
+
continue;
|
|
68711
|
+
if (child.type === "string")
|
|
68712
|
+
return true;
|
|
68713
|
+
if (child.type === "binary_operator" && hasStringOperand(child))
|
|
68714
|
+
return true;
|
|
68715
|
+
}
|
|
68716
|
+
return false;
|
|
68717
|
+
}
|
|
68348
68718
|
var PYTHON_QUERY_METHODS, pythonSqlInjectionVisitor;
|
|
68349
68719
|
var init_sql_injection2 = __esm({
|
|
68350
68720
|
"packages/analyzer/dist/rules/security/visitors/python/sql-injection.js"() {
|
|
@@ -68398,7 +68768,9 @@ var init_sql_injection2 = __esm({
|
|
|
68398
68768
|
if (firstArg.type === "binary_operator") {
|
|
68399
68769
|
const op = firstArg.children.find((c) => c.text === "+");
|
|
68400
68770
|
if (op) {
|
|
68401
|
-
|
|
68771
|
+
if (hasStringOperand(firstArg)) {
|
|
68772
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Potential SQL injection", `String concatenation passed to ${methodName}(). Use parameterized queries instead.`, sourceCode, "Use parameterized queries (e.g., %s or :param) instead of string concatenation in SQL.");
|
|
68773
|
+
}
|
|
68402
68774
|
}
|
|
68403
68775
|
}
|
|
68404
68776
|
return null;
|
|
@@ -70350,6 +70722,40 @@ var init_paramiko_call = __esm({
|
|
|
70350
70722
|
});
|
|
70351
70723
|
|
|
70352
70724
|
// packages/analyzer/dist/rules/security/visitors/python/suspicious-url-open.js
|
|
70725
|
+
function isConfigSourcedVar(node2, varName) {
|
|
70726
|
+
let func = node2.parent;
|
|
70727
|
+
while (func) {
|
|
70728
|
+
if (func.type === "function_definition")
|
|
70729
|
+
break;
|
|
70730
|
+
func = func.parent;
|
|
70731
|
+
}
|
|
70732
|
+
let scope = func ? func.childForFieldName("body") : node2.tree.rootNode;
|
|
70733
|
+
if (!scope)
|
|
70734
|
+
return false;
|
|
70735
|
+
let found = false;
|
|
70736
|
+
function walk(n) {
|
|
70737
|
+
if (found)
|
|
70738
|
+
return;
|
|
70739
|
+
if (n.type === "assignment") {
|
|
70740
|
+
const lhs = n.childForFieldName("left");
|
|
70741
|
+
const rhs = n.childForFieldName("right");
|
|
70742
|
+
if (lhs?.type === "identifier" && lhs.text === varName && rhs) {
|
|
70743
|
+
const text = rhs.text;
|
|
70744
|
+
if (/\bos\.getenv\b|\bos\.environ\b|\bconfig\.|\bsettings\.|\bSettings\(\)|\bConfig\(\)/.test(text)) {
|
|
70745
|
+
found = true;
|
|
70746
|
+
return;
|
|
70747
|
+
}
|
|
70748
|
+
}
|
|
70749
|
+
}
|
|
70750
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
70751
|
+
const ch = n.child(i);
|
|
70752
|
+
if (ch)
|
|
70753
|
+
walk(ch);
|
|
70754
|
+
}
|
|
70755
|
+
}
|
|
70756
|
+
walk(scope);
|
|
70757
|
+
return found;
|
|
70758
|
+
}
|
|
70353
70759
|
var pythonSuspiciousUrlOpenVisitor;
|
|
70354
70760
|
var init_suspicious_url_open = __esm({
|
|
70355
70761
|
"packages/analyzer/dist/rules/security/visitors/python/suspicious-url-open.js"() {
|
|
@@ -70383,10 +70789,11 @@ var init_suspicious_url_open = __esm({
|
|
|
70383
70789
|
const firstArg = args.namedChildren[0];
|
|
70384
70790
|
if (!firstArg)
|
|
70385
70791
|
return null;
|
|
70386
|
-
if (firstArg.type
|
|
70387
|
-
return
|
|
70388
|
-
|
|
70389
|
-
|
|
70792
|
+
if (firstArg.type === "string" && !firstArg.text.startsWith("f"))
|
|
70793
|
+
return null;
|
|
70794
|
+
if (firstArg.type === "identifier" && isConfigSourcedVar(node2, firstArg.text))
|
|
70795
|
+
return null;
|
|
70796
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "urlopen with user-controlled URL", `${objectName ? objectName + "." : ""}urlopen() called with a non-literal URL. User-controlled URLs enable SSRF.`, sourceCode, "Validate and allowlist URLs before passing them to urlopen().");
|
|
70390
70797
|
}
|
|
70391
70798
|
};
|
|
70392
70799
|
}
|
|
@@ -72844,8 +73251,15 @@ var init_exclusions = __esm({
|
|
|
72844
73251
|
// All asterisks (masked values)
|
|
72845
73252
|
/^x+$/i,
|
|
72846
73253
|
// All x's (placeholder)
|
|
72847
|
-
/^\.{3,}
|
|
73254
|
+
/^\.{3,}$/,
|
|
72848
73255
|
// Ellipsis placeholders
|
|
73256
|
+
// Filenames with a recognized document / media / archive / data
|
|
73257
|
+
// extension. Composed object keys like `<hash>__<date>__<batch>.pdf`
|
|
73258
|
+
// look like high-entropy tokens to pattern detectors, but the
|
|
73259
|
+
// trailing extension makes them clearly filenames. The optional
|
|
73260
|
+
// trailing slash matches S3 path prefixes that act as directories
|
|
73261
|
+
// (`processed/.../<file>.pdf/source_pdfs/...`).
|
|
73262
|
+
/\.(pdf|csv|tsv|json|jsonl|ndjson|parquet|xml|html?|md|rst|txt|log|zip|tar|gz|tgz|bz2|7z|rar|png|jpe?g|gif|webp|svg|ico|bmp|tiff?|mp[34]|wav|flac|ogg|webm|avi|mov|mkv|docx?|xlsx?|pptx?|odt|ods|odp|rtf|epub|yaml|yml|toml|ini|cfg|conf|sql)\/?$/i
|
|
72849
73263
|
];
|
|
72850
73264
|
}
|
|
72851
73265
|
});
|
|
@@ -74650,7 +75064,7 @@ var init_secret_scanner = __esm({
|
|
|
74650
75064
|
function stripCommentMarkers(text) {
|
|
74651
75065
|
return text.replace(/^\/\/\s?/, "").replace(/^\/\*\s?/, "").replace(/\s?\*\/$/, "").replace(/^\s*\*\s?/gm, "").replace(/^#\s?/gm, "").trim();
|
|
74652
75066
|
}
|
|
74653
|
-
var hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor,
|
|
75067
|
+
var hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor, DB_URL_PATTERN, DB_PASSWORD_KV_PATTERNS, NON_LITERAL_PASSWORD_SEGMENT, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
|
|
74654
75068
|
var init_universal = __esm({
|
|
74655
75069
|
"packages/analyzer/dist/rules/security/visitors/universal.js"() {
|
|
74656
75070
|
"use strict";
|
|
@@ -74668,6 +75082,15 @@ var init_universal = __esm({
|
|
|
74668
75082
|
if (parent?.type === "pair" && parent.childForFieldName("key")?.id === node2.id) {
|
|
74669
75083
|
return null;
|
|
74670
75084
|
}
|
|
75085
|
+
if (parent?.type === "expression_statement") {
|
|
75086
|
+
const grandParent = parent.parent;
|
|
75087
|
+
if (grandParent && (grandParent.type === "module" || grandParent.type === "block")) {
|
|
75088
|
+
const firstChild = grandParent.namedChild(0);
|
|
75089
|
+
if (firstChild && firstChild.id === parent.id) {
|
|
75090
|
+
return null;
|
|
75091
|
+
}
|
|
75092
|
+
}
|
|
75093
|
+
}
|
|
74671
75094
|
const match2 = scanForSecrets(stripped);
|
|
74672
75095
|
if (match2) {
|
|
74673
75096
|
return makeViolation(this.ruleKey, node2, filePath, "critical", `Hardcoded secret detected (${match2.patternId})`, `This string matches a known secret pattern: ${match2.description}. Use environment variables instead.`, sourceCode, "Move this secret to an environment variable.");
|
|
@@ -74682,7 +75105,11 @@ var init_universal = __esm({
|
|
|
74682
75105
|
const secretNames = ["password", "passwd", "secret", "apikey", "api_key", "token", "auth_token", "access_token", "private_key"];
|
|
74683
75106
|
const isNonSecretName = /(?:uri|url|endpoint|type|scope|name|header|grant|method)/.test(name);
|
|
74684
75107
|
const isNonSecretValue = /https?:\/\//.test(stripped) || /^(true|false|null|undefined|localhost|None|True|False|Bearer)$/i.test(stripped) || /[[\]<>{}()#.=\s]/.test(stripped);
|
|
74685
|
-
|
|
75108
|
+
const nameClean = name.replace(/^['"`]+|['"`]+$/g, "");
|
|
75109
|
+
const valueClean = stripped.toLowerCase();
|
|
75110
|
+
const isPlainAlphaSnake = /^[a-z_]+$/.test(valueClean);
|
|
75111
|
+
const isVocabularyTag = isPlainAlphaSnake && nameClean.length >= 4 && (valueClean === nameClean || valueClean.includes(nameClean));
|
|
75112
|
+
if (secretNames.some((s) => name.includes(s)) && stripped.length >= 8 && !isNonSecretName && !isNonSecretValue && !isVocabularyTag) {
|
|
74686
75113
|
return makeViolation(this.ruleKey, node2, filePath, "critical", "Hardcoded secret detected", `Variable "${nameNode.text}" contains what appears to be a hardcoded secret. Use environment variables instead.`, sourceCode, "Move this secret to an environment variable.");
|
|
74687
75114
|
}
|
|
74688
75115
|
}
|
|
@@ -75696,21 +76123,28 @@ var init_universal = __esm({
|
|
|
75696
76123
|
return null;
|
|
75697
76124
|
}
|
|
75698
76125
|
};
|
|
75699
|
-
|
|
75700
|
-
|
|
76126
|
+
DB_URL_PATTERN = /(?:mysql|postgresql|postgres|mongodb|sqlite|mssql|oracle):\/\/[^:]+:([^@]+)@/i;
|
|
76127
|
+
DB_PASSWORD_KV_PATTERNS = [
|
|
75701
76128
|
/password\s*=\s*['"][^'"]{4,}['"]/i,
|
|
75702
76129
|
/pwd\s*=\s*['"][^'"]{4,}['"]/i
|
|
75703
76130
|
];
|
|
76131
|
+
NON_LITERAL_PASSWORD_SEGMENT = /^\{[^{}]*\}$|^\$\{[^}]*\}$|^%s$|^%\([^)]+\)s$|^<[^>]+>$|^\$\([^)]*\)$|^\$[A-Z_][A-Z0-9_]*$|process\.env/i;
|
|
75704
76132
|
hardcodedDatabasePasswordVisitor = {
|
|
75705
76133
|
ruleKey: "security/deterministic/hardcoded-database-password",
|
|
75706
76134
|
nodeTypes: ["string", "template_string"],
|
|
75707
76135
|
visit(node2, filePath, sourceCode) {
|
|
75708
76136
|
const text = node2.text;
|
|
75709
76137
|
const stripped = text.replace(/^[fFbBrRuU]*['"`]{1,3}|['"`]{1,3}$/g, "");
|
|
75710
|
-
|
|
76138
|
+
if (/\$\{|%s|<password>|\$\(|process\.env/i.test(stripped))
|
|
76139
|
+
return null;
|
|
76140
|
+
const urlMatch = DB_URL_PATTERN.exec(stripped);
|
|
76141
|
+
if (urlMatch) {
|
|
76142
|
+
if (NON_LITERAL_PASSWORD_SEGMENT.test(urlMatch[1]))
|
|
76143
|
+
return null;
|
|
76144
|
+
return makeViolation(this.ruleKey, node2, filePath, "critical", "Hardcoded database password", "Database connection string contains a hardcoded password.", sourceCode, "Load database credentials from environment variables.");
|
|
76145
|
+
}
|
|
76146
|
+
for (const pattern of DB_PASSWORD_KV_PATTERNS) {
|
|
75711
76147
|
if (pattern.test(stripped)) {
|
|
75712
|
-
if (/\$\{|%s|<password>|\$\(|process\.env/i.test(stripped))
|
|
75713
|
-
return null;
|
|
75714
76148
|
return makeViolation(this.ruleKey, node2, filePath, "critical", "Hardcoded database password", "Database connection string contains a hardcoded password.", sourceCode, "Load database credentials from environment variables.");
|
|
75715
76149
|
}
|
|
75716
76150
|
}
|
|
@@ -77686,6 +78120,78 @@ function isKeyFromControlledIteration(assignmentNode, keyName) {
|
|
|
77686
78120
|
}
|
|
77687
78121
|
return false;
|
|
77688
78122
|
}
|
|
78123
|
+
function isKeyFromForOfLoopVarMember(assignmentNode, keyName) {
|
|
78124
|
+
let scope = assignmentNode.parent;
|
|
78125
|
+
while (scope) {
|
|
78126
|
+
if (scope.type === "function_declaration" || scope.type === "function_expression" || scope.type === "arrow_function" || scope.type === "method_definition" || scope.type === "program")
|
|
78127
|
+
break;
|
|
78128
|
+
scope = scope.parent;
|
|
78129
|
+
}
|
|
78130
|
+
if (!scope)
|
|
78131
|
+
return false;
|
|
78132
|
+
const forVarNames = /* @__PURE__ */ new Set();
|
|
78133
|
+
function collectForVars(n) {
|
|
78134
|
+
if (n.type === "for_in_statement") {
|
|
78135
|
+
const left = n.childForFieldName("left");
|
|
78136
|
+
if (left?.type === "identifier")
|
|
78137
|
+
forVarNames.add(left.text);
|
|
78138
|
+
}
|
|
78139
|
+
if (n !== scope && (n.type === "function_declaration" || n.type === "function_expression" || n.type === "arrow_function" || n.type === "method_definition"))
|
|
78140
|
+
return;
|
|
78141
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
78142
|
+
const ch = n.child(i);
|
|
78143
|
+
if (ch)
|
|
78144
|
+
collectForVars(ch);
|
|
78145
|
+
}
|
|
78146
|
+
}
|
|
78147
|
+
collectForVars(scope);
|
|
78148
|
+
if (forVarNames.size === 0)
|
|
78149
|
+
return false;
|
|
78150
|
+
let found = false;
|
|
78151
|
+
function findAssignment(n) {
|
|
78152
|
+
if (found)
|
|
78153
|
+
return;
|
|
78154
|
+
if (n.type === "variable_declarator") {
|
|
78155
|
+
const name = n.childForFieldName("name");
|
|
78156
|
+
const value = n.childForFieldName("value");
|
|
78157
|
+
if (name?.type === "identifier" && name.text === keyName && value?.type === "member_expression") {
|
|
78158
|
+
const obj = value.childForFieldName("object");
|
|
78159
|
+
if (obj?.type === "identifier" && forVarNames.has(obj.text)) {
|
|
78160
|
+
found = true;
|
|
78161
|
+
return;
|
|
78162
|
+
}
|
|
78163
|
+
}
|
|
78164
|
+
}
|
|
78165
|
+
if (n !== scope && (n.type === "function_declaration" || n.type === "function_expression" || n.type === "arrow_function" || n.type === "method_definition"))
|
|
78166
|
+
return;
|
|
78167
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
78168
|
+
const ch = n.child(i);
|
|
78169
|
+
if (ch)
|
|
78170
|
+
findAssignment(ch);
|
|
78171
|
+
}
|
|
78172
|
+
}
|
|
78173
|
+
findAssignment(scope);
|
|
78174
|
+
return found;
|
|
78175
|
+
}
|
|
78176
|
+
function isAssignmentGuardedByKeyInObject(assignmentNode, keyName, objText) {
|
|
78177
|
+
let cursor = assignmentNode.parent;
|
|
78178
|
+
while (cursor) {
|
|
78179
|
+
if (cursor.type === "if_statement") {
|
|
78180
|
+
const cond = cursor.childForFieldName("condition");
|
|
78181
|
+
if (cond) {
|
|
78182
|
+
const condText = cond.text;
|
|
78183
|
+
const pattern = new RegExp(`\\b${escapeRegExp(keyName)}\\s+in\\s+${escapeRegExp(objText)}\\b`);
|
|
78184
|
+
if (pattern.test(condText))
|
|
78185
|
+
return true;
|
|
78186
|
+
}
|
|
78187
|
+
}
|
|
78188
|
+
cursor = cursor.parent;
|
|
78189
|
+
}
|
|
78190
|
+
return false;
|
|
78191
|
+
}
|
|
78192
|
+
function escapeRegExp(s) {
|
|
78193
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
78194
|
+
}
|
|
77689
78195
|
function isObjectEntriesOrKeys(node2) {
|
|
77690
78196
|
if (node2.type === "call_expression") {
|
|
77691
78197
|
const fn = node2.childForFieldName("function");
|
|
@@ -77699,6 +78205,77 @@ function isObjectEntriesOrKeys(node2) {
|
|
|
77699
78205
|
}
|
|
77700
78206
|
return false;
|
|
77701
78207
|
}
|
|
78208
|
+
function isKeyDestructuredParam(assignmentNode, keyName) {
|
|
78209
|
+
let scope = assignmentNode.parent;
|
|
78210
|
+
while (scope) {
|
|
78211
|
+
if (scope.type === "function_declaration" || scope.type === "function_expression" || scope.type === "arrow_function" || scope.type === "method_definition") {
|
|
78212
|
+
const params = scope.childForFieldName("parameters") ?? scope.childForFieldName("parameter");
|
|
78213
|
+
if (params && containsDestructuredName(params, keyName))
|
|
78214
|
+
return true;
|
|
78215
|
+
}
|
|
78216
|
+
if (scope.type === "program")
|
|
78217
|
+
break;
|
|
78218
|
+
scope = scope.parent;
|
|
78219
|
+
}
|
|
78220
|
+
return false;
|
|
78221
|
+
}
|
|
78222
|
+
function containsDestructuredName(params, keyName) {
|
|
78223
|
+
let found = false;
|
|
78224
|
+
function walk(n) {
|
|
78225
|
+
if (found)
|
|
78226
|
+
return;
|
|
78227
|
+
if (n.type === "identifier" && n.text === keyName) {
|
|
78228
|
+
const parent = n.parent;
|
|
78229
|
+
if (parent?.type === "array_pattern" || parent?.type === "object_pattern") {
|
|
78230
|
+
found = true;
|
|
78231
|
+
return;
|
|
78232
|
+
}
|
|
78233
|
+
if (parent?.type === "shorthand_property_identifier_pattern" && n.text === keyName) {
|
|
78234
|
+
found = true;
|
|
78235
|
+
return;
|
|
78236
|
+
}
|
|
78237
|
+
}
|
|
78238
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
78239
|
+
const child = n.child(i);
|
|
78240
|
+
if (child)
|
|
78241
|
+
walk(child);
|
|
78242
|
+
}
|
|
78243
|
+
}
|
|
78244
|
+
walk(params);
|
|
78245
|
+
return found;
|
|
78246
|
+
}
|
|
78247
|
+
function isKeyAssignedFromLocalCall(assignmentNode, keyName) {
|
|
78248
|
+
let scope = assignmentNode.parent;
|
|
78249
|
+
while (scope) {
|
|
78250
|
+
if (scope.type === "function_declaration" || scope.type === "function_expression" || scope.type === "arrow_function" || scope.type === "method_definition" || scope.type === "program")
|
|
78251
|
+
break;
|
|
78252
|
+
scope = scope.parent;
|
|
78253
|
+
}
|
|
78254
|
+
if (!scope)
|
|
78255
|
+
return false;
|
|
78256
|
+
let found = false;
|
|
78257
|
+
function walk(n) {
|
|
78258
|
+
if (found)
|
|
78259
|
+
return;
|
|
78260
|
+
if (n.type === "variable_declarator") {
|
|
78261
|
+
const name = n.childForFieldName("name");
|
|
78262
|
+
const value = n.childForFieldName("value");
|
|
78263
|
+
if (name?.type === "identifier" && name.text === keyName && value?.type === "call_expression") {
|
|
78264
|
+
found = true;
|
|
78265
|
+
return;
|
|
78266
|
+
}
|
|
78267
|
+
}
|
|
78268
|
+
if (n !== scope && (n.type === "function_declaration" || n.type === "function_expression" || n.type === "arrow_function" || n.type === "method_definition"))
|
|
78269
|
+
return;
|
|
78270
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
78271
|
+
const child = n.child(i);
|
|
78272
|
+
if (child)
|
|
78273
|
+
walk(child);
|
|
78274
|
+
}
|
|
78275
|
+
}
|
|
78276
|
+
walk(scope);
|
|
78277
|
+
return found;
|
|
78278
|
+
}
|
|
77702
78279
|
var prototypePollutionVisitor;
|
|
77703
78280
|
var init_prototype_pollution = __esm({
|
|
77704
78281
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/prototype-pollution.js"() {
|
|
@@ -77729,6 +78306,14 @@ var init_prototype_pollution = __esm({
|
|
|
77729
78306
|
const keyName = index.text;
|
|
77730
78307
|
if (isKeyFromControlledIteration(node2, keyName))
|
|
77731
78308
|
return null;
|
|
78309
|
+
if (isKeyAssignedFromLocalCall(node2, keyName))
|
|
78310
|
+
return null;
|
|
78311
|
+
if (isKeyDestructuredParam(node2, keyName))
|
|
78312
|
+
return null;
|
|
78313
|
+
if (isKeyFromForOfLoopVarMember(node2, keyName))
|
|
78314
|
+
return null;
|
|
78315
|
+
if (isAssignmentGuardedByKeyInObject(node2, keyName, obj.text))
|
|
78316
|
+
return null;
|
|
77732
78317
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Prototype pollution", `\`${left.text}\` uses a dynamic key for property assignment \u2014 if \`${index.text}\` is \`"__proto__"\` or \`"constructor"\`, this enables prototype pollution.`, sourceCode, `Validate that \`${index.text}\` is not "__proto__", "constructor", or "prototype" before assignment, or use Map instead.`);
|
|
77733
78318
|
}
|
|
77734
78319
|
};
|
|
@@ -81652,8 +82237,10 @@ var init_useeffect_missing_deps = __esm({
|
|
|
81652
82237
|
}
|
|
81653
82238
|
const suspiciousIds = [...usedIds].filter((id) => /^[a-z]/.test(id) && !id.startsWith("set") && id.length > 1 && !EXCLUDED_IDENTIFIERS.has(id) && !refAccessedIds.has(id) && !stableFunctions.has(id) && !componentBodyFunctions.has(id));
|
|
81654
82239
|
const nodeStartLine = node2.startPosition.row;
|
|
81655
|
-
const
|
|
81656
|
-
|
|
82240
|
+
const nodeEndLine = node2.endPosition.row;
|
|
82241
|
+
const sourceLines = sourceCode.split("\n");
|
|
82242
|
+
const windowLines = sourceLines.slice(Math.max(0, nodeStartLine - 2), nodeEndLine + 2);
|
|
82243
|
+
if (windowLines.some((line) => line.includes("eslint-disable")))
|
|
81657
82244
|
return null;
|
|
81658
82245
|
if (suspiciousIds.length > 0) {
|
|
81659
82246
|
return makeViolation(this.ruleKey, depsArg, filePath, "high", "useEffect with empty deps may have stale closure", `\`useEffect\` has an empty dependency array but references \`${suspiciousIds.slice(0, 3).join("`, `")}\` \u2014 these will be stale closures if they change.`, sourceCode, `Add the referenced variables to the dependency array: \`[${suspiciousIds.slice(0, 3).join(", ")}]\`.`);
|
|
@@ -83117,7 +83704,7 @@ var init_empty_catch2 = __esm({
|
|
|
83117
83704
|
if (!body)
|
|
83118
83705
|
return null;
|
|
83119
83706
|
const statements = body.namedChildren.filter((c) => c.type !== "comment");
|
|
83120
|
-
if (statements.length === 0
|
|
83707
|
+
if (statements.length === 0) {
|
|
83121
83708
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Empty except block", "This except block swallows errors silently. Add error handling or at least log the error.", sourceCode, "Add error logging or re-raise the exception in this except block.");
|
|
83122
83709
|
}
|
|
83123
83710
|
return null;
|
|
@@ -83159,6 +83746,15 @@ var init_bare_except = __esm({
|
|
|
83159
83746
|
});
|
|
83160
83747
|
|
|
83161
83748
|
// packages/analyzer/dist/rules/bugs/visitors/python/_helpers.js
|
|
83749
|
+
function isFStringWithInterpolation(node2) {
|
|
83750
|
+
if (node2.type !== "string")
|
|
83751
|
+
return false;
|
|
83752
|
+
for (const child of node2.namedChildren) {
|
|
83753
|
+
if (child && child.type === "interpolation")
|
|
83754
|
+
return true;
|
|
83755
|
+
}
|
|
83756
|
+
return false;
|
|
83757
|
+
}
|
|
83162
83758
|
var MUTABLE_DEFAULTS, PY_TERMINAL_TYPES, SAFE_DEFAULT_CALLS, VALID_OPEN_MODES, DUNDER_PARAM_COUNTS, CANCELLATION_EXCEPTIONS, BROAD_EXCEPTIONS, MUTATING_METHODS, SPECIAL_METHOD_RETURN_CONSTRAINTS, PYTHON_BUILTIN_NON_EXCEPTIONS, BIDI_CHARS, VALID_FORMAT_CHARS, FORMAT_SPEC_RE;
|
|
83163
83759
|
var init_helpers2 = __esm({
|
|
83164
83760
|
"packages/analyzer/dist/rules/bugs/visitors/python/_helpers.js"() {
|
|
@@ -83961,17 +84557,25 @@ var init_loop_variable_overrides_iterator = __esm({
|
|
|
83961
84557
|
if (loopVar.type !== "identifier")
|
|
83962
84558
|
return null;
|
|
83963
84559
|
const varName = loopVar.text;
|
|
83964
|
-
function
|
|
83965
|
-
if (n.type === "identifier" && n.text === name)
|
|
84560
|
+
function hasFreeUse(n, name) {
|
|
84561
|
+
if (n.type === "identifier" && n.text === name) {
|
|
84562
|
+
const parent = n.parent;
|
|
84563
|
+
if (parent?.type === "attribute" && parent.childForFieldName("attribute")?.id === n.id) {
|
|
84564
|
+
return false;
|
|
84565
|
+
}
|
|
84566
|
+
if (parent?.type === "keyword_argument" && parent.childForFieldName("name")?.id === n.id) {
|
|
84567
|
+
return false;
|
|
84568
|
+
}
|
|
83966
84569
|
return true;
|
|
84570
|
+
}
|
|
83967
84571
|
for (let i = 0; i < n.childCount; i++) {
|
|
83968
84572
|
const child = n.child(i);
|
|
83969
|
-
if (child &&
|
|
84573
|
+
if (child && hasFreeUse(child, name))
|
|
83970
84574
|
return true;
|
|
83971
84575
|
}
|
|
83972
84576
|
return false;
|
|
83973
84577
|
}
|
|
83974
|
-
if (
|
|
84578
|
+
if (hasFreeUse(iterExpr, varName)) {
|
|
83975
84579
|
return makeViolation(this.ruleKey, loopVar, filePath, "high", "Loop variable overrides iterator", `Loop variable \`${varName}\` has the same name as the iterable \`${iterExpr.text}\` \u2014 after the first iteration \`${varName}\` no longer references the original iterable.`, sourceCode, `Rename the loop variable to something different from \`${iterExpr.text}\`.`);
|
|
83976
84580
|
}
|
|
83977
84581
|
return null;
|
|
@@ -85561,6 +86165,7 @@ var init_static_key_dict_comprehension = __esm({
|
|
|
85561
86165
|
"packages/analyzer/dist/rules/bugs/visitors/python/static-key-dict-comprehension.js"() {
|
|
85562
86166
|
"use strict";
|
|
85563
86167
|
init_types2();
|
|
86168
|
+
init_helpers2();
|
|
85564
86169
|
pythonStaticKeyDictComprehensionVisitor = {
|
|
85565
86170
|
ruleKey: "bugs/deterministic/static-key-dict-comprehension",
|
|
85566
86171
|
languages: ["python"],
|
|
@@ -85573,7 +86178,7 @@ var init_static_key_dict_comprehension = __esm({
|
|
|
85573
86178
|
if (!keyNode)
|
|
85574
86179
|
return null;
|
|
85575
86180
|
const LITERAL_TYPES5 = /* @__PURE__ */ new Set(["string", "integer", "float", "true", "false", "none"]);
|
|
85576
|
-
if (LITERAL_TYPES5.has(keyNode.type)) {
|
|
86181
|
+
if (LITERAL_TYPES5.has(keyNode.type) && !isFStringWithInterpolation(keyNode)) {
|
|
85577
86182
|
return makeViolation(this.ruleKey, keyNode, filePath, "high", "Static key in dict comprehension", `Dict comprehension with constant key \`${keyNode.text}\` \u2014 every iteration overwrites the same key, leaving only the last value.`, sourceCode, "Use a variable as the key, or use a list comprehension if you only need the values.");
|
|
85578
86183
|
}
|
|
85579
86184
|
return null;
|
|
@@ -86778,6 +87383,7 @@ var init_duplicate_dict_key = __esm({
|
|
|
86778
87383
|
"packages/analyzer/dist/rules/bugs/visitors/python/duplicate-dict-key.js"() {
|
|
86779
87384
|
"use strict";
|
|
86780
87385
|
init_types2();
|
|
87386
|
+
init_helpers2();
|
|
86781
87387
|
pythonDuplicateDictKeyVisitor = {
|
|
86782
87388
|
ruleKey: "bugs/deterministic/duplicate-dict-key",
|
|
86783
87389
|
languages: ["python"],
|
|
@@ -86793,7 +87399,8 @@ var init_duplicate_dict_key = __esm({
|
|
|
86793
87399
|
const leftNode = forInClause?.childForFieldName("left");
|
|
86794
87400
|
const loopVarNames = leftNode ? collectLoopVarNames(leftNode) : /* @__PURE__ */ new Set();
|
|
86795
87401
|
const LITERAL_TYPES5 = /* @__PURE__ */ new Set(["string", "integer", "float", "true", "false", "none"]);
|
|
86796
|
-
const
|
|
87402
|
+
const isStaticLiteral = LITERAL_TYPES5.has(keyNode.type) && !isFStringWithInterpolation(keyNode);
|
|
87403
|
+
const isConstantKey = isStaticLiteral || keyNode.type === "identifier" && loopVarNames.size > 0 && !loopVarNames.has(keyNode.text);
|
|
86797
87404
|
if (isConstantKey) {
|
|
86798
87405
|
return makeViolation(this.ruleKey, keyNode, filePath, "high", "Constant key in dict comprehension", `Dict comprehension with constant key \`${keyNode.text}\` \u2014 each iteration overwrites the same key, leaving only the last value.`, sourceCode, "Use a key expression that depends on the loop variable, or use a list comprehension instead.");
|
|
86799
87406
|
}
|
|
@@ -87200,9 +87807,8 @@ var init_warnings_no_stacklevel = __esm({
|
|
|
87200
87807
|
const funcText = func.text;
|
|
87201
87808
|
if (funcText !== "warnings.warn" && funcText !== "warn")
|
|
87202
87809
|
return null;
|
|
87203
|
-
if (funcText === "warn")
|
|
87810
|
+
if (funcText === "warn")
|
|
87204
87811
|
return null;
|
|
87205
|
-
}
|
|
87206
87812
|
const args = node2.childForFieldName("arguments");
|
|
87207
87813
|
if (!args)
|
|
87208
87814
|
return null;
|
|
@@ -88421,6 +89027,27 @@ function isMutableInit2(node2) {
|
|
|
88421
89027
|
function isConstantName(name) {
|
|
88422
89028
|
return name === name.toUpperCase();
|
|
88423
89029
|
}
|
|
89030
|
+
function moduleHasLockGuard(moduleNode) {
|
|
89031
|
+
for (let i = 0; i < moduleNode.namedChildCount; i++) {
|
|
89032
|
+
const stmt = moduleNode.namedChild(i);
|
|
89033
|
+
if (!stmt)
|
|
89034
|
+
continue;
|
|
89035
|
+
const inner = stmt.type === "expression_statement" ? stmt.namedChild(0) : stmt;
|
|
89036
|
+
if (inner?.type !== "assignment")
|
|
89037
|
+
continue;
|
|
89038
|
+
const rhs = inner.childForFieldName("right");
|
|
89039
|
+
if (rhs?.type !== "call")
|
|
89040
|
+
continue;
|
|
89041
|
+
const fn = rhs.childForFieldName("function");
|
|
89042
|
+
if (!fn)
|
|
89043
|
+
continue;
|
|
89044
|
+
const text = fn.text;
|
|
89045
|
+
if (text === "threading.Lock" || text === "threading.RLock" || text === "threading.Semaphore" || text === "threading.BoundedSemaphore" || text === "asyncio.Lock" || text === "multiprocessing.Lock" || text === "multiprocessing.RLock" || text === "Lock" || text === "RLock") {
|
|
89046
|
+
return true;
|
|
89047
|
+
}
|
|
89048
|
+
}
|
|
89049
|
+
return false;
|
|
89050
|
+
}
|
|
88424
89051
|
var pythonSharedMutableModuleStateVisitor;
|
|
88425
89052
|
var init_shared_mutable_module_state2 = __esm({
|
|
88426
89053
|
"packages/analyzer/dist/rules/bugs/visitors/python/shared-mutable-module-state.js"() {
|
|
@@ -88444,6 +89071,11 @@ var init_shared_mutable_module_state2 = __esm({
|
|
|
88444
89071
|
return null;
|
|
88445
89072
|
if (varName === "__all__")
|
|
88446
89073
|
return null;
|
|
89074
|
+
let moduleNode = node2.parent;
|
|
89075
|
+
while (moduleNode && moduleNode.type !== "module")
|
|
89076
|
+
moduleNode = moduleNode.parent;
|
|
89077
|
+
if (moduleNode && moduleHasLockGuard(moduleNode))
|
|
89078
|
+
return null;
|
|
88447
89079
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Shared mutable state in module scope", `\`${varName}\` is a mutable ${right.type} at module level \u2014 in server environments this state is shared across all requests, causing race conditions and data leaks.`, sourceCode, "Move mutable state inside request handlers or use immutable data structures.");
|
|
88448
89080
|
}
|
|
88449
89081
|
};
|
|
@@ -89861,9 +90493,31 @@ function extractStringContent(node2) {
|
|
|
89861
90493
|
}
|
|
89862
90494
|
return null;
|
|
89863
90495
|
}
|
|
90496
|
+
function pythonRegexToJs(pattern) {
|
|
90497
|
+
let flags = "";
|
|
90498
|
+
const collectFlag = (f2) => {
|
|
90499
|
+
if ("imsu".includes(f2) && !flags.includes(f2))
|
|
90500
|
+
flags += f2;
|
|
90501
|
+
};
|
|
90502
|
+
const prefix = pattern.match(/^\(\?([aiLmsux]+)\)/);
|
|
90503
|
+
if (prefix) {
|
|
90504
|
+
for (const f2 of prefix[1])
|
|
90505
|
+
collectFlag(f2);
|
|
90506
|
+
pattern = pattern.slice(prefix[0].length);
|
|
90507
|
+
}
|
|
90508
|
+
pattern = pattern.replace(/\(\?([aiLmsux]+):/g, (_, flagSet) => {
|
|
90509
|
+
for (const f2 of flagSet)
|
|
90510
|
+
collectFlag(f2);
|
|
90511
|
+
return "(?:";
|
|
90512
|
+
});
|
|
90513
|
+
pattern = pattern.replace(/\(\?P</g, "(?<");
|
|
90514
|
+
pattern = pattern.replace(/\(\?P=([^)]+)\)/g, "\\k<$1>");
|
|
90515
|
+
return { pattern, flags };
|
|
90516
|
+
}
|
|
89864
90517
|
function isValidRegex(pattern) {
|
|
89865
90518
|
try {
|
|
89866
|
-
|
|
90519
|
+
const normalized = pythonRegexToJs(pattern);
|
|
90520
|
+
new RegExp(normalized.pattern, normalized.flags);
|
|
89867
90521
|
return true;
|
|
89868
90522
|
} catch {
|
|
89869
90523
|
return false;
|
|
@@ -91606,6 +92260,25 @@ var init_confusing_implicit_concat = __esm({
|
|
|
91606
92260
|
}
|
|
91607
92261
|
if (hasFormatString)
|
|
91608
92262
|
continue;
|
|
92263
|
+
const isMultiLine = child.startPosition.row !== child.endPosition.row;
|
|
92264
|
+
if (isMultiLine) {
|
|
92265
|
+
const parts = child.namedChildren;
|
|
92266
|
+
let allButLastEndWithSpace = parts.length > 1;
|
|
92267
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
92268
|
+
const t = parts[i].text;
|
|
92269
|
+
const stripped = t.replace(/['"]+$/, "");
|
|
92270
|
+
const lastChar = stripped.charAt(stripped.length - 1);
|
|
92271
|
+
if (lastChar !== " " && lastChar !== "\\" && lastChar !== " ") {
|
|
92272
|
+
allButLastEndWithSpace = false;
|
|
92273
|
+
break;
|
|
92274
|
+
}
|
|
92275
|
+
}
|
|
92276
|
+
if (allButLastEndWithSpace)
|
|
92277
|
+
continue;
|
|
92278
|
+
const totalLen = parts.reduce((sum, p) => sum + p.text.length, 0);
|
|
92279
|
+
if (totalLen > 60)
|
|
92280
|
+
continue;
|
|
92281
|
+
}
|
|
91609
92282
|
return makeViolation(this.ruleKey, child, filePath, "medium", "Confusing implicit string concatenation", `Implicit string concatenation \`${child.text.slice(0, 50)}${child.text.length > 50 ? "..." : ""}\` inside a ${node2.type.replace(/_/g, " ")} \u2014 adjacent string literals are joined at compile time. If two separate strings were intended, add a comma between them.`, sourceCode, "If you meant two separate strings, add a comma. If intentional concatenation, use explicit `+` or join into a single string.");
|
|
91610
92283
|
}
|
|
91611
92284
|
}
|
|
@@ -92089,6 +92762,7 @@ var init_static_key_dict_comprehension_ruff = __esm({
|
|
|
92089
92762
|
"packages/analyzer/dist/rules/bugs/visitors/python/static-key-dict-comprehension-ruff.js"() {
|
|
92090
92763
|
"use strict";
|
|
92091
92764
|
init_types2();
|
|
92765
|
+
init_helpers2();
|
|
92092
92766
|
LITERAL_TYPES2 = /* @__PURE__ */ new Set(["string", "integer", "float", "true", "false", "none"]);
|
|
92093
92767
|
pythonStaticKeyDictComprehensionRuffVisitor = {
|
|
92094
92768
|
ruleKey: "bugs/deterministic/static-key-dict-comprehension-ruff",
|
|
@@ -92101,7 +92775,7 @@ var init_static_key_dict_comprehension_ruff = __esm({
|
|
|
92101
92775
|
const keyNode = pairNode.childForFieldName("key");
|
|
92102
92776
|
if (!keyNode)
|
|
92103
92777
|
return null;
|
|
92104
|
-
if (LITERAL_TYPES2.has(keyNode.type)) {
|
|
92778
|
+
if (LITERAL_TYPES2.has(keyNode.type) && !isFStringWithInterpolation(keyNode)) {
|
|
92105
92779
|
return makeViolation(this.ruleKey, keyNode, filePath, "high", "Static key in dict comprehension", `Dict comprehension with constant key \`${keyNode.text}\` \u2014 every iteration overwrites the same key, resulting in a single-entry dict with only the last value.`, sourceCode, "Use a variable expression as the key. If you only need values, use a list comprehension instead.");
|
|
92106
92780
|
}
|
|
92107
92781
|
return null;
|
|
@@ -93704,7 +94378,16 @@ function findFunctionDefs(root) {
|
|
|
93704
94378
|
hasVarArgs = true;
|
|
93705
94379
|
continue;
|
|
93706
94380
|
}
|
|
93707
|
-
if (p.type === "
|
|
94381
|
+
if (p.type === "typed_parameter") {
|
|
94382
|
+
const inner = p.namedChild(0);
|
|
94383
|
+
if (inner && (inner.type === "list_splat_pattern" || inner.type === "dictionary_splat_pattern")) {
|
|
94384
|
+
hasVarArgs = true;
|
|
94385
|
+
continue;
|
|
94386
|
+
}
|
|
94387
|
+
required++;
|
|
94388
|
+
continue;
|
|
94389
|
+
}
|
|
94390
|
+
if (p.type === "identifier") {
|
|
93708
94391
|
required++;
|
|
93709
94392
|
} else if (p.type === "default_parameter" || p.type === "typed_default_parameter") {
|
|
93710
94393
|
optional++;
|
|
@@ -95690,14 +96373,25 @@ var init_expression_complexity = __esm({
|
|
|
95690
96373
|
return null;
|
|
95691
96374
|
let operatorCount = 0;
|
|
95692
96375
|
const BINARY_TYPES = /* @__PURE__ */ new Set(["binary_expression", "logical_expression"]);
|
|
96376
|
+
const FUNCTION_BOUNDARY_TYPES = /* @__PURE__ */ new Set([
|
|
96377
|
+
"function_declaration",
|
|
96378
|
+
"function_expression",
|
|
96379
|
+
"arrow_function",
|
|
96380
|
+
"method_definition",
|
|
96381
|
+
"generator_function_declaration",
|
|
96382
|
+
"generator_function"
|
|
96383
|
+
]);
|
|
95693
96384
|
function countOps(n) {
|
|
95694
96385
|
if (BINARY_TYPES.has(n.type)) {
|
|
95695
96386
|
operatorCount++;
|
|
95696
96387
|
}
|
|
95697
96388
|
for (let i = 0; i < n.childCount; i++) {
|
|
95698
96389
|
const child = n.child(i);
|
|
95699
|
-
if (child)
|
|
95700
|
-
|
|
96390
|
+
if (!child)
|
|
96391
|
+
continue;
|
|
96392
|
+
if (FUNCTION_BOUNDARY_TYPES.has(child.type))
|
|
96393
|
+
continue;
|
|
96394
|
+
countOps(child);
|
|
95701
96395
|
}
|
|
95702
96396
|
}
|
|
95703
96397
|
countOps(expr);
|
|
@@ -105924,26 +106618,6 @@ var init_and_or_ternary = __esm({
|
|
|
105924
106618
|
}
|
|
105925
106619
|
});
|
|
105926
106620
|
|
|
105927
|
-
// packages/analyzer/dist/rules/code-quality/visitors/python/any-type-hint.js
|
|
105928
|
-
var pythonAnyTypeHintVisitor;
|
|
105929
|
-
var init_any_type_hint = __esm({
|
|
105930
|
-
"packages/analyzer/dist/rules/code-quality/visitors/python/any-type-hint.js"() {
|
|
105931
|
-
"use strict";
|
|
105932
|
-
init_types2();
|
|
105933
|
-
pythonAnyTypeHintVisitor = {
|
|
105934
|
-
ruleKey: "code-quality/deterministic/any-type-hint",
|
|
105935
|
-
languages: ["python"],
|
|
105936
|
-
nodeTypes: ["type"],
|
|
105937
|
-
visit(node2, filePath, sourceCode) {
|
|
105938
|
-
const text = node2.text.trim();
|
|
105939
|
-
if (text !== "Any")
|
|
105940
|
-
return null;
|
|
105941
|
-
return makeViolation(this.ruleKey, node2, filePath, "medium", "Any used as type hint", "Using `Any` as a type hint defeats type checking \u2014 use a more specific type.", sourceCode, "Replace `Any` with a specific type annotation, or use `object` if truly any type is acceptable.");
|
|
105942
|
-
}
|
|
105943
|
-
};
|
|
105944
|
-
}
|
|
105945
|
-
});
|
|
105946
|
-
|
|
105947
106621
|
// packages/analyzer/dist/rules/code-quality/visitors/python/assert-in-production.js
|
|
105948
106622
|
var pythonAssertInProductionVisitor;
|
|
105949
106623
|
var init_assert_in_production = __esm({
|
|
@@ -108480,6 +109154,24 @@ var init_implicit_string_concatenation = __esm({
|
|
|
108480
109154
|
const grandparent = parent.parent;
|
|
108481
109155
|
if (!isCollection(parent) && !isCollection(grandparent ?? { type: "" }))
|
|
108482
109156
|
return null;
|
|
109157
|
+
if (node2.startPosition.row !== node2.endPosition.row) {
|
|
109158
|
+
const parts = node2.namedChildren;
|
|
109159
|
+
let allButLastEndWithSpace = parts.length > 1;
|
|
109160
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
109161
|
+
const t = parts[i].text;
|
|
109162
|
+
const inner = t.replace(/['"]+$/, "");
|
|
109163
|
+
const lastChar = inner.charAt(inner.length - 1);
|
|
109164
|
+
if (lastChar !== " " && lastChar !== "\\" && lastChar !== " ") {
|
|
109165
|
+
allButLastEndWithSpace = false;
|
|
109166
|
+
break;
|
|
109167
|
+
}
|
|
109168
|
+
}
|
|
109169
|
+
if (allButLastEndWithSpace)
|
|
109170
|
+
return null;
|
|
109171
|
+
const totalLen = parts.reduce((sum, p) => sum + p.text.length, 0);
|
|
109172
|
+
if (totalLen > 60)
|
|
109173
|
+
return null;
|
|
109174
|
+
}
|
|
108483
109175
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Implicit string concatenation in collection", "Adjacent string literals in a collection are implicitly concatenated \u2014 this may be a missing comma.", sourceCode, "Add a comma between the strings, or use explicit `+` concatenation if intentional.");
|
|
108484
109176
|
}
|
|
108485
109177
|
};
|
|
@@ -112223,6 +112915,14 @@ var init_try_except_continue = __esm({
|
|
|
112223
112915
|
return null;
|
|
112224
112916
|
if (stmts[0].type !== "continue_statement")
|
|
112225
112917
|
return null;
|
|
112918
|
+
const exprChildren = node2.namedChildren.filter((c) => c.type !== "block" && c.type !== "comment");
|
|
112919
|
+
if (exprChildren.length > 0) {
|
|
112920
|
+
const exceptionType = exprChildren[0];
|
|
112921
|
+
const typeText = exceptionType.type === "as_pattern" ? exceptionType.namedChildren[0]?.text : exceptionType.text;
|
|
112922
|
+
if (typeText && typeText !== "Exception" && typeText !== "BaseException") {
|
|
112923
|
+
return null;
|
|
112924
|
+
}
|
|
112925
|
+
}
|
|
112226
112926
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Silent exception with continue", "`except` block with only `continue` silently ignores errors in loops.", sourceCode, "Add logging or error handling before `continue`, or use `contextlib.suppress()` for intentional suppression.");
|
|
112227
112927
|
}
|
|
112228
112928
|
};
|
|
@@ -114594,34 +115294,56 @@ var init_ambiguous_unicode_character = __esm({
|
|
|
114594
115294
|
"use strict";
|
|
114595
115295
|
init_types2();
|
|
114596
115296
|
CONFUSABLE_MAP = {
|
|
114597
|
-
"\xB4": "'",
|
|
114598
|
-
// acute accent → apostrophe
|
|
114599
|
-
"\u2018": "'",
|
|
114600
|
-
// left single quote → apostrophe
|
|
114601
|
-
"\u2019": "'",
|
|
114602
|
-
// right single quote → apostrophe
|
|
114603
|
-
"\u201C": '"',
|
|
114604
|
-
// left double quote → double quote
|
|
114605
|
-
"\u201D": '"',
|
|
114606
|
-
// right double quote → double quote
|
|
114607
|
-
"\u2013": "-",
|
|
114608
|
-
// en dash → hyphen
|
|
114609
|
-
"\u2014": "--",
|
|
114610
|
-
// em dash → double hyphen
|
|
114611
|
-
"\xAD": "-",
|
|
114612
|
-
// soft hyphen
|
|
114613
|
-
"\u2212": "-",
|
|
114614
|
-
// minus sign
|
|
114615
|
-
"\xD7": "x",
|
|
114616
|
-
// multiplication sign
|
|
114617
115297
|
"\u03BF": "o",
|
|
114618
115298
|
// Greek small letter omicron
|
|
114619
115299
|
"\u0430": "a",
|
|
114620
115300
|
// Cyrillic small a
|
|
114621
115301
|
"\u0435": "e",
|
|
114622
115302
|
// Cyrillic small e
|
|
114623
|
-
"\u043E": "o"
|
|
115303
|
+
"\u043E": "o",
|
|
114624
115304
|
// Cyrillic small o
|
|
115305
|
+
"\u0440": "p",
|
|
115306
|
+
// Cyrillic small er
|
|
115307
|
+
"\u0441": "c",
|
|
115308
|
+
// Cyrillic small es
|
|
115309
|
+
"\u0445": "x",
|
|
115310
|
+
// Cyrillic small ha
|
|
115311
|
+
"\u0455": "s",
|
|
115312
|
+
// Cyrillic small dze
|
|
115313
|
+
"\u0456": "i",
|
|
115314
|
+
// Cyrillic small Byelorussian-Ukrainian I
|
|
115315
|
+
"\u0501": "d",
|
|
115316
|
+
// Cyrillic small komi de
|
|
115317
|
+
"\u051B": "q",
|
|
115318
|
+
// Cyrillic small qa
|
|
115319
|
+
"\u051D": "w",
|
|
115320
|
+
// Cyrillic small we
|
|
115321
|
+
"\u0399": "I",
|
|
115322
|
+
// Greek capital iota
|
|
115323
|
+
"\u0396": "Z",
|
|
115324
|
+
// Greek capital zeta
|
|
115325
|
+
"\u039F": "O",
|
|
115326
|
+
// Greek capital omicron
|
|
115327
|
+
"\u0410": "A",
|
|
115328
|
+
// Cyrillic capital A
|
|
115329
|
+
"\u0415": "E",
|
|
115330
|
+
// Cyrillic capital E
|
|
115331
|
+
"\u041A": "K",
|
|
115332
|
+
// Cyrillic capital Ka
|
|
115333
|
+
"\u041C": "M",
|
|
115334
|
+
// Cyrillic capital Em
|
|
115335
|
+
"\u041D": "H",
|
|
115336
|
+
// Cyrillic capital En (renders as H)
|
|
115337
|
+
"\u041E": "O",
|
|
115338
|
+
// Cyrillic capital O
|
|
115339
|
+
"\u0420": "P",
|
|
115340
|
+
// Cyrillic capital er
|
|
115341
|
+
"\u0421": "C",
|
|
115342
|
+
// Cyrillic capital es
|
|
115343
|
+
"\u0422": "T",
|
|
115344
|
+
// Cyrillic capital te
|
|
115345
|
+
"\u0425": "X"
|
|
115346
|
+
// Cyrillic capital ha
|
|
114625
115347
|
};
|
|
114626
115348
|
pythonAmbiguousUnicodeCharacterVisitor = {
|
|
114627
115349
|
ruleKey: "code-quality/deterministic/ambiguous-unicode-character",
|
|
@@ -114869,6 +115591,8 @@ var init_regex_char_class_preferred = __esm({
|
|
|
114869
115591
|
if (!isReCall4)
|
|
114870
115592
|
return null;
|
|
114871
115593
|
const pattern = node2.text.slice(1, -1);
|
|
115594
|
+
if (/\(\?[aiLmux]*s[aiLmux]*[):]/.test(pattern))
|
|
115595
|
+
return null;
|
|
114872
115596
|
if (/\..+?\?/.test(pattern) || /\.\*\?/.test(pattern)) {
|
|
114873
115597
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Reluctant quantifier where character class preferred", "Using `.+?` or `.*?` \u2014 a character class like `[^x]*` is more explicit and efficient than a reluctant quantifier.", sourceCode, "Replace the reluctant quantifier with an explicit character class.");
|
|
114874
115598
|
}
|
|
@@ -115596,6 +116320,62 @@ var init_lambda_reserved_env_var = __esm({
|
|
|
115596
116320
|
});
|
|
115597
116321
|
|
|
115598
116322
|
// packages/analyzer/dist/rules/code-quality/visitors/python/boto3-client-error.js
|
|
116323
|
+
function findAwsClientVarNames(root) {
|
|
116324
|
+
const names = /* @__PURE__ */ new Set();
|
|
116325
|
+
function walk(n) {
|
|
116326
|
+
if (n.type === "assignment") {
|
|
116327
|
+
const lhs = n.childForFieldName("left");
|
|
116328
|
+
const rhs = n.childForFieldName("right");
|
|
116329
|
+
if (lhs?.type === "identifier" && rhs?.type === "call") {
|
|
116330
|
+
const fn = rhs.childForFieldName("function");
|
|
116331
|
+
if (fn?.type === "attribute") {
|
|
116332
|
+
const obj = fn.childForFieldName("object");
|
|
116333
|
+
const attr = fn.childForFieldName("attribute");
|
|
116334
|
+
if ((obj?.text === "boto3" || obj?.text === "aiobotocore") && (attr?.text === "client" || attr?.text === "resource")) {
|
|
116335
|
+
names.add(lhs.text);
|
|
116336
|
+
}
|
|
116337
|
+
}
|
|
116338
|
+
}
|
|
116339
|
+
}
|
|
116340
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
116341
|
+
const child = n.child(i);
|
|
116342
|
+
if (child)
|
|
116343
|
+
walk(child);
|
|
116344
|
+
}
|
|
116345
|
+
}
|
|
116346
|
+
walk(root);
|
|
116347
|
+
return names;
|
|
116348
|
+
}
|
|
116349
|
+
function bodyHasAwsCall(body, awsVarNames) {
|
|
116350
|
+
let found = false;
|
|
116351
|
+
function walk(n) {
|
|
116352
|
+
if (found)
|
|
116353
|
+
return;
|
|
116354
|
+
if (n.type === "call") {
|
|
116355
|
+
const fn = n.childForFieldName("function");
|
|
116356
|
+
if (fn?.type === "attribute") {
|
|
116357
|
+
let receiver = fn.childForFieldName("object");
|
|
116358
|
+
while (receiver?.type === "attribute") {
|
|
116359
|
+
receiver = receiver.childForFieldName("object");
|
|
116360
|
+
}
|
|
116361
|
+
if (receiver?.type === "identifier") {
|
|
116362
|
+
const id = receiver.text;
|
|
116363
|
+
if (id === "boto3" || id === "botocore" || id === "aiobotocore" || awsVarNames.has(id)) {
|
|
116364
|
+
found = true;
|
|
116365
|
+
return;
|
|
116366
|
+
}
|
|
116367
|
+
}
|
|
116368
|
+
}
|
|
116369
|
+
}
|
|
116370
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
116371
|
+
const child = n.child(i);
|
|
116372
|
+
if (child)
|
|
116373
|
+
walk(child);
|
|
116374
|
+
}
|
|
116375
|
+
}
|
|
116376
|
+
walk(body);
|
|
116377
|
+
return found;
|
|
116378
|
+
}
|
|
115599
116379
|
var pythonBoto3ClientErrorVisitor;
|
|
115600
116380
|
var init_boto3_client_error = __esm({
|
|
115601
116381
|
"packages/analyzer/dist/rules/code-quality/visitors/python/boto3-client-error.js"() {
|
|
@@ -115633,6 +116413,9 @@ var init_boto3_client_error = __esm({
|
|
|
115633
116413
|
});
|
|
115634
116414
|
if (!hasBareOrGeneric)
|
|
115635
116415
|
return null;
|
|
116416
|
+
const awsVarNames = findAwsClientVarNames(node2.tree.rootNode);
|
|
116417
|
+
if (!bodyHasAwsCall(body, awsVarNames))
|
|
116418
|
+
return null;
|
|
115636
116419
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Uncaught botocore ClientError", "boto3/botocore calls inside try block but `ClientError` is not explicitly caught \u2014 generic `except` hides AWS API errors.", sourceCode, "Add explicit `except botocore.exceptions.ClientError as e:` to handle AWS API errors.");
|
|
115637
116420
|
}
|
|
115638
116421
|
};
|
|
@@ -116497,7 +117280,6 @@ var init_python7 = __esm({
|
|
|
116497
117280
|
init_commented_out_code2();
|
|
116498
117281
|
init_abstract_class_without_abstract_method();
|
|
116499
117282
|
init_and_or_ternary();
|
|
116500
|
-
init_any_type_hint();
|
|
116501
117283
|
init_assert_in_production();
|
|
116502
117284
|
init_async_long_sleep();
|
|
116503
117285
|
init_async_unused_async();
|
|
@@ -116752,7 +117534,6 @@ var init_python7 = __esm({
|
|
|
116752
117534
|
pythonCommentedOutCodeVisitor,
|
|
116753
117535
|
pythonAbstractClassWithoutAbstractMethodVisitor,
|
|
116754
117536
|
pythonAndOrTernaryVisitor,
|
|
116755
|
-
pythonAnyTypeHintVisitor,
|
|
116756
117537
|
pythonAssertInProductionVisitor,
|
|
116757
117538
|
pythonAsyncLongSleepVisitor,
|
|
116758
117539
|
pythonAsyncUnusedAsyncVisitor,
|
|
@@ -119133,6 +119914,22 @@ var init_incorrect_dict_iterator = __esm({
|
|
|
119133
119914
|
});
|
|
119134
119915
|
|
|
119135
119916
|
// packages/analyzer/dist/rules/performance/visitors/python/try-except-in-loop.js
|
|
119917
|
+
function isTypedSkipOnlyHandler(exceptClause) {
|
|
119918
|
+
const block = exceptClause.namedChildren.find((c) => c.type === "block");
|
|
119919
|
+
if (!block)
|
|
119920
|
+
return false;
|
|
119921
|
+
const stmts = block.namedChildren.filter((c) => c.type !== "comment");
|
|
119922
|
+
if (stmts.length !== 1)
|
|
119923
|
+
return false;
|
|
119924
|
+
if (stmts[0].type !== "continue_statement" && stmts[0].type !== "pass_statement")
|
|
119925
|
+
return false;
|
|
119926
|
+
const exprChildren = exceptClause.namedChildren.filter((c) => c.type !== "block" && c.type !== "comment");
|
|
119927
|
+
if (exprChildren.length === 0)
|
|
119928
|
+
return false;
|
|
119929
|
+
const exceptionType = exprChildren[0];
|
|
119930
|
+
const typeText = exceptionType.type === "as_pattern" ? exceptionType.namedChildren[0]?.text : exceptionType.text;
|
|
119931
|
+
return typeText !== void 0 && typeText !== "Exception" && typeText !== "BaseException";
|
|
119932
|
+
}
|
|
119136
119933
|
var tryExceptInLoopVisitor;
|
|
119137
119934
|
var init_try_except_in_loop = __esm({
|
|
119138
119935
|
"packages/analyzer/dist/rules/performance/visitors/python/try-except-in-loop.js"() {
|
|
@@ -119146,6 +119943,10 @@ var init_try_except_in_loop = __esm({
|
|
|
119146
119943
|
visit(node2, filePath, sourceCode) {
|
|
119147
119944
|
if (!isInsidePythonLoop(node2))
|
|
119148
119945
|
return null;
|
|
119946
|
+
const exceptClauses = node2.namedChildren.filter((c) => c.type === "except_clause");
|
|
119947
|
+
if (exceptClauses.length > 0 && exceptClauses.every(isTypedSkipOnlyHandler)) {
|
|
119948
|
+
return null;
|
|
119949
|
+
}
|
|
119149
119950
|
return makeViolation(this.ruleKey, node2, filePath, "low", "try/except inside loop", "try/except inside a loop adds overhead per iteration. Move the try/except outside the loop if possible.", sourceCode, "Wrap the entire loop in a try/except, or use a conditional check instead of exception handling.");
|
|
119150
119951
|
}
|
|
119151
119952
|
};
|
|
@@ -119519,6 +120320,9 @@ var init_catch_without_error_type = __esm({
|
|
|
119519
120320
|
const hasTypeAnnotation = node2.childForFieldName("type") !== null;
|
|
119520
120321
|
if (hasTypeAnnotation)
|
|
119521
120322
|
return null;
|
|
120323
|
+
const stmts = body.namedChildren.filter((c) => c.type !== "comment");
|
|
120324
|
+
if (stmts.length <= 1)
|
|
120325
|
+
return null;
|
|
119522
120326
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Catch without error type discrimination", "Catch block does not check or narrow the error type. Different error types may need different handling.", sourceCode, "Use instanceof checks or type guards in the catch block to handle specific error types.");
|
|
119523
120327
|
}
|
|
119524
120328
|
};
|
|
@@ -119586,6 +120390,28 @@ function isInsidePromiseConstructor(node2) {
|
|
|
119586
120390
|
}
|
|
119587
120391
|
return false;
|
|
119588
120392
|
}
|
|
120393
|
+
function looksLikeAwsLambda(sourceCode) {
|
|
120394
|
+
if (/\baws-lambda\b/.test(sourceCode))
|
|
120395
|
+
return true;
|
|
120396
|
+
if (/\bAPIGatewayProxy(Event|Result|Handler)/.test(sourceCode))
|
|
120397
|
+
return true;
|
|
120398
|
+
if (/\bLambda(Event|Result|Context|Handler)\b/.test(sourceCode))
|
|
120399
|
+
return true;
|
|
120400
|
+
if (/export\s+const\s+handler\s*[:=]\s*async\s*\(/.test(sourceCode))
|
|
120401
|
+
return true;
|
|
120402
|
+
return false;
|
|
120403
|
+
}
|
|
120404
|
+
function looksLikeAwsCdkScript(sourceCode) {
|
|
120405
|
+
if (/\baws-cdk-lib\b/.test(sourceCode))
|
|
120406
|
+
return true;
|
|
120407
|
+
if (/@aws-cdk\//.test(sourceCode))
|
|
120408
|
+
return true;
|
|
120409
|
+
if (/\bnew\s+cdk\.(App|Stack)\b/.test(sourceCode))
|
|
120410
|
+
return true;
|
|
120411
|
+
if (/\bapp\.synth\(\)/.test(sourceCode))
|
|
120412
|
+
return true;
|
|
120413
|
+
return false;
|
|
120414
|
+
}
|
|
119589
120415
|
var init_helpers6 = __esm({
|
|
119590
120416
|
"packages/analyzer/dist/rules/reliability/visitors/javascript/_helpers.js"() {
|
|
119591
120417
|
"use strict";
|
|
@@ -119872,7 +120698,11 @@ var init_unchecked_array_access = __esm({
|
|
|
119872
120698
|
init_helpers6();
|
|
119873
120699
|
uncheckedArrayAccessVisitor = {
|
|
119874
120700
|
ruleKey: "reliability/deterministic/unchecked-array-access",
|
|
119875
|
-
|
|
120701
|
+
// TS/TSX only. The rule is meaningful when the project opts into
|
|
120702
|
+
// `noUncheckedIndexedAccess`, which only exists in TypeScript. Plain
|
|
120703
|
+
// JS / JSX has no static type system to opt into - every index access
|
|
120704
|
+
// is implicitly `T | undefined` and flagging it is pure noise.
|
|
120705
|
+
languages: ["typescript", "tsx"],
|
|
119876
120706
|
nodeTypes: ["subscript_expression"],
|
|
119877
120707
|
visit(node2, filePath, sourceCode) {
|
|
119878
120708
|
const object = node2.childForFieldName("object");
|
|
@@ -120168,6 +120998,7 @@ var init_uncaught_exception_no_handler = __esm({
|
|
|
120168
120998
|
"packages/analyzer/dist/rules/reliability/visitors/javascript/uncaught-exception-no-handler.js"() {
|
|
120169
120999
|
"use strict";
|
|
120170
121000
|
init_types2();
|
|
121001
|
+
init_helpers6();
|
|
120171
121002
|
uncaughtExceptionNoHandlerVisitor = {
|
|
120172
121003
|
ruleKey: "reliability/deterministic/uncaught-exception-no-handler",
|
|
120173
121004
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -120180,6 +121011,10 @@ var init_uncaught_exception_no_handler = __esm({
|
|
|
120180
121011
|
if (!lowerPath.includes("index.") && !lowerPath.includes("main.") && !lowerPath.includes("server.") && !lowerPath.includes("app.") && !lowerPath.endsWith("/worker.ts") && !lowerPath.endsWith("/worker.js") && !lowerPath.includes("bin/")) {
|
|
120181
121012
|
return null;
|
|
120182
121013
|
}
|
|
121014
|
+
if (looksLikeAwsLambda(sourceCode))
|
|
121015
|
+
return null;
|
|
121016
|
+
if (looksLikeAwsCdkScript(sourceCode))
|
|
121017
|
+
return null;
|
|
120183
121018
|
const text = sourceCode.replace(/\/\/.*$/gm, "");
|
|
120184
121019
|
if (text.includes("'uncaughtException'") || text.includes('"uncaughtException"') || text.includes("`uncaughtException`")) {
|
|
120185
121020
|
return null;
|
|
@@ -120232,6 +121067,7 @@ var init_unhandled_rejection_no_handler = __esm({
|
|
|
120232
121067
|
"packages/analyzer/dist/rules/reliability/visitors/javascript/unhandled-rejection-no-handler.js"() {
|
|
120233
121068
|
"use strict";
|
|
120234
121069
|
init_types2();
|
|
121070
|
+
init_helpers6();
|
|
120235
121071
|
unhandledRejectionNoHandlerVisitor = {
|
|
120236
121072
|
ruleKey: "reliability/deterministic/unhandled-rejection-no-handler",
|
|
120237
121073
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -120244,6 +121080,10 @@ var init_unhandled_rejection_no_handler = __esm({
|
|
|
120244
121080
|
if (!lowerPath.includes("index.") && !lowerPath.includes("main.") && !lowerPath.includes("server.") && !lowerPath.includes("app.") && !lowerPath.endsWith("/worker.ts") && !lowerPath.endsWith("/worker.js") && !lowerPath.includes("bin/")) {
|
|
120245
121081
|
return null;
|
|
120246
121082
|
}
|
|
121083
|
+
if (looksLikeAwsLambda(sourceCode))
|
|
121084
|
+
return null;
|
|
121085
|
+
if (looksLikeAwsCdkScript(sourceCode))
|
|
121086
|
+
return null;
|
|
120247
121087
|
const text = sourceCode.replace(/\/\/.*$/gm, "");
|
|
120248
121088
|
if (text.includes("'unhandledRejection'") || text.includes('"unhandledRejection"') || text.includes("`unhandledRejection`")) {
|
|
120249
121089
|
return null;
|
|
@@ -121560,6 +122400,21 @@ var init_duplicate_import4 = __esm({
|
|
|
121560
122400
|
});
|
|
121561
122401
|
|
|
121562
122402
|
// packages/analyzer/dist/rules/architecture/visitors/python/declarations-in-global-scope.js
|
|
122403
|
+
function isTypingConstruct(node2) {
|
|
122404
|
+
if (node2.type !== "subscript")
|
|
122405
|
+
return false;
|
|
122406
|
+
const value = node2.childForFieldName("value");
|
|
122407
|
+
if (!value)
|
|
122408
|
+
return false;
|
|
122409
|
+
if (value.type === "identifier")
|
|
122410
|
+
return TYPING_NAMES.has(value.text);
|
|
122411
|
+
if (value.type === "attribute") {
|
|
122412
|
+
const attr = value.childForFieldName("attribute");
|
|
122413
|
+
if (attr && TYPING_NAMES.has(attr.text))
|
|
122414
|
+
return true;
|
|
122415
|
+
}
|
|
122416
|
+
return false;
|
|
122417
|
+
}
|
|
121563
122418
|
function rootIsCall(node2) {
|
|
121564
122419
|
if (node2.type === "call")
|
|
121565
122420
|
return true;
|
|
@@ -121575,11 +122430,46 @@ function rootIsCall(node2) {
|
|
|
121575
122430
|
}
|
|
121576
122431
|
return false;
|
|
121577
122432
|
}
|
|
121578
|
-
var pythonDeclarationsInGlobalScopeVisitor;
|
|
122433
|
+
var TYPING_NAMES, pythonDeclarationsInGlobalScopeVisitor;
|
|
121579
122434
|
var init_declarations_in_global_scope2 = __esm({
|
|
121580
122435
|
"packages/analyzer/dist/rules/architecture/visitors/python/declarations-in-global-scope.js"() {
|
|
121581
122436
|
"use strict";
|
|
121582
122437
|
init_types2();
|
|
122438
|
+
TYPING_NAMES = /* @__PURE__ */ new Set([
|
|
122439
|
+
"Callable",
|
|
122440
|
+
"Optional",
|
|
122441
|
+
"Union",
|
|
122442
|
+
"Literal",
|
|
122443
|
+
"Annotated",
|
|
122444
|
+
"Final",
|
|
122445
|
+
"List",
|
|
122446
|
+
"Tuple",
|
|
122447
|
+
"Dict",
|
|
122448
|
+
"Set",
|
|
122449
|
+
"FrozenSet",
|
|
122450
|
+
"Type",
|
|
122451
|
+
"ClassVar",
|
|
122452
|
+
"Sequence",
|
|
122453
|
+
"Mapping",
|
|
122454
|
+
"MutableMapping",
|
|
122455
|
+
"Iterable",
|
|
122456
|
+
"Iterator",
|
|
122457
|
+
"Generator",
|
|
122458
|
+
"Coroutine",
|
|
122459
|
+
"Awaitable",
|
|
122460
|
+
"AsyncIterable",
|
|
122461
|
+
"AsyncIterator",
|
|
122462
|
+
"Protocol",
|
|
122463
|
+
"TypedDict",
|
|
122464
|
+
"NamedTuple",
|
|
122465
|
+
// Built-in generics (PEP 585)
|
|
122466
|
+
"list",
|
|
122467
|
+
"tuple",
|
|
122468
|
+
"dict",
|
|
122469
|
+
"set",
|
|
122470
|
+
"frozenset",
|
|
122471
|
+
"type"
|
|
122472
|
+
]);
|
|
121583
122473
|
pythonDeclarationsInGlobalScopeVisitor = {
|
|
121584
122474
|
ruleKey: "architecture/deterministic/declarations-in-global-scope",
|
|
121585
122475
|
languages: ["python"],
|
|
@@ -121641,6 +122531,8 @@ var init_declarations_in_global_scope2 = __esm({
|
|
|
121641
122531
|
return null;
|
|
121642
122532
|
if (right?.type === "attribute" && rootIsCall(right))
|
|
121643
122533
|
return null;
|
|
122534
|
+
if (right?.type === "subscript" && isTypingConstruct(right))
|
|
122535
|
+
return null;
|
|
121644
122536
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Mutable variable in global scope", `Module-level mutable variable '${name}' creates shared state that is hard to test.`, sourceCode, "Move into a function, class, or use UPPER_CASE for intended constants.");
|
|
121645
122537
|
}
|
|
121646
122538
|
};
|
|
@@ -122105,13 +122997,47 @@ var init_missing_transaction = __esm({
|
|
|
122105
122997
|
});
|
|
122106
122998
|
|
|
122107
122999
|
// packages/analyzer/dist/rules/database/visitors/javascript/unvalidated-external-data.js
|
|
122108
|
-
|
|
123000
|
+
function hasOrmLikeReceiver(node2) {
|
|
123001
|
+
const fn = node2.childForFieldName("function");
|
|
123002
|
+
if (fn?.type !== "member_expression")
|
|
123003
|
+
return false;
|
|
123004
|
+
let receiver = fn.childForFieldName("object");
|
|
123005
|
+
while (receiver?.type === "member_expression") {
|
|
123006
|
+
receiver = receiver.childForFieldName("object");
|
|
123007
|
+
}
|
|
123008
|
+
if (receiver?.type !== "identifier")
|
|
123009
|
+
return false;
|
|
123010
|
+
const name = receiver.text.toLowerCase();
|
|
123011
|
+
return ORM_RECEIVER_NAMES2.has(name);
|
|
123012
|
+
}
|
|
123013
|
+
var AMBIGUOUS_ORM_METHODS, ORM_RECEIVER_NAMES2, unvalidatedExternalDataVisitor;
|
|
122109
123014
|
var init_unvalidated_external_data = __esm({
|
|
122110
123015
|
"packages/analyzer/dist/rules/database/visitors/javascript/unvalidated-external-data.js"() {
|
|
122111
123016
|
"use strict";
|
|
122112
123017
|
init_types2();
|
|
122113
123018
|
init_helpers8();
|
|
122114
123019
|
init_javascript_helpers();
|
|
123020
|
+
AMBIGUOUS_ORM_METHODS = /* @__PURE__ */ new Set(["add", "delete"]);
|
|
123021
|
+
ORM_RECEIVER_NAMES2 = /* @__PURE__ */ new Set([
|
|
123022
|
+
"session",
|
|
123023
|
+
"db",
|
|
123024
|
+
"conn",
|
|
123025
|
+
"connection",
|
|
123026
|
+
"cursor",
|
|
123027
|
+
"engine",
|
|
123028
|
+
"database",
|
|
123029
|
+
"manager",
|
|
123030
|
+
"repo",
|
|
123031
|
+
"repository",
|
|
123032
|
+
"orm",
|
|
123033
|
+
"em",
|
|
123034
|
+
"tx",
|
|
123035
|
+
"trx",
|
|
123036
|
+
"knex",
|
|
123037
|
+
"prisma",
|
|
123038
|
+
"sequelize",
|
|
123039
|
+
"mongoose"
|
|
123040
|
+
]);
|
|
122115
123041
|
unvalidatedExternalDataVisitor = {
|
|
122116
123042
|
ruleKey: "database/deterministic/unvalidated-external-data",
|
|
122117
123043
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -122121,6 +123047,8 @@ var init_unvalidated_external_data = __esm({
|
|
|
122121
123047
|
const methodName = getMethodName(node2);
|
|
122122
123048
|
if (!ORM_WRITE_METHODS2.has(methodName) && !SQL_WRITE_METHODS.has(methodName))
|
|
122123
123049
|
return null;
|
|
123050
|
+
if (AMBIGUOUS_ORM_METHODS.has(methodName) && !hasOrmLikeReceiver(node2))
|
|
123051
|
+
return null;
|
|
122124
123052
|
const args = node2.childForFieldName("arguments");
|
|
122125
123053
|
if (!args)
|
|
122126
123054
|
return null;
|
|
@@ -122506,6 +123434,9 @@ var init_missing_migration2 = __esm({
|
|
|
122506
123434
|
if (!/alter\s+table|create\s+table|drop\s+table|create\s+index|drop\s+index/.test(sqlText)) {
|
|
122507
123435
|
return null;
|
|
122508
123436
|
}
|
|
123437
|
+
if (/if\s+(not\s+)?exists/.test(sqlText)) {
|
|
123438
|
+
return null;
|
|
123439
|
+
}
|
|
122509
123440
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Schema change outside migration file", `DDL statement found outside a migration file. Schema changes should be tracked in migrations.`, sourceCode, "Move this schema change into a versioned migration file.");
|
|
122510
123441
|
}
|
|
122511
123442
|
};
|
|
@@ -122513,6 +123444,122 @@ var init_missing_migration2 = __esm({
|
|
|
122513
123444
|
});
|
|
122514
123445
|
|
|
122515
123446
|
// packages/analyzer/dist/rules/database/visitors/python/connection-not-released.js
|
|
123447
|
+
function escapeRegExp2(s) {
|
|
123448
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
123449
|
+
}
|
|
123450
|
+
function isFactoryReturn(node2) {
|
|
123451
|
+
let current = node2.parent;
|
|
123452
|
+
while (current) {
|
|
123453
|
+
if (current.type === "return_statement")
|
|
123454
|
+
return true;
|
|
123455
|
+
if (current.type === "expression_statement")
|
|
123456
|
+
return false;
|
|
123457
|
+
if (current.type === "function_definition")
|
|
123458
|
+
return false;
|
|
123459
|
+
current = current.parent;
|
|
123460
|
+
}
|
|
123461
|
+
return false;
|
|
123462
|
+
}
|
|
123463
|
+
function isModuleLevelCache(node2) {
|
|
123464
|
+
let assign = node2.parent;
|
|
123465
|
+
while (assign) {
|
|
123466
|
+
if (assign.type === "assignment")
|
|
123467
|
+
break;
|
|
123468
|
+
if (assign.type === "expression_statement")
|
|
123469
|
+
return false;
|
|
123470
|
+
if (assign.type === "function_definition")
|
|
123471
|
+
return false;
|
|
123472
|
+
if (assign.type === "class_definition")
|
|
123473
|
+
return false;
|
|
123474
|
+
assign = assign.parent;
|
|
123475
|
+
}
|
|
123476
|
+
if (!assign)
|
|
123477
|
+
return false;
|
|
123478
|
+
const stmt = assign.parent;
|
|
123479
|
+
if (!(stmt?.type === "expression_statement" && stmt.parent?.type === "module"))
|
|
123480
|
+
return false;
|
|
123481
|
+
const lhs = assign.childForFieldName("left");
|
|
123482
|
+
const nameNode = lhs?.type === "identifier" ? lhs : null;
|
|
123483
|
+
if (!nameNode)
|
|
123484
|
+
return false;
|
|
123485
|
+
const name = nameNode.text;
|
|
123486
|
+
return /^[A-Z_][A-Z0-9_]*$/.test(name) || name.startsWith("_");
|
|
123487
|
+
}
|
|
123488
|
+
function isInstanceAttributeAssignmentInPrivateClass(node2) {
|
|
123489
|
+
let assign = node2.parent;
|
|
123490
|
+
while (assign) {
|
|
123491
|
+
if (assign.type === "assignment")
|
|
123492
|
+
break;
|
|
123493
|
+
if (assign.type === "expression_statement")
|
|
123494
|
+
return false;
|
|
123495
|
+
if (assign.type === "function_definition")
|
|
123496
|
+
return false;
|
|
123497
|
+
assign = assign.parent;
|
|
123498
|
+
}
|
|
123499
|
+
if (!assign)
|
|
123500
|
+
return false;
|
|
123501
|
+
const lhs = assign.childForFieldName("left");
|
|
123502
|
+
if (lhs?.type !== "attribute")
|
|
123503
|
+
return false;
|
|
123504
|
+
const obj = lhs.childForFieldName("object");
|
|
123505
|
+
if (!(obj?.type === "identifier" && (obj.text === "self" || obj.text === "cls")))
|
|
123506
|
+
return false;
|
|
123507
|
+
let cls = assign.parent;
|
|
123508
|
+
while (cls) {
|
|
123509
|
+
if (cls.type === "class_definition") {
|
|
123510
|
+
const name = cls.childForFieldName("name");
|
|
123511
|
+
return name?.type === "identifier" && name.text.startsWith("_");
|
|
123512
|
+
}
|
|
123513
|
+
cls = cls.parent;
|
|
123514
|
+
}
|
|
123515
|
+
return false;
|
|
123516
|
+
}
|
|
123517
|
+
function isClosedInFinally(node2) {
|
|
123518
|
+
let assignmentParent = node2.parent;
|
|
123519
|
+
while (assignmentParent) {
|
|
123520
|
+
if (assignmentParent.type === "assignment")
|
|
123521
|
+
break;
|
|
123522
|
+
if (assignmentParent.type === "expression_statement")
|
|
123523
|
+
return false;
|
|
123524
|
+
if (assignmentParent.type === "function_definition")
|
|
123525
|
+
return false;
|
|
123526
|
+
assignmentParent = assignmentParent.parent;
|
|
123527
|
+
}
|
|
123528
|
+
if (!assignmentParent)
|
|
123529
|
+
return false;
|
|
123530
|
+
const lhs = assignmentParent.childForFieldName("left");
|
|
123531
|
+
if (!lhs || lhs.type !== "identifier")
|
|
123532
|
+
return false;
|
|
123533
|
+
const varName = lhs.text;
|
|
123534
|
+
let func = assignmentParent.parent;
|
|
123535
|
+
while (func) {
|
|
123536
|
+
if (func.type === "function_definition")
|
|
123537
|
+
break;
|
|
123538
|
+
func = func.parent;
|
|
123539
|
+
}
|
|
123540
|
+
if (!func)
|
|
123541
|
+
return false;
|
|
123542
|
+
const body = func.childForFieldName("body");
|
|
123543
|
+
if (!body)
|
|
123544
|
+
return false;
|
|
123545
|
+
const closePattern2 = new RegExp(`\\b${escapeRegExp2(varName)}\\.(?:close|dispose|release)\\s*\\(`);
|
|
123546
|
+
let found = false;
|
|
123547
|
+
function walk(n) {
|
|
123548
|
+
if (found)
|
|
123549
|
+
return;
|
|
123550
|
+
if (n.type === "finally_clause" && closePattern2.test(n.text)) {
|
|
123551
|
+
found = true;
|
|
123552
|
+
return;
|
|
123553
|
+
}
|
|
123554
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
123555
|
+
const ch = n.child(i);
|
|
123556
|
+
if (ch)
|
|
123557
|
+
walk(ch);
|
|
123558
|
+
}
|
|
123559
|
+
}
|
|
123560
|
+
walk(body);
|
|
123561
|
+
return found;
|
|
123562
|
+
}
|
|
122516
123563
|
var pythonConnectionNotReleasedVisitor;
|
|
122517
123564
|
var init_connection_not_released2 = __esm({
|
|
122518
123565
|
"packages/analyzer/dist/rules/database/visitors/python/connection-not-released.js"() {
|
|
@@ -122560,6 +123607,14 @@ var init_connection_not_released2 = __esm({
|
|
|
122560
123607
|
break;
|
|
122561
123608
|
current = current.parent;
|
|
122562
123609
|
}
|
|
123610
|
+
if (isFactoryReturn(node2))
|
|
123611
|
+
return null;
|
|
123612
|
+
if (isClosedInFinally(node2))
|
|
123613
|
+
return null;
|
|
123614
|
+
if (isModuleLevelCache(node2))
|
|
123615
|
+
return null;
|
|
123616
|
+
if (isInstanceAttributeAssignmentInPrivateClass(node2))
|
|
123617
|
+
return null;
|
|
122563
123618
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Database connection not released", `${methodName}() acquires a connection but it may not be released on error. Use a context manager (with statement) or try/finally to guarantee the connection is released.`, sourceCode, "Use `with connection:` or a try/finally block to ensure the connection is always released.");
|
|
122564
123619
|
}
|
|
122565
123620
|
};
|
|
@@ -122643,6 +123698,23 @@ var init_orm_lazy_load_in_loop2 = __esm({
|
|
|
122643
123698
|
});
|
|
122644
123699
|
|
|
122645
123700
|
// packages/analyzer/dist/rules/database/visitors/python/missing-transaction.js
|
|
123701
|
+
function hasCursorLikeParam(params) {
|
|
123702
|
+
for (let i = 0; i < params.namedChildCount; i++) {
|
|
123703
|
+
const p = params.namedChild(i);
|
|
123704
|
+
if (!p)
|
|
123705
|
+
continue;
|
|
123706
|
+
let nameNode = null;
|
|
123707
|
+
if (p.type === "identifier") {
|
|
123708
|
+
nameNode = p;
|
|
123709
|
+
} else if (p.type === "typed_parameter" || p.type === "default_parameter" || p.type === "typed_default_parameter") {
|
|
123710
|
+
nameNode = p.childForFieldName("name") ?? p.namedChild(0);
|
|
123711
|
+
}
|
|
123712
|
+
if (nameNode && nameNode.type === "identifier" && CURSOR_PARAM_NAMES.has(nameNode.text)) {
|
|
123713
|
+
return true;
|
|
123714
|
+
}
|
|
123715
|
+
}
|
|
123716
|
+
return false;
|
|
123717
|
+
}
|
|
122646
123718
|
function isOrmWriteCall(n) {
|
|
122647
123719
|
const name = getPythonMethodName(n);
|
|
122648
123720
|
if (name === "execute" || name === "executemany") {
|
|
@@ -122650,7 +123722,7 @@ function isOrmWriteCall(n) {
|
|
|
122650
123722
|
const firstArg = args?.namedChildren[0];
|
|
122651
123723
|
if (firstArg?.type === "string") {
|
|
122652
123724
|
const sql = firstArg.text.toLowerCase();
|
|
122653
|
-
if (
|
|
123725
|
+
if (/\b(insert|update|delete|alter|create|drop)\b/.test(sql))
|
|
122654
123726
|
return true;
|
|
122655
123727
|
}
|
|
122656
123728
|
return false;
|
|
@@ -122663,26 +123735,26 @@ function isOrmWriteCall(n) {
|
|
|
122663
123735
|
if (fn?.type === "attribute") {
|
|
122664
123736
|
const receiver = fn.childForFieldName("object");
|
|
122665
123737
|
if (receiver?.type === "identifier") {
|
|
122666
|
-
return
|
|
123738
|
+
return ORM_RECEIVER_NAMES3.has(receiver.text);
|
|
122667
123739
|
}
|
|
122668
123740
|
if (receiver?.type === "attribute") {
|
|
122669
123741
|
const attrText = receiver.childForFieldName("attribute")?.text;
|
|
122670
|
-
if (attrText &&
|
|
123742
|
+
if (attrText && ORM_RECEIVER_NAMES3.has(attrText))
|
|
122671
123743
|
return true;
|
|
122672
123744
|
const rootObj = receiver.childForFieldName("object");
|
|
122673
|
-
if (rootObj?.type === "identifier" &&
|
|
123745
|
+
if (rootObj?.type === "identifier" && ORM_RECEIVER_NAMES3.has(rootObj.text))
|
|
122674
123746
|
return true;
|
|
122675
123747
|
}
|
|
122676
123748
|
}
|
|
122677
123749
|
return false;
|
|
122678
123750
|
}
|
|
122679
|
-
var
|
|
123751
|
+
var ORM_RECEIVER_NAMES3, UNAMBIGUOUS_DB_WRITES, CURSOR_PARAM_NAMES, pythonMissingTransactionVisitor;
|
|
122680
123752
|
var init_missing_transaction2 = __esm({
|
|
122681
123753
|
"packages/analyzer/dist/rules/database/visitors/python/missing-transaction.js"() {
|
|
122682
123754
|
"use strict";
|
|
122683
123755
|
init_types2();
|
|
122684
123756
|
init_helpers9();
|
|
122685
|
-
|
|
123757
|
+
ORM_RECEIVER_NAMES3 = /* @__PURE__ */ new Set([
|
|
122686
123758
|
"session",
|
|
122687
123759
|
"db",
|
|
122688
123760
|
"conn",
|
|
@@ -122702,6 +123774,17 @@ var init_missing_transaction2 = __esm({
|
|
|
122702
123774
|
"bulk_update",
|
|
122703
123775
|
"executemany"
|
|
122704
123776
|
]);
|
|
123777
|
+
CURSOR_PARAM_NAMES = /* @__PURE__ */ new Set([
|
|
123778
|
+
"cur",
|
|
123779
|
+
"cursor",
|
|
123780
|
+
"conn",
|
|
123781
|
+
"connection",
|
|
123782
|
+
"session",
|
|
123783
|
+
"db",
|
|
123784
|
+
"tx",
|
|
123785
|
+
"trans",
|
|
123786
|
+
"transaction"
|
|
123787
|
+
]);
|
|
122705
123788
|
pythonMissingTransactionVisitor = {
|
|
122706
123789
|
ruleKey: "database/deterministic/missing-transaction",
|
|
122707
123790
|
languages: ["python"],
|
|
@@ -122713,8 +123796,18 @@ var init_missing_transaction2 = __esm({
|
|
|
122713
123796
|
if (!body)
|
|
122714
123797
|
return null;
|
|
122715
123798
|
const bodyText = body.text.toLowerCase();
|
|
122716
|
-
if (/transaction|atomic|begin\b/.test(bodyText))
|
|
123799
|
+
if (/transaction|atomic|begin\b|autocommit\s*=\s*false/.test(bodyText))
|
|
123800
|
+
return null;
|
|
123801
|
+
if (/\bwith\s+[^:]*\bas\s+(conn|connection|cursor|cur|session|engine|tx|trans)\b[^:]*:/.test(bodyText))
|
|
122717
123802
|
return null;
|
|
123803
|
+
const funcDef = body.parent;
|
|
123804
|
+
if (funcDef?.type === "function_definition") {
|
|
123805
|
+
const nameNode = funcDef.childForFieldName("name");
|
|
123806
|
+
const params = funcDef.childForFieldName("parameters");
|
|
123807
|
+
if (nameNode?.text.startsWith("_") && params && hasCursorLikeParam(params)) {
|
|
123808
|
+
return null;
|
|
123809
|
+
}
|
|
123810
|
+
}
|
|
122718
123811
|
let writeCount = 0;
|
|
122719
123812
|
let seenSelf = false;
|
|
122720
123813
|
let isSecondOccurrence = false;
|
|
@@ -124128,6 +125221,503 @@ var init_dist2 = __esm({
|
|
|
124128
125221
|
}
|
|
124129
125222
|
});
|
|
124130
125223
|
|
|
125224
|
+
// node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js
|
|
125225
|
+
var require_windows = __commonJS({
|
|
125226
|
+
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module) {
|
|
125227
|
+
module.exports = isexe;
|
|
125228
|
+
isexe.sync = sync;
|
|
125229
|
+
var fs14 = __require("fs");
|
|
125230
|
+
function checkPathExt(path19, options) {
|
|
125231
|
+
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
125232
|
+
if (!pathext) {
|
|
125233
|
+
return true;
|
|
125234
|
+
}
|
|
125235
|
+
pathext = pathext.split(";");
|
|
125236
|
+
if (pathext.indexOf("") !== -1) {
|
|
125237
|
+
return true;
|
|
125238
|
+
}
|
|
125239
|
+
for (var i = 0; i < pathext.length; i++) {
|
|
125240
|
+
var p = pathext[i].toLowerCase();
|
|
125241
|
+
if (p && path19.substr(-p.length).toLowerCase() === p) {
|
|
125242
|
+
return true;
|
|
125243
|
+
}
|
|
125244
|
+
}
|
|
125245
|
+
return false;
|
|
125246
|
+
}
|
|
125247
|
+
function checkStat(stat, path19, options) {
|
|
125248
|
+
if (!stat.isSymbolicLink() && !stat.isFile()) {
|
|
125249
|
+
return false;
|
|
125250
|
+
}
|
|
125251
|
+
return checkPathExt(path19, options);
|
|
125252
|
+
}
|
|
125253
|
+
function isexe(path19, options, cb) {
|
|
125254
|
+
fs14.stat(path19, function(er, stat) {
|
|
125255
|
+
cb(er, er ? false : checkStat(stat, path19, options));
|
|
125256
|
+
});
|
|
125257
|
+
}
|
|
125258
|
+
function sync(path19, options) {
|
|
125259
|
+
return checkStat(fs14.statSync(path19), path19, options);
|
|
125260
|
+
}
|
|
125261
|
+
}
|
|
125262
|
+
});
|
|
125263
|
+
|
|
125264
|
+
// node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js
|
|
125265
|
+
var require_mode = __commonJS({
|
|
125266
|
+
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module) {
|
|
125267
|
+
module.exports = isexe;
|
|
125268
|
+
isexe.sync = sync;
|
|
125269
|
+
var fs14 = __require("fs");
|
|
125270
|
+
function isexe(path19, options, cb) {
|
|
125271
|
+
fs14.stat(path19, function(er, stat) {
|
|
125272
|
+
cb(er, er ? false : checkStat(stat, options));
|
|
125273
|
+
});
|
|
125274
|
+
}
|
|
125275
|
+
function sync(path19, options) {
|
|
125276
|
+
return checkStat(fs14.statSync(path19), options);
|
|
125277
|
+
}
|
|
125278
|
+
function checkStat(stat, options) {
|
|
125279
|
+
return stat.isFile() && checkMode(stat, options);
|
|
125280
|
+
}
|
|
125281
|
+
function checkMode(stat, options) {
|
|
125282
|
+
var mod = stat.mode;
|
|
125283
|
+
var uid = stat.uid;
|
|
125284
|
+
var gid = stat.gid;
|
|
125285
|
+
var myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid();
|
|
125286
|
+
var myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid();
|
|
125287
|
+
var u = parseInt("100", 8);
|
|
125288
|
+
var g = parseInt("010", 8);
|
|
125289
|
+
var o = parseInt("001", 8);
|
|
125290
|
+
var ug = u | g;
|
|
125291
|
+
var ret = mod & o || mod & g && gid === myGid || mod & u && uid === myUid || mod & ug && myUid === 0;
|
|
125292
|
+
return ret;
|
|
125293
|
+
}
|
|
125294
|
+
}
|
|
125295
|
+
});
|
|
125296
|
+
|
|
125297
|
+
// node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js
|
|
125298
|
+
var require_isexe = __commonJS({
|
|
125299
|
+
"node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module) {
|
|
125300
|
+
var fs14 = __require("fs");
|
|
125301
|
+
var core;
|
|
125302
|
+
if (process.platform === "win32" || global.TESTING_WINDOWS) {
|
|
125303
|
+
core = require_windows();
|
|
125304
|
+
} else {
|
|
125305
|
+
core = require_mode();
|
|
125306
|
+
}
|
|
125307
|
+
module.exports = isexe;
|
|
125308
|
+
isexe.sync = sync;
|
|
125309
|
+
function isexe(path19, options, cb) {
|
|
125310
|
+
if (typeof options === "function") {
|
|
125311
|
+
cb = options;
|
|
125312
|
+
options = {};
|
|
125313
|
+
}
|
|
125314
|
+
if (!cb) {
|
|
125315
|
+
if (typeof Promise !== "function") {
|
|
125316
|
+
throw new TypeError("callback not provided");
|
|
125317
|
+
}
|
|
125318
|
+
return new Promise(function(resolve7, reject) {
|
|
125319
|
+
isexe(path19, options || {}, function(er, is) {
|
|
125320
|
+
if (er) {
|
|
125321
|
+
reject(er);
|
|
125322
|
+
} else {
|
|
125323
|
+
resolve7(is);
|
|
125324
|
+
}
|
|
125325
|
+
});
|
|
125326
|
+
});
|
|
125327
|
+
}
|
|
125328
|
+
core(path19, options || {}, function(er, is) {
|
|
125329
|
+
if (er) {
|
|
125330
|
+
if (er.code === "EACCES" || options && options.ignoreErrors) {
|
|
125331
|
+
er = null;
|
|
125332
|
+
is = false;
|
|
125333
|
+
}
|
|
125334
|
+
}
|
|
125335
|
+
cb(er, is);
|
|
125336
|
+
});
|
|
125337
|
+
}
|
|
125338
|
+
function sync(path19, options) {
|
|
125339
|
+
try {
|
|
125340
|
+
return core.sync(path19, options || {});
|
|
125341
|
+
} catch (er) {
|
|
125342
|
+
if (options && options.ignoreErrors || er.code === "EACCES") {
|
|
125343
|
+
return false;
|
|
125344
|
+
} else {
|
|
125345
|
+
throw er;
|
|
125346
|
+
}
|
|
125347
|
+
}
|
|
125348
|
+
}
|
|
125349
|
+
}
|
|
125350
|
+
});
|
|
125351
|
+
|
|
125352
|
+
// node_modules/.pnpm/which@2.0.2/node_modules/which/which.js
|
|
125353
|
+
var require_which = __commonJS({
|
|
125354
|
+
"node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module) {
|
|
125355
|
+
var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
125356
|
+
var path19 = __require("path");
|
|
125357
|
+
var COLON = isWindows ? ";" : ":";
|
|
125358
|
+
var isexe = require_isexe();
|
|
125359
|
+
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
125360
|
+
var getPathInfo = (cmd, opt) => {
|
|
125361
|
+
const colon = opt.colon || COLON;
|
|
125362
|
+
const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [
|
|
125363
|
+
// windows always checks the cwd first
|
|
125364
|
+
...isWindows ? [process.cwd()] : [],
|
|
125365
|
+
...(opt.path || process.env.PATH || /* istanbul ignore next: very unusual */
|
|
125366
|
+
"").split(colon)
|
|
125367
|
+
];
|
|
125368
|
+
const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : "";
|
|
125369
|
+
const pathExt = isWindows ? pathExtExe.split(colon) : [""];
|
|
125370
|
+
if (isWindows) {
|
|
125371
|
+
if (cmd.indexOf(".") !== -1 && pathExt[0] !== "")
|
|
125372
|
+
pathExt.unshift("");
|
|
125373
|
+
}
|
|
125374
|
+
return {
|
|
125375
|
+
pathEnv,
|
|
125376
|
+
pathExt,
|
|
125377
|
+
pathExtExe
|
|
125378
|
+
};
|
|
125379
|
+
};
|
|
125380
|
+
var which = (cmd, opt, cb) => {
|
|
125381
|
+
if (typeof opt === "function") {
|
|
125382
|
+
cb = opt;
|
|
125383
|
+
opt = {};
|
|
125384
|
+
}
|
|
125385
|
+
if (!opt)
|
|
125386
|
+
opt = {};
|
|
125387
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
125388
|
+
const found = [];
|
|
125389
|
+
const step = (i) => new Promise((resolve7, reject) => {
|
|
125390
|
+
if (i === pathEnv.length)
|
|
125391
|
+
return opt.all && found.length ? resolve7(found) : reject(getNotFoundError(cmd));
|
|
125392
|
+
const ppRaw = pathEnv[i];
|
|
125393
|
+
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
125394
|
+
const pCmd = path19.join(pathPart, cmd);
|
|
125395
|
+
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
125396
|
+
resolve7(subStep(p, i, 0));
|
|
125397
|
+
});
|
|
125398
|
+
const subStep = (p, i, ii) => new Promise((resolve7, reject) => {
|
|
125399
|
+
if (ii === pathExt.length)
|
|
125400
|
+
return resolve7(step(i + 1));
|
|
125401
|
+
const ext2 = pathExt[ii];
|
|
125402
|
+
isexe(p + ext2, { pathExt: pathExtExe }, (er, is) => {
|
|
125403
|
+
if (!er && is) {
|
|
125404
|
+
if (opt.all)
|
|
125405
|
+
found.push(p + ext2);
|
|
125406
|
+
else
|
|
125407
|
+
return resolve7(p + ext2);
|
|
125408
|
+
}
|
|
125409
|
+
return resolve7(subStep(p, i, ii + 1));
|
|
125410
|
+
});
|
|
125411
|
+
});
|
|
125412
|
+
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
125413
|
+
};
|
|
125414
|
+
var whichSync = (cmd, opt) => {
|
|
125415
|
+
opt = opt || {};
|
|
125416
|
+
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
125417
|
+
const found = [];
|
|
125418
|
+
for (let i = 0; i < pathEnv.length; i++) {
|
|
125419
|
+
const ppRaw = pathEnv[i];
|
|
125420
|
+
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
125421
|
+
const pCmd = path19.join(pathPart, cmd);
|
|
125422
|
+
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
125423
|
+
for (let j = 0; j < pathExt.length; j++) {
|
|
125424
|
+
const cur = p + pathExt[j];
|
|
125425
|
+
try {
|
|
125426
|
+
const is = isexe.sync(cur, { pathExt: pathExtExe });
|
|
125427
|
+
if (is) {
|
|
125428
|
+
if (opt.all)
|
|
125429
|
+
found.push(cur);
|
|
125430
|
+
else
|
|
125431
|
+
return cur;
|
|
125432
|
+
}
|
|
125433
|
+
} catch (ex) {
|
|
125434
|
+
}
|
|
125435
|
+
}
|
|
125436
|
+
}
|
|
125437
|
+
if (opt.all && found.length)
|
|
125438
|
+
return found;
|
|
125439
|
+
if (opt.nothrow)
|
|
125440
|
+
return null;
|
|
125441
|
+
throw getNotFoundError(cmd);
|
|
125442
|
+
};
|
|
125443
|
+
module.exports = which;
|
|
125444
|
+
which.sync = whichSync;
|
|
125445
|
+
}
|
|
125446
|
+
});
|
|
125447
|
+
|
|
125448
|
+
// node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js
|
|
125449
|
+
var require_path_key = __commonJS({
|
|
125450
|
+
"node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js"(exports, module) {
|
|
125451
|
+
"use strict";
|
|
125452
|
+
var pathKey = (options = {}) => {
|
|
125453
|
+
const environment = options.env || process.env;
|
|
125454
|
+
const platform = options.platform || process.platform;
|
|
125455
|
+
if (platform !== "win32") {
|
|
125456
|
+
return "PATH";
|
|
125457
|
+
}
|
|
125458
|
+
return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path";
|
|
125459
|
+
};
|
|
125460
|
+
module.exports = pathKey;
|
|
125461
|
+
module.exports.default = pathKey;
|
|
125462
|
+
}
|
|
125463
|
+
});
|
|
125464
|
+
|
|
125465
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js
|
|
125466
|
+
var require_resolveCommand = __commonJS({
|
|
125467
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
|
|
125468
|
+
"use strict";
|
|
125469
|
+
var path19 = __require("path");
|
|
125470
|
+
var which = require_which();
|
|
125471
|
+
var getPathKey = require_path_key();
|
|
125472
|
+
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
125473
|
+
const env = parsed.options.env || process.env;
|
|
125474
|
+
const cwd = process.cwd();
|
|
125475
|
+
const hasCustomCwd = parsed.options.cwd != null;
|
|
125476
|
+
const shouldSwitchCwd = hasCustomCwd && process.chdir !== void 0 && !process.chdir.disabled;
|
|
125477
|
+
if (shouldSwitchCwd) {
|
|
125478
|
+
try {
|
|
125479
|
+
process.chdir(parsed.options.cwd);
|
|
125480
|
+
} catch (err) {
|
|
125481
|
+
}
|
|
125482
|
+
}
|
|
125483
|
+
let resolved;
|
|
125484
|
+
try {
|
|
125485
|
+
resolved = which.sync(parsed.command, {
|
|
125486
|
+
path: env[getPathKey({ env })],
|
|
125487
|
+
pathExt: withoutPathExt ? path19.delimiter : void 0
|
|
125488
|
+
});
|
|
125489
|
+
} catch (e) {
|
|
125490
|
+
} finally {
|
|
125491
|
+
if (shouldSwitchCwd) {
|
|
125492
|
+
process.chdir(cwd);
|
|
125493
|
+
}
|
|
125494
|
+
}
|
|
125495
|
+
if (resolved) {
|
|
125496
|
+
resolved = path19.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
|
|
125497
|
+
}
|
|
125498
|
+
return resolved;
|
|
125499
|
+
}
|
|
125500
|
+
function resolveCommand(parsed) {
|
|
125501
|
+
return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
|
|
125502
|
+
}
|
|
125503
|
+
module.exports = resolveCommand;
|
|
125504
|
+
}
|
|
125505
|
+
});
|
|
125506
|
+
|
|
125507
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.js
|
|
125508
|
+
var require_escape = __commonJS({
|
|
125509
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/escape.js"(exports, module) {
|
|
125510
|
+
"use strict";
|
|
125511
|
+
var metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
|
|
125512
|
+
function escapeCommand(arg) {
|
|
125513
|
+
arg = arg.replace(metaCharsRegExp, "^$1");
|
|
125514
|
+
return arg;
|
|
125515
|
+
}
|
|
125516
|
+
function escapeArgument(arg, doubleEscapeMetaChars) {
|
|
125517
|
+
arg = `${arg}`;
|
|
125518
|
+
arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
|
|
125519
|
+
arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1");
|
|
125520
|
+
arg = `"${arg}"`;
|
|
125521
|
+
arg = arg.replace(metaCharsRegExp, "^$1");
|
|
125522
|
+
if (doubleEscapeMetaChars) {
|
|
125523
|
+
arg = arg.replace(metaCharsRegExp, "^$1");
|
|
125524
|
+
}
|
|
125525
|
+
return arg;
|
|
125526
|
+
}
|
|
125527
|
+
module.exports.command = escapeCommand;
|
|
125528
|
+
module.exports.argument = escapeArgument;
|
|
125529
|
+
}
|
|
125530
|
+
});
|
|
125531
|
+
|
|
125532
|
+
// node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js
|
|
125533
|
+
var require_shebang_regex = __commonJS({
|
|
125534
|
+
"node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js"(exports, module) {
|
|
125535
|
+
"use strict";
|
|
125536
|
+
module.exports = /^#!(.*)/;
|
|
125537
|
+
}
|
|
125538
|
+
});
|
|
125539
|
+
|
|
125540
|
+
// node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js
|
|
125541
|
+
var require_shebang_command = __commonJS({
|
|
125542
|
+
"node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js"(exports, module) {
|
|
125543
|
+
"use strict";
|
|
125544
|
+
var shebangRegex = require_shebang_regex();
|
|
125545
|
+
module.exports = (string = "") => {
|
|
125546
|
+
const match2 = string.match(shebangRegex);
|
|
125547
|
+
if (!match2) {
|
|
125548
|
+
return null;
|
|
125549
|
+
}
|
|
125550
|
+
const [path19, argument] = match2[0].replace(/#! ?/, "").split(" ");
|
|
125551
|
+
const binary = path19.split("/").pop();
|
|
125552
|
+
if (binary === "env") {
|
|
125553
|
+
return argument;
|
|
125554
|
+
}
|
|
125555
|
+
return argument ? `${binary} ${argument}` : binary;
|
|
125556
|
+
};
|
|
125557
|
+
}
|
|
125558
|
+
});
|
|
125559
|
+
|
|
125560
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js
|
|
125561
|
+
var require_readShebang = __commonJS({
|
|
125562
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) {
|
|
125563
|
+
"use strict";
|
|
125564
|
+
var fs14 = __require("fs");
|
|
125565
|
+
var shebangCommand = require_shebang_command();
|
|
125566
|
+
function readShebang(command) {
|
|
125567
|
+
const size = 150;
|
|
125568
|
+
const buffer = Buffer.alloc(size);
|
|
125569
|
+
let fd;
|
|
125570
|
+
try {
|
|
125571
|
+
fd = fs14.openSync(command, "r");
|
|
125572
|
+
fs14.readSync(fd, buffer, 0, size, 0);
|
|
125573
|
+
fs14.closeSync(fd);
|
|
125574
|
+
} catch (e) {
|
|
125575
|
+
}
|
|
125576
|
+
return shebangCommand(buffer.toString());
|
|
125577
|
+
}
|
|
125578
|
+
module.exports = readShebang;
|
|
125579
|
+
}
|
|
125580
|
+
});
|
|
125581
|
+
|
|
125582
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js
|
|
125583
|
+
var require_parse2 = __commonJS({
|
|
125584
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/parse.js"(exports, module) {
|
|
125585
|
+
"use strict";
|
|
125586
|
+
var path19 = __require("path");
|
|
125587
|
+
var resolveCommand = require_resolveCommand();
|
|
125588
|
+
var escape3 = require_escape();
|
|
125589
|
+
var readShebang = require_readShebang();
|
|
125590
|
+
var isWin = process.platform === "win32";
|
|
125591
|
+
var isExecutableRegExp = /\.(?:com|exe)$/i;
|
|
125592
|
+
var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
|
|
125593
|
+
function detectShebang(parsed) {
|
|
125594
|
+
parsed.file = resolveCommand(parsed);
|
|
125595
|
+
const shebang = parsed.file && readShebang(parsed.file);
|
|
125596
|
+
if (shebang) {
|
|
125597
|
+
parsed.args.unshift(parsed.file);
|
|
125598
|
+
parsed.command = shebang;
|
|
125599
|
+
return resolveCommand(parsed);
|
|
125600
|
+
}
|
|
125601
|
+
return parsed.file;
|
|
125602
|
+
}
|
|
125603
|
+
function parseNonShell(parsed) {
|
|
125604
|
+
if (!isWin) {
|
|
125605
|
+
return parsed;
|
|
125606
|
+
}
|
|
125607
|
+
const commandFile = detectShebang(parsed);
|
|
125608
|
+
const needsShell = !isExecutableRegExp.test(commandFile);
|
|
125609
|
+
if (parsed.options.forceShell || needsShell) {
|
|
125610
|
+
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
|
|
125611
|
+
parsed.command = path19.normalize(parsed.command);
|
|
125612
|
+
parsed.command = escape3.command(parsed.command);
|
|
125613
|
+
parsed.args = parsed.args.map((arg) => escape3.argument(arg, needsDoubleEscapeMetaChars));
|
|
125614
|
+
const shellCommand = [parsed.command].concat(parsed.args).join(" ");
|
|
125615
|
+
parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`];
|
|
125616
|
+
parsed.command = process.env.comspec || "cmd.exe";
|
|
125617
|
+
parsed.options.windowsVerbatimArguments = true;
|
|
125618
|
+
}
|
|
125619
|
+
return parsed;
|
|
125620
|
+
}
|
|
125621
|
+
function parse2(command, args, options) {
|
|
125622
|
+
if (args && !Array.isArray(args)) {
|
|
125623
|
+
options = args;
|
|
125624
|
+
args = null;
|
|
125625
|
+
}
|
|
125626
|
+
args = args ? args.slice(0) : [];
|
|
125627
|
+
options = Object.assign({}, options);
|
|
125628
|
+
const parsed = {
|
|
125629
|
+
command,
|
|
125630
|
+
args,
|
|
125631
|
+
options,
|
|
125632
|
+
file: void 0,
|
|
125633
|
+
original: {
|
|
125634
|
+
command,
|
|
125635
|
+
args
|
|
125636
|
+
}
|
|
125637
|
+
};
|
|
125638
|
+
return options.shell ? parsed : parseNonShell(parsed);
|
|
125639
|
+
}
|
|
125640
|
+
module.exports = parse2;
|
|
125641
|
+
}
|
|
125642
|
+
});
|
|
125643
|
+
|
|
125644
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.js
|
|
125645
|
+
var require_enoent = __commonJS({
|
|
125646
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/lib/enoent.js"(exports, module) {
|
|
125647
|
+
"use strict";
|
|
125648
|
+
var isWin = process.platform === "win32";
|
|
125649
|
+
function notFoundError(original, syscall) {
|
|
125650
|
+
return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
|
|
125651
|
+
code: "ENOENT",
|
|
125652
|
+
errno: "ENOENT",
|
|
125653
|
+
syscall: `${syscall} ${original.command}`,
|
|
125654
|
+
path: original.command,
|
|
125655
|
+
spawnargs: original.args
|
|
125656
|
+
});
|
|
125657
|
+
}
|
|
125658
|
+
function hookChildProcess(cp, parsed) {
|
|
125659
|
+
if (!isWin) {
|
|
125660
|
+
return;
|
|
125661
|
+
}
|
|
125662
|
+
const originalEmit = cp.emit;
|
|
125663
|
+
cp.emit = function(name, arg1) {
|
|
125664
|
+
if (name === "exit") {
|
|
125665
|
+
const err = verifyENOENT(arg1, parsed);
|
|
125666
|
+
if (err) {
|
|
125667
|
+
return originalEmit.call(cp, "error", err);
|
|
125668
|
+
}
|
|
125669
|
+
}
|
|
125670
|
+
return originalEmit.apply(cp, arguments);
|
|
125671
|
+
};
|
|
125672
|
+
}
|
|
125673
|
+
function verifyENOENT(status, parsed) {
|
|
125674
|
+
if (isWin && status === 1 && !parsed.file) {
|
|
125675
|
+
return notFoundError(parsed.original, "spawn");
|
|
125676
|
+
}
|
|
125677
|
+
return null;
|
|
125678
|
+
}
|
|
125679
|
+
function verifyENOENTSync(status, parsed) {
|
|
125680
|
+
if (isWin && status === 1 && !parsed.file) {
|
|
125681
|
+
return notFoundError(parsed.original, "spawnSync");
|
|
125682
|
+
}
|
|
125683
|
+
return null;
|
|
125684
|
+
}
|
|
125685
|
+
module.exports = {
|
|
125686
|
+
hookChildProcess,
|
|
125687
|
+
verifyENOENT,
|
|
125688
|
+
verifyENOENTSync,
|
|
125689
|
+
notFoundError
|
|
125690
|
+
};
|
|
125691
|
+
}
|
|
125692
|
+
});
|
|
125693
|
+
|
|
125694
|
+
// node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js
|
|
125695
|
+
var require_cross_spawn = __commonJS({
|
|
125696
|
+
"node_modules/.pnpm/cross-spawn@7.0.6/node_modules/cross-spawn/index.js"(exports, module) {
|
|
125697
|
+
"use strict";
|
|
125698
|
+
var cp = __require("child_process");
|
|
125699
|
+
var parse2 = require_parse2();
|
|
125700
|
+
var enoent = require_enoent();
|
|
125701
|
+
function spawn4(command, args, options) {
|
|
125702
|
+
const parsed = parse2(command, args, options);
|
|
125703
|
+
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
|
|
125704
|
+
enoent.hookChildProcess(spawned, parsed);
|
|
125705
|
+
return spawned;
|
|
125706
|
+
}
|
|
125707
|
+
function spawnSync(command, args, options) {
|
|
125708
|
+
const parsed = parse2(command, args, options);
|
|
125709
|
+
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
|
|
125710
|
+
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
|
|
125711
|
+
return result;
|
|
125712
|
+
}
|
|
125713
|
+
module.exports = spawn4;
|
|
125714
|
+
module.exports.spawn = spawn4;
|
|
125715
|
+
module.exports.sync = spawnSync;
|
|
125716
|
+
module.exports._parse = parse2;
|
|
125717
|
+
module.exports._enoent = enoent;
|
|
125718
|
+
}
|
|
125719
|
+
});
|
|
125720
|
+
|
|
124131
125721
|
// node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
124132
125722
|
var Node, Queue;
|
|
124133
125723
|
var init_yocto_queue = __esm({
|
|
@@ -126608,15 +128198,15 @@ var init_schemas = __esm({
|
|
|
126608
128198
|
});
|
|
126609
128199
|
|
|
126610
128200
|
// packages/core/dist/services/llm/cli-provider.js
|
|
126611
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
126612
128201
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
126613
128202
|
import { join as join8 } from "node:path";
|
|
126614
128203
|
import { tmpdir } from "node:os";
|
|
126615
128204
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
126616
|
-
var BaseCLIProvider, ClaudeCodeProvider;
|
|
128205
|
+
var import_cross_spawn, BaseCLIProvider, ClaudeCodeProvider;
|
|
126617
128206
|
var init_cli_provider = __esm({
|
|
126618
128207
|
"packages/core/dist/services/llm/cli-provider.js"() {
|
|
126619
128208
|
"use strict";
|
|
128209
|
+
import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
126620
128210
|
init_logger();
|
|
126621
128211
|
init_p_limit();
|
|
126622
128212
|
init_analysis_registry();
|
|
@@ -126719,7 +128309,7 @@ var init_cli_provider = __esm({
|
|
|
126719
128309
|
...opts?.extraArgs ?? []
|
|
126720
128310
|
];
|
|
126721
128311
|
return new Promise((resolve7, reject) => {
|
|
126722
|
-
const child =
|
|
128312
|
+
const child = (0, import_cross_spawn.default)(this.binaryName, args, {
|
|
126723
128313
|
env: this.getCleanEnv(),
|
|
126724
128314
|
stdio: ["pipe", "pipe", "pipe"],
|
|
126725
128315
|
...this._repoPath ? { cwd: this._repoPath } : {}
|
|
@@ -140671,7 +142261,7 @@ import fs2 from "node:fs";
|
|
|
140671
142261
|
import os2 from "node:os";
|
|
140672
142262
|
import path3 from "node:path";
|
|
140673
142263
|
var TRUECOURSE_DIR = ".truecourse";
|
|
140674
|
-
var GITIGNORE_CONTENTS = "analyses/\
|
|
142264
|
+
var GITIGNORE_CONTENTS = "analyses/\nhistory.json\ndiff.json\nui-state.json\nlogs/\n.analyze.lock\n";
|
|
140675
142265
|
function getGlobalDir() {
|
|
140676
142266
|
return process.env.TRUECOURSE_HOME || path3.join(os2.homedir(), TRUECOURSE_DIR);
|
|
140677
142267
|
}
|
|
@@ -146706,7 +148296,38 @@ function splitIntoBatches(tier, rules, content, fileCount, functionCount, filePa
|
|
|
146706
148296
|
}
|
|
146707
148297
|
return batches;
|
|
146708
148298
|
}
|
|
148299
|
+
function translateContextRangeError(err, fileContents) {
|
|
148300
|
+
if (!(err instanceof RangeError) || !err.message.includes("Invalid string length")) {
|
|
148301
|
+
throw err;
|
|
148302
|
+
}
|
|
148303
|
+
const sized = [...fileContents.entries()].map(([filePath, fc]) => ({
|
|
148304
|
+
filePath,
|
|
148305
|
+
sizeKb: Math.round(fc.content.length / 1024),
|
|
148306
|
+
lineCount: fc.lineCount,
|
|
148307
|
+
// long single lines are a strong minified-bundle signal
|
|
148308
|
+
maxLineLength: fc.lineCount > 0 ? Math.round(fc.content.length / fc.lineCount) : 0
|
|
148309
|
+
})).sort((a, b) => b.sizeKb - a.sizeKb).slice(0, 5);
|
|
148310
|
+
const list = sized.map((f2) => {
|
|
148311
|
+
const minHint = f2.maxLineLength > 5e3 ? " [likely minified]" : "";
|
|
148312
|
+
return ` - ${f2.filePath} (${f2.sizeKb} KB, ${f2.lineCount} lines)${minHint}`;
|
|
148313
|
+
}).join("\n");
|
|
148314
|
+
const suggestions = sized.slice(0, 3).map((f2) => ` ${f2.filePath}`).join("\n");
|
|
148315
|
+
throw new Error(`LLM context exceeded V8's max string length (~512 MB) while preparing rule batches. This is almost always caused by minified bundles, vendored libraries, or generated files.
|
|
148316
|
+
|
|
148317
|
+
Largest files in scope:
|
|
148318
|
+
${list}
|
|
148319
|
+
|
|
148320
|
+
Add the offending paths to \`.truecourseignore\` at the repo root, e.g.:
|
|
148321
|
+
${suggestions}`);
|
|
148322
|
+
}
|
|
146709
148323
|
function estimateContext(rules, fileAnalyses, fileContents, options) {
|
|
148324
|
+
try {
|
|
148325
|
+
return estimateContextInner(rules, fileAnalyses, fileContents, options);
|
|
148326
|
+
} catch (err) {
|
|
148327
|
+
translateContextRangeError(err, fileContents);
|
|
148328
|
+
}
|
|
148329
|
+
}
|
|
148330
|
+
function estimateContextInner(rules, fileAnalyses, fileContents, options) {
|
|
146710
148331
|
const grouped = groupRulesByContext(rules);
|
|
146711
148332
|
const tiers = [];
|
|
146712
148333
|
const useFilePaths = options?.useFilePaths ?? false;
|
|
@@ -146765,6 +148386,13 @@ function estimateContext(rules, fileAnalyses, fileContents, options) {
|
|
|
146765
148386
|
};
|
|
146766
148387
|
}
|
|
146767
148388
|
function routeContext(rules, fileAnalyses, fileContents) {
|
|
148389
|
+
try {
|
|
148390
|
+
return routeContextInner(rules, fileAnalyses, fileContents);
|
|
148391
|
+
} catch (err) {
|
|
148392
|
+
translateContextRangeError(err, fileContents);
|
|
148393
|
+
}
|
|
148394
|
+
}
|
|
148395
|
+
function routeContextInner(rules, fileAnalyses, fileContents) {
|
|
146768
148396
|
const grouped = groupRulesByContext(rules);
|
|
146769
148397
|
const batches = [];
|
|
146770
148398
|
const faByPath = /* @__PURE__ */ new Map();
|
|
@@ -147354,24 +148982,39 @@ async function runViolationPipeline(input) {
|
|
|
147354
148982
|
tracker?.start(stepKey);
|
|
147355
148983
|
if (domain === "architecture") {
|
|
147356
148984
|
tracker?.detail(stepKey, "Service checks...");
|
|
148985
|
+
await new Promise((r) => setImmediate(r));
|
|
148986
|
+
throwIfAborted(signal);
|
|
147357
148987
|
serviceViolationResults.push(...runDeterministicServiceChecks(result, domainRules));
|
|
147358
148988
|
tracker?.detail(stepKey, "Module checks...");
|
|
148989
|
+
await new Promise((r) => setImmediate(r));
|
|
148990
|
+
throwIfAborted(signal);
|
|
147359
148991
|
moduleViolationResults.push(...runDeterministicModuleChecks(result, domainRules));
|
|
147360
148992
|
tracker?.detail(stepKey, "Method checks...");
|
|
148993
|
+
await new Promise((r) => setImmediate(r));
|
|
148994
|
+
throwIfAborted(signal);
|
|
147361
148995
|
methodViolationResults.push(...runDeterministicMethodChecks(result, domainRules));
|
|
147362
148996
|
tracker?.detail(stepKey, "Deterministic checks done");
|
|
147363
148997
|
}
|
|
147364
148998
|
}
|
|
147365
148999
|
const allCodeViolations = [];
|
|
147366
149000
|
if (enabledCodeRules.length > 0 && filesToScan.length > 0) {
|
|
149001
|
+
const activeCodeDomains = [];
|
|
147367
149002
|
for (const domain of DOMAIN_ORDER) {
|
|
147368
149003
|
if (domain === "architecture")
|
|
147369
149004
|
continue;
|
|
147370
149005
|
const domainRules = enabledDeterministic.filter((r) => (r.domain ?? "").startsWith(domain));
|
|
147371
|
-
if (domainRules.length > 0)
|
|
149006
|
+
if (domainRules.length > 0) {
|
|
147372
149007
|
tracker?.start(`${domain}`);
|
|
149008
|
+
activeCodeDomains.push(domain);
|
|
149009
|
+
}
|
|
147373
149010
|
}
|
|
147374
149011
|
await new Promise((r) => setImmediate(r));
|
|
149012
|
+
const totalFiles = filesToScan.length;
|
|
149013
|
+
let processed = 0;
|
|
149014
|
+
const SPINNER_YIELD_MS = 25;
|
|
149015
|
+
const DETAIL_UPDATE_MS = 100;
|
|
149016
|
+
let lastYieldMs = Date.now();
|
|
149017
|
+
let lastDetailMs = lastYieldMs;
|
|
147375
149018
|
for (const { filePath, resolve: resolve7 } of filesToScan) {
|
|
147376
149019
|
try {
|
|
147377
149020
|
const lang = detectLanguage(filePath);
|
|
@@ -147387,6 +149030,23 @@ async function runViolationPipeline(input) {
|
|
|
147387
149030
|
allCodeViolations.push(...codeRuleViolations);
|
|
147388
149031
|
} catch {
|
|
147389
149032
|
}
|
|
149033
|
+
processed++;
|
|
149034
|
+
if (signal?.aborted)
|
|
149035
|
+
throw new DOMException("Analysis cancelled", "AbortError");
|
|
149036
|
+
const now2 = Date.now();
|
|
149037
|
+
const isLast = processed === totalFiles;
|
|
149038
|
+
if (isLast || now2 - lastDetailMs >= DETAIL_UPDATE_MS) {
|
|
149039
|
+
const detail = `${processed}/${totalFiles} files`;
|
|
149040
|
+
for (const domain of activeCodeDomains)
|
|
149041
|
+
tracker?.detail(domain, detail);
|
|
149042
|
+
lastDetailMs = now2;
|
|
149043
|
+
}
|
|
149044
|
+
if (isLast || now2 - lastYieldMs >= SPINNER_YIELD_MS) {
|
|
149045
|
+
await new Promise((r) => setImmediate(r));
|
|
149046
|
+
lastYieldMs = Date.now();
|
|
149047
|
+
if (signal?.aborted)
|
|
149048
|
+
throw new DOMException("Analysis cancelled", "AbortError");
|
|
149049
|
+
}
|
|
147390
149050
|
}
|
|
147391
149051
|
}
|
|
147392
149052
|
log.info(`[Pipeline] Code scan: ${allCodeViolations.length} violations from ${filesToScan.length} files (${enabledCodeRules.length} det rules, ${enabledLlmCodeRules.length} LLM rules)`);
|