truecourse 0.7.0-next.4 → 0.7.0-next.5
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 +15 -3
- package/cli.mjs +1393 -682
- package/package.json +1 -1
- package/public/assets/{index-rfhWLMD4.js → index-CI8n7IHP.js} +231 -226
- package/public/index.html +1 -1
- package/roslyn-host/Humanizer.dll +0 -0
- package/roslyn-host/Microsoft.Bcl.AsyncInterfaces.dll +0 -0
- package/roslyn-host/Microsoft.Build.Locator.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.dll +0 -0
- package/roslyn-host/Microsoft.Extensions.DependencyInjection.Abstractions.dll +0 -0
- package/roslyn-host/Microsoft.Extensions.Logging.Abstractions.dll +0 -0
- package/roslyn-host/Newtonsoft.Json.dll +0 -0
- package/roslyn-host/System.Composition.AttributedModel.dll +0 -0
- package/roslyn-host/System.Composition.Convention.dll +0 -0
- package/roslyn-host/System.Composition.Hosting.dll +0 -0
- package/roslyn-host/System.Composition.Runtime.dll +0 -0
- package/roslyn-host/System.Composition.TypedParts.dll +0 -0
- package/roslyn-host/System.IO.Pipelines.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.deps.json +545 -0
- package/roslyn-host/csharp-roslyn-host.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.dll.config +623 -0
- package/roslyn-host/csharp-roslyn-host.pdb +0 -0
- package/roslyn-host/csharp-roslyn-host.runtimeconfig.json +16 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/server.mjs +1018 -344
package/server.mjs
CHANGED
|
@@ -28008,7 +28008,12 @@ var init_result = __esm({
|
|
|
28008
28008
|
durationMs: external_exports.number().nonnegative(),
|
|
28009
28009
|
/** Present on `fail` / `error`. */
|
|
28010
28010
|
failure: GuardFailureDetailSchema.optional(),
|
|
28011
|
-
/**
|
|
28011
|
+
/**
|
|
28012
|
+
* Repo-relative pointer into `evidence/`; present on every EXECUTED outcome —
|
|
28013
|
+
* `pass` (the run transcript proving what ran) as well as `fail` / `error`.
|
|
28014
|
+
* Absent on non-executed `stale` / `orphaned`, and on pre-2026-07-08 runs whose
|
|
28015
|
+
* passes were never given a bundle (optional keeps those parsing + rendering).
|
|
28016
|
+
*/
|
|
28012
28017
|
evidencePath: external_exports.string().optional(),
|
|
28013
28018
|
/**
|
|
28014
28019
|
* Present when the bound section moved: the section kept its text but now
|
|
@@ -44192,8 +44197,8 @@ function getMaxParameters(filePath) {
|
|
|
44192
44197
|
}
|
|
44193
44198
|
return 5;
|
|
44194
44199
|
}
|
|
44195
|
-
function isBootstrapEntry(
|
|
44196
|
-
return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(
|
|
44200
|
+
function isBootstrapEntry(functionName2, filePath) {
|
|
44201
|
+
return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(functionName2) && c.bootstrap.filePattern.test(filePath));
|
|
44197
44202
|
}
|
|
44198
44203
|
function getLanguageConfig(language) {
|
|
44199
44204
|
const config2 = LANGUAGE_CONFIGS.find((c) => c.name === language);
|
|
@@ -48928,9 +48933,6 @@ function buildCSharpSymbolIndex(files, rootPath) {
|
|
|
48928
48933
|
}
|
|
48929
48934
|
function contributeCSharpEdges(files, rootPath) {
|
|
48930
48935
|
const index = buildCSharpSymbolIndex(files, rootPath);
|
|
48931
|
-
if (index.stats.ambiguousRefs > 0) {
|
|
48932
|
-
console.warn(`[TrueCourse] C# symbol index: ${index.stats.ambiguousRefs} ambiguous type reference(s) skipped (same type name visible from multiple namespaces)`);
|
|
48933
|
-
}
|
|
48934
48936
|
return index.edges;
|
|
48935
48937
|
}
|
|
48936
48938
|
var TYPE_DECL_KINDS;
|
|
@@ -54259,12 +54261,9 @@ var init_lsp_client = __esm({
|
|
|
54259
54261
|
this.process.stdout.on("data", (chunk3) => this.onData(chunk3));
|
|
54260
54262
|
this.process.stderr.on("data", (chunk3) => {
|
|
54261
54263
|
});
|
|
54262
|
-
this.process.on("error", (err) =>
|
|
54263
|
-
|
|
54264
|
-
|
|
54265
|
-
}
|
|
54266
|
-
this.pendingRequests.clear();
|
|
54267
|
-
});
|
|
54264
|
+
this.process.on("error", (err) => this.rejectAllPending(err));
|
|
54265
|
+
this.process.stdin.on("error", (err) => this.rejectAllPending(new Error(`${this.config.name} LSP stdin error: ${err.message}`)));
|
|
54266
|
+
this.process.on("exit", (code, signal) => this.rejectAllPending(new Error(`${this.config.name} LSP server exited (${signal ?? `code ${code}`}) before responding`)));
|
|
54268
54267
|
const initResult = await this.sendRequest("initialize", {
|
|
54269
54268
|
processId: process.pid,
|
|
54270
54269
|
capabilities: {
|
|
@@ -54443,17 +54442,33 @@ var init_lsp_client = __esm({
|
|
|
54443
54442
|
// -------------------------------------------------------------------------
|
|
54444
54443
|
// Internal: JSON-RPC transport
|
|
54445
54444
|
// -------------------------------------------------------------------------
|
|
54445
|
+
/** Reject and clear every in-flight request — used when the server dies. */
|
|
54446
|
+
rejectAllPending(err) {
|
|
54447
|
+
if (this.pendingRequests.size === 0)
|
|
54448
|
+
return;
|
|
54449
|
+
for (const pending of this.pendingRequests.values())
|
|
54450
|
+
pending.reject(err);
|
|
54451
|
+
this.pendingRequests.clear();
|
|
54452
|
+
}
|
|
54446
54453
|
sendRequest(method, params) {
|
|
54447
54454
|
return new Promise((resolve11, reject) => {
|
|
54448
54455
|
const id = ++this.requestId;
|
|
54449
54456
|
this.pendingRequests.set(id, { resolve: resolve11, reject });
|
|
54450
54457
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
54451
|
-
|
|
54458
|
+
try {
|
|
54459
|
+
this.process.stdin.write(encodeMessage(msg));
|
|
54460
|
+
} catch (err) {
|
|
54461
|
+
this.pendingRequests.delete(id);
|
|
54462
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
54463
|
+
}
|
|
54452
54464
|
});
|
|
54453
54465
|
}
|
|
54454
54466
|
sendNotification(method, params) {
|
|
54455
54467
|
const msg = { jsonrpc: "2.0", method, params };
|
|
54456
|
-
|
|
54468
|
+
try {
|
|
54469
|
+
this.process.stdin.write(encodeMessage(msg));
|
|
54470
|
+
} catch {
|
|
54471
|
+
}
|
|
54457
54472
|
}
|
|
54458
54473
|
onData(chunk3) {
|
|
54459
54474
|
this.buffer = Buffer.concat([this.buffer, chunk3]);
|
|
@@ -64938,7 +64953,8 @@ var init_deterministic4 = __esm({
|
|
|
64938
64953
|
description: "Function parameter is never referenced in the function body \u2014 remove it or prefix with `_`.",
|
|
64939
64954
|
enabled: true,
|
|
64940
64955
|
severity: "low",
|
|
64941
|
-
type: "deterministic"
|
|
64956
|
+
type: "deterministic",
|
|
64957
|
+
engine: "roslyn-host"
|
|
64942
64958
|
},
|
|
64943
64959
|
{
|
|
64944
64960
|
key: "code-quality/deterministic/hardcoded-url",
|
|
@@ -76222,6 +76238,94 @@ var init_production_debug_enabled = __esm({
|
|
|
76222
76238
|
});
|
|
76223
76239
|
|
|
76224
76240
|
// packages/analyzer/dist/rules/security/visitors/javascript/insecure-random.js
|
|
76241
|
+
function isSecuritySensitiveName(name) {
|
|
76242
|
+
const lower = name.toLowerCase();
|
|
76243
|
+
return SECURITY_KEYWORDS.some((kw) => lower.includes(kw));
|
|
76244
|
+
}
|
|
76245
|
+
function securityContextName(callNode) {
|
|
76246
|
+
let parent = callNode.parent;
|
|
76247
|
+
while (parent) {
|
|
76248
|
+
switch (parent.type) {
|
|
76249
|
+
case "variable_declarator":
|
|
76250
|
+
return leafName(parent.childForFieldName("name"));
|
|
76251
|
+
case "assignment_expression":
|
|
76252
|
+
case "augmented_assignment_expression":
|
|
76253
|
+
return leafName(parent.childForFieldName("left"));
|
|
76254
|
+
case "pair":
|
|
76255
|
+
return leafName(parent.childForFieldName("key"));
|
|
76256
|
+
case "public_field_definition":
|
|
76257
|
+
case "field_definition":
|
|
76258
|
+
return leafName(parent.childForFieldName("property") ?? parent.childForFieldName("name"));
|
|
76259
|
+
// The value is returned from the enclosing function (e.g.
|
|
76260
|
+
// `function generateToken() { return Math.random()... }`) — the
|
|
76261
|
+
// function name is the security context.
|
|
76262
|
+
case "return_statement":
|
|
76263
|
+
return enclosingFunctionName(parent);
|
|
76264
|
+
// Expression-bodied arrow returns the value directly, e.g.
|
|
76265
|
+
// `const genToken = () => Math.random()...`.
|
|
76266
|
+
case "arrow_function":
|
|
76267
|
+
return functionName(parent);
|
|
76268
|
+
// Structural boundaries: the random value is discarded or otherwise
|
|
76269
|
+
// not bound to a name.
|
|
76270
|
+
case "expression_statement":
|
|
76271
|
+
case "statement_block":
|
|
76272
|
+
case "if_statement":
|
|
76273
|
+
case "for_statement":
|
|
76274
|
+
case "for_in_statement":
|
|
76275
|
+
case "while_statement":
|
|
76276
|
+
case "do_statement":
|
|
76277
|
+
case "switch_statement":
|
|
76278
|
+
case "function_declaration":
|
|
76279
|
+
case "function_expression":
|
|
76280
|
+
case "method_definition":
|
|
76281
|
+
return null;
|
|
76282
|
+
}
|
|
76283
|
+
parent = parent.parent;
|
|
76284
|
+
}
|
|
76285
|
+
return null;
|
|
76286
|
+
}
|
|
76287
|
+
function enclosingFunctionName(node2) {
|
|
76288
|
+
let p = node2.parent;
|
|
76289
|
+
while (p) {
|
|
76290
|
+
if (p.type === "function_declaration" || p.type === "generator_function_declaration" || p.type === "method_definition" || p.type === "arrow_function" || p.type === "function_expression") {
|
|
76291
|
+
return functionName(p);
|
|
76292
|
+
}
|
|
76293
|
+
p = p.parent;
|
|
76294
|
+
}
|
|
76295
|
+
return null;
|
|
76296
|
+
}
|
|
76297
|
+
function functionName(fn) {
|
|
76298
|
+
const nameField = fn.childForFieldName("name");
|
|
76299
|
+
if (nameField)
|
|
76300
|
+
return leafName(nameField);
|
|
76301
|
+
const p = fn.parent;
|
|
76302
|
+
if (!p)
|
|
76303
|
+
return null;
|
|
76304
|
+
if (p.type === "variable_declarator")
|
|
76305
|
+
return leafName(p.childForFieldName("name"));
|
|
76306
|
+
if (p.type === "pair")
|
|
76307
|
+
return leafName(p.childForFieldName("key"));
|
|
76308
|
+
if (p.type === "assignment_expression")
|
|
76309
|
+
return leafName(p.childForFieldName("left"));
|
|
76310
|
+
if (p.type === "public_field_definition" || p.type === "field_definition") {
|
|
76311
|
+
return leafName(p.childForFieldName("property") ?? p.childForFieldName("name"));
|
|
76312
|
+
}
|
|
76313
|
+
return null;
|
|
76314
|
+
}
|
|
76315
|
+
function leafName(node2) {
|
|
76316
|
+
if (!node2)
|
|
76317
|
+
return null;
|
|
76318
|
+
if (node2.type === "identifier" || node2.type === "property_identifier" || node2.type === "shorthand_property_identifier") {
|
|
76319
|
+
return node2.text;
|
|
76320
|
+
}
|
|
76321
|
+
if (node2.type === "member_expression") {
|
|
76322
|
+
return node2.childForFieldName("property")?.text ?? null;
|
|
76323
|
+
}
|
|
76324
|
+
if (node2.type === "string") {
|
|
76325
|
+
return node2.text.slice(1, -1);
|
|
76326
|
+
}
|
|
76327
|
+
return node2.text;
|
|
76328
|
+
}
|
|
76225
76329
|
function isFixtureOrTestFile(filePath) {
|
|
76226
76330
|
const lower = filePath.toLowerCase();
|
|
76227
76331
|
return /[\\/]seeds?[\\/]/.test(lower) || /[\\/](?:seed|seeds)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /\.seed\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /[\\/]__tests__[\\/]/.test(lower) || /\.(?:test|spec)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower);
|
|
@@ -76252,19 +76356,9 @@ var init_insecure_random = __esm({
|
|
|
76252
76356
|
if (/\.length\b/.test(parentText))
|
|
76253
76357
|
return null;
|
|
76254
76358
|
}
|
|
76255
|
-
|
|
76256
|
-
|
|
76257
|
-
|
|
76258
|
-
if (SECURITY_KEYWORDS.some((kw) => parentText.includes(kw))) {
|
|
76259
|
-
const immediateParent = node2.parent;
|
|
76260
|
-
if (immediateParent?.type === "binary_expression" && /\.length\b/.test(immediateParent.text)) {
|
|
76261
|
-
return null;
|
|
76262
|
-
}
|
|
76263
|
-
return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
|
|
76264
|
-
}
|
|
76265
|
-
if (parent.type === "expression_statement" || parent.type === "variable_declaration" || parent.type === "assignment_expression" || parent.type === "lexical_declaration")
|
|
76266
|
-
break;
|
|
76267
|
-
parent = parent.parent;
|
|
76359
|
+
const contextName = securityContextName(node2);
|
|
76360
|
+
if (contextName && isSecuritySensitiveName(contextName)) {
|
|
76361
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
|
|
76268
76362
|
}
|
|
76269
76363
|
}
|
|
76270
76364
|
}
|
|
@@ -76693,6 +76787,11 @@ var init_unverified_cross_origin_message = __esm({
|
|
|
76693
76787
|
}
|
|
76694
76788
|
if (methodName3 !== "addEventListener")
|
|
76695
76789
|
return null;
|
|
76790
|
+
if (fn.type === "member_expression") {
|
|
76791
|
+
const receiver = fn.childForFieldName("object")?.text ?? "";
|
|
76792
|
+
if (receiver !== "window" && receiver !== "self" && receiver !== "globalThis")
|
|
76793
|
+
return null;
|
|
76794
|
+
}
|
|
76696
76795
|
const args = node2.childForFieldName("arguments");
|
|
76697
76796
|
if (!args)
|
|
76698
76797
|
return null;
|
|
@@ -77175,7 +77274,11 @@ var init_timing_attack_comparison = __esm({
|
|
|
77175
77274
|
return null;
|
|
77176
77275
|
const leftLeaf = leafIdentifier(left);
|
|
77177
77276
|
const rightLeaf = leafIdentifier(right);
|
|
77178
|
-
|
|
77277
|
+
const leftIsLiteral = left.type === "string" || left.type === "template_string";
|
|
77278
|
+
const rightIsLiteral = right.type === "string" || right.type === "template_string";
|
|
77279
|
+
const leftMatches = !leftIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(leftLeaf);
|
|
77280
|
+
const rightMatches = !rightIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(rightLeaf);
|
|
77281
|
+
if (leftMatches || rightMatches) {
|
|
77179
77282
|
if (sourceCode.includes("timingSafeEqual"))
|
|
77180
77283
|
return null;
|
|
77181
77284
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Timing attack via string comparison", `Using ${operator.text} to compare what may be a secret/token. This is vulnerable to timing attacks.`, sourceCode, "Use crypto.timingSafeEqual() for comparing secrets and tokens.");
|
|
@@ -77762,6 +77865,14 @@ var init_mixed_http_methods = __esm({
|
|
|
77762
77865
|
});
|
|
77763
77866
|
|
|
77764
77867
|
// packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js
|
|
77868
|
+
function isOwnedProcessPid(node2) {
|
|
77869
|
+
if (node2.type !== "member_expression")
|
|
77870
|
+
return false;
|
|
77871
|
+
if (node2.childForFieldName("property")?.text !== "pid")
|
|
77872
|
+
return false;
|
|
77873
|
+
const obj = node2.childForFieldName("object");
|
|
77874
|
+
return obj?.type === "member_expression" && obj.childForFieldName("property")?.text === "process";
|
|
77875
|
+
}
|
|
77765
77876
|
var processSignalingVisitor;
|
|
77766
77877
|
var init_process_signaling = __esm({
|
|
77767
77878
|
"packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js"() {
|
|
@@ -77789,6 +77900,8 @@ var init_process_signaling = __esm({
|
|
|
77789
77900
|
const pidArg = args.namedChildren[0];
|
|
77790
77901
|
if (!pidArg)
|
|
77791
77902
|
return null;
|
|
77903
|
+
if (isOwnedProcessPid(pidArg))
|
|
77904
|
+
return null;
|
|
77792
77905
|
if (pidArg.type !== "number") {
|
|
77793
77906
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Sending signals to arbitrary processes", "process.kill() called with a non-literal PID. If the PID is user-controlled, this enables process manipulation.", sourceCode, "Validate and sanitize the PID before passing it to process.kill().");
|
|
77794
77907
|
}
|
|
@@ -79142,8 +79255,8 @@ var init_sql_injection2 = __esm({
|
|
|
79142
79255
|
if (!firstArg)
|
|
79143
79256
|
return null;
|
|
79144
79257
|
if (firstArg.type === "string" && firstArg.text.startsWith("f")) {
|
|
79145
|
-
const
|
|
79146
|
-
if (
|
|
79258
|
+
const hasInterpolation = firstArg.namedChildren.some((c) => c.type === "interpolation");
|
|
79259
|
+
if (hasInterpolation) {
|
|
79147
79260
|
const queryText = firstArg.text;
|
|
79148
79261
|
if (/:[a-z_]\w*|%s|%\(\w+\)s/.test(queryText))
|
|
79149
79262
|
return null;
|
|
@@ -79653,17 +79766,17 @@ var init_eval_usage2 = __esm({
|
|
|
79653
79766
|
funcName = attr.text;
|
|
79654
79767
|
const receiver = fn.childForFieldName("object");
|
|
79655
79768
|
if (receiver) {
|
|
79656
|
-
let
|
|
79769
|
+
let leafName3 = null;
|
|
79657
79770
|
if (receiver.type === "identifier") {
|
|
79658
|
-
|
|
79771
|
+
leafName3 = receiver.text;
|
|
79659
79772
|
} else if (receiver.type === "attribute") {
|
|
79660
79773
|
const attrChild = receiver.childForFieldName("attribute");
|
|
79661
79774
|
if (attrChild) {
|
|
79662
|
-
|
|
79775
|
+
leafName3 = attrChild.text.replace(/^_+/, "");
|
|
79663
79776
|
}
|
|
79664
79777
|
}
|
|
79665
|
-
if (
|
|
79666
|
-
const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[
|
|
79778
|
+
if (leafName3 && SAFE_METHOD_RECEIVERS.has(leafName3)) {
|
|
79779
|
+
const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[leafName3];
|
|
79667
79780
|
if (expectedSources) {
|
|
79668
79781
|
const sources = getPythonImportSources(node2);
|
|
79669
79782
|
for (const src of sources) {
|
|
@@ -83635,6 +83748,12 @@ var init_exclusions = __esm({
|
|
|
83635
83748
|
/^https?:\/\/(?!hooks\.slack\.com|[a-z0-9]+\.webhook\.office\.com)/,
|
|
83636
83749
|
// Docker / container image references
|
|
83637
83750
|
/^(?:docker|ghcr|quay|gcr|ecr)\.io\//,
|
|
83751
|
+
// PostHog public project API key (phc_…) — intentionally client-exposed,
|
|
83752
|
+
// embedded in frontend code to send analytics events. Its 40+ char base62
|
|
83753
|
+
// body collides with the generic Cohere-token shape, but it is not a
|
|
83754
|
+
// private credential. Private PostHog keys use the phx_/phs_ prefixes and
|
|
83755
|
+
// are deliberately NOT allowlisted here.
|
|
83756
|
+
/^phc_[A-Za-z0-9]{30,}$/,
|
|
83638
83757
|
// Common config patterns that aren't secrets
|
|
83639
83758
|
/^\*+$/,
|
|
83640
83759
|
// All asterisks (masked values)
|
|
@@ -85450,10 +85569,33 @@ var init_secret_scanner = __esm({
|
|
|
85450
85569
|
});
|
|
85451
85570
|
|
|
85452
85571
|
// packages/analyzer/dist/rules/security/visitors/universal.js
|
|
85572
|
+
function hasPasswordToken(text4) {
|
|
85573
|
+
const lower = text4.toLowerCase();
|
|
85574
|
+
return lower.includes("password") || lower.includes("passwd");
|
|
85575
|
+
}
|
|
85576
|
+
function isPasswordHashingContext(node2) {
|
|
85577
|
+
let stmt = node2.parent;
|
|
85578
|
+
while (stmt && !STATEMENT_NODE_TYPES2.has(stmt.type) && !FUNCTION_NODE_TYPES3.has(stmt.type)) {
|
|
85579
|
+
stmt = stmt.parent;
|
|
85580
|
+
}
|
|
85581
|
+
if (stmt && STATEMENT_NODE_TYPES2.has(stmt.type) && hasPasswordToken(stmt.text))
|
|
85582
|
+
return true;
|
|
85583
|
+
let fnNode = node2.parent;
|
|
85584
|
+
while (fnNode && !FUNCTION_NODE_TYPES3.has(fnNode.type))
|
|
85585
|
+
fnNode = fnNode.parent;
|
|
85586
|
+
if (fnNode) {
|
|
85587
|
+
const nameNode = fnNode.childForFieldName("name");
|
|
85588
|
+
const paramsNode = fnNode.childForFieldName("parameters") ?? fnNode.childForFieldName("parameter_list");
|
|
85589
|
+
const signature = `${nameNode?.text ?? ""} ${paramsNode?.text ?? ""}`;
|
|
85590
|
+
if (hasPasswordToken(signature))
|
|
85591
|
+
return true;
|
|
85592
|
+
}
|
|
85593
|
+
return false;
|
|
85594
|
+
}
|
|
85453
85595
|
function stripCommentMarkers(text4) {
|
|
85454
85596
|
return text4.replace(/^\/{2,3}\s?/, "").replace(/^\/\*\s?/, "").replace(/\s?\*\/$/, "").replace(/^\s*\*\s?/gm, "").replace(/^#\s?/gm, "").trim();
|
|
85455
85597
|
}
|
|
85456
|
-
var STRING_NODE_TYPES, 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, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
|
|
85598
|
+
var STRING_NODE_TYPES, 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, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, STATEMENT_NODE_TYPES2, FUNCTION_NODE_TYPES3, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
|
|
85457
85599
|
var init_universal = __esm({
|
|
85458
85600
|
"packages/analyzer/dist/rules/security/visitors/universal.js"() {
|
|
85459
85601
|
"use strict";
|
|
@@ -86633,6 +86775,29 @@ var init_universal = __esm({
|
|
|
86633
86775
|
"sha256",
|
|
86634
86776
|
"sha512"
|
|
86635
86777
|
]);
|
|
86778
|
+
STATEMENT_NODE_TYPES2 = /* @__PURE__ */ new Set([
|
|
86779
|
+
"expression_statement",
|
|
86780
|
+
"return_statement",
|
|
86781
|
+
"lexical_declaration",
|
|
86782
|
+
"variable_declaration",
|
|
86783
|
+
"variable_declarator",
|
|
86784
|
+
"local_declaration_statement",
|
|
86785
|
+
"assignment",
|
|
86786
|
+
"augmented_assignment",
|
|
86787
|
+
"assignment_expression",
|
|
86788
|
+
"augmented_assignment_expression"
|
|
86789
|
+
]);
|
|
86790
|
+
FUNCTION_NODE_TYPES3 = /* @__PURE__ */ new Set([
|
|
86791
|
+
"function_declaration",
|
|
86792
|
+
"function_expression",
|
|
86793
|
+
"arrow_function",
|
|
86794
|
+
"generator_function_declaration",
|
|
86795
|
+
"method_definition",
|
|
86796
|
+
"function_definition",
|
|
86797
|
+
"method_declaration",
|
|
86798
|
+
"local_function_statement",
|
|
86799
|
+
"constructor_declaration"
|
|
86800
|
+
]);
|
|
86636
86801
|
unpredictableSaltMissingVisitor = {
|
|
86637
86802
|
ruleKey: "security/deterministic/unpredictable-salt-missing",
|
|
86638
86803
|
nodeTypes: ["call_expression", "call", "invocation_expression"],
|
|
@@ -86650,21 +86815,12 @@ var init_universal = __esm({
|
|
|
86650
86815
|
}
|
|
86651
86816
|
if (!HASH_FUNCTIONS_NEEDING_SALT.has(methodName3))
|
|
86652
86817
|
return null;
|
|
86653
|
-
|
|
86654
|
-
|
|
86655
|
-
|
|
86656
|
-
|
|
86657
|
-
|
|
86658
|
-
|
|
86659
|
-
return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
|
|
86660
|
-
}
|
|
86661
|
-
return null;
|
|
86662
|
-
}
|
|
86663
|
-
if (parent.type === "function_declaration" || parent.type === "function_definition" || parent.type === "method_definition" || parent.type === "program" || parent.type === "method_declaration" || parent.type === "compilation_unit")
|
|
86664
|
-
break;
|
|
86665
|
-
parent = parent.parent;
|
|
86666
|
-
}
|
|
86667
|
-
return null;
|
|
86818
|
+
const args = node2.childForFieldName("arguments");
|
|
86819
|
+
if (!args || args.namedChildren.length >= 2)
|
|
86820
|
+
return null;
|
|
86821
|
+
if (!isPasswordHashingContext(node2))
|
|
86822
|
+
return null;
|
|
86823
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
|
|
86668
86824
|
}
|
|
86669
86825
|
};
|
|
86670
86826
|
sensitiveFileVisitor = {
|
|
@@ -86757,7 +86913,7 @@ function isLiteralNode(n) {
|
|
|
86757
86913
|
}
|
|
86758
86914
|
return false;
|
|
86759
86915
|
}
|
|
86760
|
-
var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
|
|
86916
|
+
var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, SOCKET_IO_RECEIVERS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
|
|
86761
86917
|
var init_helpers = __esm({
|
|
86762
86918
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/_helpers.js"() {
|
|
86763
86919
|
"use strict";
|
|
@@ -86815,6 +86971,7 @@ var init_helpers = __esm({
|
|
|
86815
86971
|
"reduce",
|
|
86816
86972
|
"reduceRight"
|
|
86817
86973
|
]);
|
|
86974
|
+
SOCKET_IO_RECEIVERS = /* @__PURE__ */ new Set(["socket", "sockets"]);
|
|
86818
86975
|
KNOWN_ARG_ORDERS = [
|
|
86819
86976
|
{ fn: "startsWith", params: [["prefix", "str", "string", "start", "needle", "search"]] },
|
|
86820
86977
|
{ fn: "endsWith", params: [["suffix", "str", "string", "end", "needle", "search"]] },
|
|
@@ -87451,6 +87608,15 @@ var init_loss_of_precision = __esm({
|
|
|
87451
87608
|
const num = Number(text4);
|
|
87452
87609
|
if (!Number.isFinite(num))
|
|
87453
87610
|
return null;
|
|
87611
|
+
const parent = node2.parent;
|
|
87612
|
+
if (parent?.type === "arguments") {
|
|
87613
|
+
const call = parent.parent;
|
|
87614
|
+
if (call?.type === "call_expression") {
|
|
87615
|
+
const fn = call.childForFieldName("function");
|
|
87616
|
+
if (fn?.type === "identifier" && fn.text === "BigInt")
|
|
87617
|
+
return null;
|
|
87618
|
+
}
|
|
87619
|
+
}
|
|
87454
87620
|
if (!Number.isSafeInteger(num)) {
|
|
87455
87621
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Loss of precision", `\`${text4}\` exceeds Number.MAX_SAFE_INTEGER and will lose precision at runtime.`, sourceCode, `Use BigInt (\`${text4}n\`) or restructure to avoid large integer literals.`);
|
|
87456
87622
|
}
|
|
@@ -89722,6 +89888,17 @@ var init_useless_increment = __esm({
|
|
|
89722
89888
|
});
|
|
89723
89889
|
|
|
89724
89890
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js
|
|
89891
|
+
function receiverBaseName(obj) {
|
|
89892
|
+
if (!obj)
|
|
89893
|
+
return null;
|
|
89894
|
+
if (obj.type === "identifier")
|
|
89895
|
+
return obj.text.toLowerCase();
|
|
89896
|
+
if (obj.type === "member_expression") {
|
|
89897
|
+
const prop = obj.childForFieldName("property");
|
|
89898
|
+
return prop ? prop.text.toLowerCase() : null;
|
|
89899
|
+
}
|
|
89900
|
+
return null;
|
|
89901
|
+
}
|
|
89725
89902
|
var ignoredReturnValueVisitor;
|
|
89726
89903
|
var init_ignored_return_value = __esm({
|
|
89727
89904
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js"() {
|
|
@@ -89742,6 +89919,11 @@ var init_ignored_return_value = __esm({
|
|
|
89742
89919
|
const prop = fn.childForFieldName("property");
|
|
89743
89920
|
if (!prop || !PURE_ARRAY_METHODS.has(prop.text))
|
|
89744
89921
|
return null;
|
|
89922
|
+
if (prop.text === "join") {
|
|
89923
|
+
const base = receiverBaseName(fn.childForFieldName("object"));
|
|
89924
|
+
if (base && SOCKET_IO_RECEIVERS.has(base))
|
|
89925
|
+
return null;
|
|
89926
|
+
}
|
|
89745
89927
|
return makeViolation(this.ruleKey, expr, filePath, "high", "Ignored return value", `The return value of \`.${prop.text}()\` is ignored \u2014 this method does not mutate the array in place; the result must be used.`, sourceCode, `Assign the result: \`const result = arr.${prop.text}(...)\`.`);
|
|
89746
89928
|
}
|
|
89747
89929
|
};
|
|
@@ -90403,7 +90585,12 @@ var init_global_this_usage = __esm({
|
|
|
90403
90585
|
let current = node2.parent;
|
|
90404
90586
|
while (current) {
|
|
90405
90587
|
const t = current.type;
|
|
90406
|
-
if (t === "function_declaration" || t === "function" ||
|
|
90588
|
+
if (t === "function_declaration" || t === "function" || // Modern tree-sitter-javascript names a `function () {}` expression
|
|
90589
|
+
// `function_expression` (only the legacy grammar used bare `function`).
|
|
90590
|
+
// Without it, `this` inside any function-expression callback / IIFE /
|
|
90591
|
+
// prototype-assigned method walks up to program scope and is wrongly
|
|
90592
|
+
// reported as a global reference.
|
|
90593
|
+
t === "function_expression" || t === "generator_function" || t === "generator_function_declaration" || t === "method_definition" || t === "class_declaration" || t === "class") {
|
|
90407
90594
|
return null;
|
|
90408
90595
|
}
|
|
90409
90596
|
if (t === "arrow_function") {
|
|
@@ -93069,6 +93256,9 @@ var init_missing_await = __esm({
|
|
|
93069
93256
|
return null;
|
|
93070
93257
|
if (!isInsideAsyncFunction(node2))
|
|
93071
93258
|
return null;
|
|
93259
|
+
const declaredName = node2.childForFieldName("name");
|
|
93260
|
+
if (declaredName?.type === "identifier" && /Promise$/.test(declaredName.text))
|
|
93261
|
+
return null;
|
|
93072
93262
|
const isPromise = typeQuery.isPromiseLike(filePath, value.startPosition.row, value.startPosition.column, value.endPosition.row, value.endPosition.column);
|
|
93073
93263
|
if (!isPromise)
|
|
93074
93264
|
return null;
|
|
@@ -93645,25 +93835,26 @@ var init_unbound_method = __esm({
|
|
|
93645
93835
|
let classBody = node2.parent;
|
|
93646
93836
|
while (classBody && classBody.type !== "class_body")
|
|
93647
93837
|
classBody = classBody.parent;
|
|
93648
|
-
if (classBody)
|
|
93649
|
-
|
|
93650
|
-
|
|
93651
|
-
|
|
93652
|
-
|
|
93653
|
-
|
|
93654
|
-
|
|
93655
|
-
|
|
93656
|
-
|
|
93657
|
-
|
|
93658
|
-
|
|
93659
|
-
|
|
93660
|
-
|
|
93661
|
-
|
|
93662
|
-
|
|
93663
|
-
|
|
93664
|
-
}
|
|
93838
|
+
if (!classBody)
|
|
93839
|
+
return null;
|
|
93840
|
+
let resolvesToMethod = false;
|
|
93841
|
+
for (const member of classBody.namedChildren) {
|
|
93842
|
+
if (member.type === "method_definition") {
|
|
93843
|
+
const nameNode = member.childForFieldName("name");
|
|
93844
|
+
if (nameNode?.text !== methodName3)
|
|
93845
|
+
continue;
|
|
93846
|
+
const isAccessor = member.children.some((c) => (c.text === "get" || c.text === "set") && c.type !== "property_identifier");
|
|
93847
|
+
resolvesToMethod = !isAccessor;
|
|
93848
|
+
break;
|
|
93849
|
+
}
|
|
93850
|
+
if (member.type === "public_field_definition" || member.type === "field_definition") {
|
|
93851
|
+
const nameNode = member.children.find((c) => c.type === "property_identifier" || c.type === "private_property_identifier");
|
|
93852
|
+
if (nameNode?.text === methodName3)
|
|
93853
|
+
return null;
|
|
93665
93854
|
}
|
|
93666
93855
|
}
|
|
93856
|
+
if (!resolvesToMethod)
|
|
93857
|
+
return null;
|
|
93667
93858
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Unbound method reference", `\`this.${methodName3}\` is used as a value without binding \u2014 \`this\` will be lost when called as a callback.`, sourceCode, "Use `this.method.bind(this)`, an arrow wrapper `() => this.method()`, or declare as an arrow function property.");
|
|
93668
93859
|
}
|
|
93669
93860
|
};
|
|
@@ -93766,7 +93957,7 @@ var init_missing_error_boundary = __esm({
|
|
|
93766
93957
|
visit(node2, filePath, sourceCode) {
|
|
93767
93958
|
if (!sourceCode.includes("React") && !sourceCode.includes("react") && !sourceCode.includes("jsx"))
|
|
93768
93959
|
return null;
|
|
93769
|
-
const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode)
|
|
93960
|
+
const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode);
|
|
93770
93961
|
if (!hasAsyncData)
|
|
93771
93962
|
return null;
|
|
93772
93963
|
if (/\bErrorBoundary\b/.test(sourceCode))
|
|
@@ -106591,6 +106782,9 @@ var init_star_import = __esm({
|
|
|
106591
106782
|
if (sourceText === "zod" || sourceText.startsWith("@react-email/") || sourceText.startsWith("fumadocs-ui/") || sourceText.startsWith("fumadocs-core/")) {
|
|
106592
106783
|
return null;
|
|
106593
106784
|
}
|
|
106785
|
+
if (sourceText === "semver" || sourceText.startsWith("@sentry/")) {
|
|
106786
|
+
return null;
|
|
106787
|
+
}
|
|
106594
106788
|
if (sourceText.startsWith("./") || sourceText.startsWith("../"))
|
|
106595
106789
|
return null;
|
|
106596
106790
|
const nsImport = node2.namedChildren.find((c) => c.type === "import_clause")?.namedChildren.find((cc) => cc.type === "namespace_import");
|
|
@@ -108159,14 +108353,20 @@ var init_expression_complexity = __esm({
|
|
|
108159
108353
|
if (!expr)
|
|
108160
108354
|
return null;
|
|
108161
108355
|
let target = expr;
|
|
108162
|
-
while (target.type === "parenthesized_expression" && target.namedChildren[0]) {
|
|
108356
|
+
while ((target.type === "parenthesized_expression" || target.type === "as_expression" || target.type === "satisfies_expression" || target.type === "non_null_expression") && target.namedChildren[0]) {
|
|
108163
108357
|
target = target.namedChildren[0];
|
|
108164
108358
|
}
|
|
108359
|
+
if (target.type === "object" || target.type === "array")
|
|
108360
|
+
return null;
|
|
108165
108361
|
if (target.type === "arrow_function" || target.type === "function_expression" || target.type === "function_declaration" || target.type === "generator_function" || target.type === "generator_function_declaration")
|
|
108166
108362
|
return null;
|
|
108167
108363
|
if (target.type === "jsx_element" || target.type === "jsx_fragment" || target.type === "jsx_self_closing_element")
|
|
108168
108364
|
return null;
|
|
108169
108365
|
let operatorCount = 0;
|
|
108366
|
+
let hasLogical = false;
|
|
108367
|
+
let hasArithmetic = false;
|
|
108368
|
+
const LOGICAL_OPS = /* @__PURE__ */ new Set(["&&", "||", "??"]);
|
|
108369
|
+
const ARITHMETIC_OPS3 = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%", "**", "&", "|", "^", "<<", ">>", ">>>"]);
|
|
108170
108370
|
const BINARY_TYPES = /* @__PURE__ */ new Set(["binary_expression", "logical_expression"]);
|
|
108171
108371
|
const FUNCTION_BOUNDARY_TYPES3 = /* @__PURE__ */ new Set([
|
|
108172
108372
|
"function_declaration",
|
|
@@ -108179,6 +108379,13 @@ var init_expression_complexity = __esm({
|
|
|
108179
108379
|
function countOps(n) {
|
|
108180
108380
|
if (BINARY_TYPES.has(n.type)) {
|
|
108181
108381
|
operatorCount++;
|
|
108382
|
+
const op = n.childForFieldName("operator")?.text;
|
|
108383
|
+
if (op) {
|
|
108384
|
+
if (LOGICAL_OPS.has(op))
|
|
108385
|
+
hasLogical = true;
|
|
108386
|
+
else if (ARITHMETIC_OPS3.has(op))
|
|
108387
|
+
hasArithmetic = true;
|
|
108388
|
+
}
|
|
108182
108389
|
}
|
|
108183
108390
|
for (let i = 0; i < n.childCount; i++) {
|
|
108184
108391
|
const child = n.child(i);
|
|
@@ -108191,6 +108398,8 @@ var init_expression_complexity = __esm({
|
|
|
108191
108398
|
}
|
|
108192
108399
|
countOps(expr);
|
|
108193
108400
|
if (operatorCount > 5) {
|
|
108401
|
+
if (hasLogical && !hasArithmetic)
|
|
108402
|
+
return null;
|
|
108194
108403
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Complex expression", `Expression has ${operatorCount} binary/logical operators (max 5). Break it into named variables for readability.`, sourceCode, "Split the expression into smaller, named intermediate variables.");
|
|
108195
108404
|
}
|
|
108196
108405
|
return null;
|
|
@@ -109015,12 +109224,73 @@ var init_strict_equality = __esm({
|
|
|
109015
109224
|
}
|
|
109016
109225
|
});
|
|
109017
109226
|
|
|
109018
|
-
// packages/analyzer/dist/rules/code-quality/visitors/
|
|
109019
|
-
|
|
109227
|
+
// packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js
|
|
109228
|
+
function stripLineMarker(line) {
|
|
109229
|
+
return line.replace(/^\s*(?:\/\/+|#+)\s?/, "").replace(/^\s*\*\/?\s?/, "").replace(/\*\/\s*$/, "").trim();
|
|
109230
|
+
}
|
|
109231
|
+
function looksLikeProse(line) {
|
|
109232
|
+
const words = line.match(/[A-Za-z][A-Za-z'-]+/g) ?? [];
|
|
109233
|
+
const codePunct = (line.match(/[;{}()=<>[\]]/g) ?? []).length;
|
|
109234
|
+
return words.length >= 4 && codePunct <= 1;
|
|
109235
|
+
}
|
|
109236
|
+
function looksLikeCode(line, opts) {
|
|
109237
|
+
if (/[;{}]\s*$/.test(line))
|
|
109238
|
+
return true;
|
|
109239
|
+
if (opts.colonTerminates && /:\s*$/.test(line))
|
|
109240
|
+
return true;
|
|
109241
|
+
if (/=>/.test(line))
|
|
109242
|
+
return true;
|
|
109243
|
+
if (/^[\w.]+\s*\([^)]*\)\s*;?\s*$/.test(line))
|
|
109244
|
+
return true;
|
|
109245
|
+
if (/^[\w.[\]]+\s*=\s*[^=].*$/.test(line) && !/==/.test(line))
|
|
109246
|
+
return true;
|
|
109247
|
+
return false;
|
|
109248
|
+
}
|
|
109249
|
+
function isCommentedOutCode(inner, opts) {
|
|
109250
|
+
let code = 0;
|
|
109251
|
+
let prose = 0;
|
|
109252
|
+
for (const raw of inner.split("\n")) {
|
|
109253
|
+
const line = stripLineMarker(raw);
|
|
109254
|
+
if (line.length === 0)
|
|
109255
|
+
continue;
|
|
109256
|
+
if (looksLikeProse(line))
|
|
109257
|
+
prose++;
|
|
109258
|
+
else if (looksLikeCode(line, opts))
|
|
109259
|
+
code++;
|
|
109260
|
+
}
|
|
109261
|
+
return code > 0 && code >= prose;
|
|
109262
|
+
}
|
|
109263
|
+
function lineCommentRunInner(node2, opts) {
|
|
109264
|
+
const isRunLine = (n) => n != null && n.type === "comment" && opts.isLineComment(n.text);
|
|
109265
|
+
if (!isRunLine(node2))
|
|
109266
|
+
return null;
|
|
109267
|
+
const prev = node2.previousSibling;
|
|
109268
|
+
if (isRunLine(prev) && prev.startPosition.row === node2.startPosition.row - 1)
|
|
109269
|
+
return null;
|
|
109270
|
+
const lines = [opts.strip(node2.text)];
|
|
109271
|
+
let cur = node2.nextSibling;
|
|
109272
|
+
let lastRow = node2.startPosition.row;
|
|
109273
|
+
while (isRunLine(cur) && cur.startPosition.row === lastRow + 1) {
|
|
109274
|
+
lines.push(opts.strip(cur.text));
|
|
109275
|
+
lastRow = cur.startPosition.row;
|
|
109276
|
+
cur = cur.nextSibling;
|
|
109277
|
+
}
|
|
109278
|
+
return lines.join("\n");
|
|
109279
|
+
}
|
|
109020
109280
|
var init_commented_out_code = __esm({
|
|
109281
|
+
"packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js"() {
|
|
109282
|
+
"use strict";
|
|
109283
|
+
}
|
|
109284
|
+
});
|
|
109285
|
+
|
|
109286
|
+
// packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js
|
|
109287
|
+
var isLineComment, commentedOutCodeVisitor;
|
|
109288
|
+
var init_commented_out_code2 = __esm({
|
|
109021
109289
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js"() {
|
|
109022
109290
|
"use strict";
|
|
109023
109291
|
init_types3();
|
|
109292
|
+
init_commented_out_code();
|
|
109293
|
+
isLineComment = (t) => t.startsWith("//");
|
|
109024
109294
|
commentedOutCodeVisitor = {
|
|
109025
109295
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
109026
109296
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -109029,21 +109299,19 @@ var init_commented_out_code = __esm({
|
|
|
109029
109299
|
const text4 = node2.text;
|
|
109030
109300
|
if (text4.startsWith("/**"))
|
|
109031
109301
|
return null;
|
|
109032
|
-
let inner
|
|
109033
|
-
if (
|
|
109034
|
-
inner =
|
|
109035
|
-
|
|
109036
|
-
|
|
109037
|
-
if (
|
|
109038
|
-
|
|
109039
|
-
|
|
109040
|
-
|
|
109041
|
-
|
|
109042
|
-
|
|
109043
|
-
|
|
109044
|
-
|
|
109045
|
-
const matchCount = codePatterns.filter((p) => p.test(inner)).length;
|
|
109046
|
-
if (matchCount >= 2) {
|
|
109302
|
+
let inner;
|
|
109303
|
+
if (isLineComment(text4)) {
|
|
109304
|
+
inner = lineCommentRunInner(node2, { isLineComment, strip: (t) => t.replace(/^\/\/+/, "") });
|
|
109305
|
+
if (inner === null)
|
|
109306
|
+
return null;
|
|
109307
|
+
} else if (text4.startsWith("/*")) {
|
|
109308
|
+
inner = text4.slice(2, -2);
|
|
109309
|
+
} else {
|
|
109310
|
+
return null;
|
|
109311
|
+
}
|
|
109312
|
+
if (inner.trim().length < 10)
|
|
109313
|
+
return null;
|
|
109314
|
+
if (isCommentedOutCode(inner, {})) {
|
|
109047
109315
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
109048
109316
|
}
|
|
109049
109317
|
return null;
|
|
@@ -109824,6 +110092,11 @@ var init_for_in_without_filter = __esm({
|
|
|
109824
110092
|
return true;
|
|
109825
110093
|
}
|
|
109826
110094
|
}
|
|
110095
|
+
if (n.type === "member_expression") {
|
|
110096
|
+
const prop = n.childForFieldName("property");
|
|
110097
|
+
if (prop?.text === "hasOwnProperty" || prop?.text === "hasOwn")
|
|
110098
|
+
return true;
|
|
110099
|
+
}
|
|
109827
110100
|
if (n.type === "string" && (n.text.includes("hasOwnProperty") || n.text.includes("hasOwn")))
|
|
109828
110101
|
return true;
|
|
109829
110102
|
for (let i = 0; i < n.childCount; i++) {
|
|
@@ -111191,6 +111464,10 @@ var init_selector_parameter = __esm({
|
|
|
111191
111464
|
languages: ["typescript", "tsx", "javascript"],
|
|
111192
111465
|
nodeTypes: JS_FUNCTION_TYPES,
|
|
111193
111466
|
visit(node2, filePath, sourceCode) {
|
|
111467
|
+
const fnParent = node2.parent;
|
|
111468
|
+
if ((node2.type === "arrow_function" || node2.type === "function_expression") && (fnParent?.type === "arguments" || fnParent?.type === "jsx_expression")) {
|
|
111469
|
+
return null;
|
|
111470
|
+
}
|
|
111194
111471
|
const params = node2.childForFieldName("parameters");
|
|
111195
111472
|
if (!params)
|
|
111196
111473
|
return null;
|
|
@@ -113273,6 +113550,15 @@ var init_meaningless_void_operator = __esm({
|
|
|
113273
113550
|
});
|
|
113274
113551
|
|
|
113275
113552
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js
|
|
113553
|
+
function isTestFile(filePath) {
|
|
113554
|
+
const norm = filePath.replace(/\\/g, "/");
|
|
113555
|
+
const base = norm.slice(norm.lastIndexOf("/") + 1);
|
|
113556
|
+
if (/\.(test|spec)\.[cm]?[jt]sx?$/i.test(base))
|
|
113557
|
+
return true;
|
|
113558
|
+
if (/(^|\/)__tests?__\//.test(norm))
|
|
113559
|
+
return true;
|
|
113560
|
+
return false;
|
|
113561
|
+
}
|
|
113276
113562
|
var testWithHardcodedTimeoutVisitor;
|
|
113277
113563
|
var init_test_with_hardcoded_timeout = __esm({
|
|
113278
113564
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js"() {
|
|
@@ -113283,7 +113569,7 @@ var init_test_with_hardcoded_timeout = __esm({
|
|
|
113283
113569
|
languages: ["typescript", "tsx", "javascript"],
|
|
113284
113570
|
nodeTypes: ["call_expression"],
|
|
113285
113571
|
visit(node2, filePath, sourceCode) {
|
|
113286
|
-
if (!
|
|
113572
|
+
if (!isTestFile(filePath))
|
|
113287
113573
|
return null;
|
|
113288
113574
|
const fn = node2.childForFieldName("function");
|
|
113289
113575
|
if (!fn)
|
|
@@ -114643,8 +114929,8 @@ var init_test_empty_file = __esm({
|
|
|
114643
114929
|
nodeTypes: ["program"],
|
|
114644
114930
|
visit(node2, filePath, sourceCode) {
|
|
114645
114931
|
const lowerPath = filePath.toLowerCase();
|
|
114646
|
-
const
|
|
114647
|
-
if (!
|
|
114932
|
+
const isTestFile6 = lowerPath.includes(".test.") || lowerPath.includes(".spec.") || lowerPath.includes("__tests__");
|
|
114933
|
+
if (!isTestFile6)
|
|
114648
114934
|
return null;
|
|
114649
114935
|
if (hasTestFunction(node2))
|
|
114650
114936
|
return null;
|
|
@@ -118350,7 +118636,7 @@ var init_javascript5 = __esm({
|
|
|
118350
118636
|
init_useless_computed_key();
|
|
118351
118637
|
init_useless_concat();
|
|
118352
118638
|
init_strict_equality();
|
|
118353
|
-
|
|
118639
|
+
init_commented_out_code2();
|
|
118354
118640
|
init_inverted_boolean();
|
|
118355
118641
|
init_prefer_single_boolean_return();
|
|
118356
118642
|
init_prefer_immediate_return();
|
|
@@ -119517,30 +119803,27 @@ var init_unused_variable2 = __esm({
|
|
|
119517
119803
|
});
|
|
119518
119804
|
|
|
119519
119805
|
// packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js
|
|
119520
|
-
var pythonCommentedOutCodeVisitor;
|
|
119521
|
-
var
|
|
119806
|
+
var isLineComment2, pythonCommentedOutCodeVisitor;
|
|
119807
|
+
var init_commented_out_code3 = __esm({
|
|
119522
119808
|
"packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js"() {
|
|
119523
119809
|
"use strict";
|
|
119524
119810
|
init_types3();
|
|
119811
|
+
init_commented_out_code();
|
|
119812
|
+
isLineComment2 = (t) => t.startsWith("#");
|
|
119525
119813
|
pythonCommentedOutCodeVisitor = {
|
|
119526
119814
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
119527
119815
|
languages: ["python"],
|
|
119528
119816
|
nodeTypes: ["comment"],
|
|
119529
119817
|
visit(node2, filePath, sourceCode) {
|
|
119530
119818
|
const text4 = node2.text;
|
|
119531
|
-
if (!text4
|
|
119819
|
+
if (!isLineComment2(text4))
|
|
119532
119820
|
return null;
|
|
119533
|
-
const inner =
|
|
119534
|
-
if (inner
|
|
119821
|
+
const inner = lineCommentRunInner(node2, { isLineComment: isLineComment2, strip: (t) => t.replace(/^#+/, "") });
|
|
119822
|
+
if (inner === null)
|
|
119535
119823
|
return null;
|
|
119536
|
-
|
|
119537
|
-
|
|
119538
|
-
|
|
119539
|
-
/\w+\(.*\)/,
|
|
119540
|
-
/=\s*\w/
|
|
119541
|
-
];
|
|
119542
|
-
const matchCount = codePatterns.filter((p) => p.test(inner)).length;
|
|
119543
|
-
if (matchCount >= 2) {
|
|
119824
|
+
if (inner.trim().length < 10)
|
|
119825
|
+
return null;
|
|
119826
|
+
if (isCommentedOutCode(inner, { colonTerminates: true })) {
|
|
119544
119827
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
119545
119828
|
}
|
|
119546
119829
|
return null;
|
|
@@ -128533,8 +128816,8 @@ var init_test_not_discoverable = __esm({
|
|
|
128533
128816
|
return null;
|
|
128534
128817
|
const className = classNameNode.text;
|
|
128535
128818
|
const isTestClass = className.startsWith("Test") || className.endsWith("Test") || className.endsWith("Tests");
|
|
128536
|
-
const
|
|
128537
|
-
if (!isTestClass && !
|
|
128819
|
+
const isTestFile6 = isPythonTestFile(filePath);
|
|
128820
|
+
if (!isTestClass && !isTestFile6)
|
|
128538
128821
|
return null;
|
|
128539
128822
|
const body = node2.childForFieldName("body");
|
|
128540
128823
|
if (!body)
|
|
@@ -130303,7 +130586,7 @@ var init_python7 = __esm({
|
|
|
130303
130586
|
init_no_debugger2();
|
|
130304
130587
|
init_require_await2();
|
|
130305
130588
|
init_unused_variable2();
|
|
130306
|
-
|
|
130589
|
+
init_commented_out_code3();
|
|
130307
130590
|
init_abstract_class_without_abstract_method();
|
|
130308
130591
|
init_and_or_ternary();
|
|
130309
130592
|
init_assert_in_production();
|
|
@@ -131477,7 +131760,18 @@ function withLanguageSupport(rules, visitors) {
|
|
|
131477
131760
|
continue;
|
|
131478
131761
|
}
|
|
131479
131762
|
if (rule.engine === "roslyn-host" || rule.engine === "roslyn-workspace") {
|
|
131480
|
-
|
|
131763
|
+
if (language === "csharp") {
|
|
131764
|
+
support[language] = { status: "supported" };
|
|
131765
|
+
continue;
|
|
131766
|
+
}
|
|
131767
|
+
const hasExplicitForLang = explicitFamilies.get(rule.key)?.has(language) ?? false;
|
|
131768
|
+
const universalExcludesLang2 = universalExcludedFamilies.get(rule.key)?.has(language) ?? false;
|
|
131769
|
+
const hasUniversalForLang = universalRuleKeys.has(rule.key) && UNIVERSAL_VISITOR_FAMILIES.includes(language) && !universalExcludesLang2;
|
|
131770
|
+
if (hasExplicitForLang || hasUniversalForLang) {
|
|
131771
|
+
support[language] = { status: "supported" };
|
|
131772
|
+
continue;
|
|
131773
|
+
}
|
|
131774
|
+
support[language] = { status: "not-applicable", reason: "C# semantic rule (Roslyn host); no equivalent in this language" };
|
|
131481
131775
|
continue;
|
|
131482
131776
|
}
|
|
131483
131777
|
const hasExplicit = explicitFamilies.get(rule.key)?.has(language) ?? false;
|
|
@@ -140236,6 +140530,12 @@ function walkCSharp(node2, visit) {
|
|
|
140236
140530
|
walkCSharp(child, visit);
|
|
140237
140531
|
}
|
|
140238
140532
|
}
|
|
140533
|
+
function isCSharpTestPath(filePath) {
|
|
140534
|
+
return filePath.split(/[\\/]/).some((segment) => {
|
|
140535
|
+
const s = segment.toLowerCase();
|
|
140536
|
+
return /\.tests?$/.test(s) || s.endsWith(".testbase") || s.endsWith("testapp");
|
|
140537
|
+
});
|
|
140538
|
+
}
|
|
140239
140539
|
var FUNCTION_BOUNDARY_TYPES;
|
|
140240
140540
|
var init_csharp_helpers = __esm({
|
|
140241
140541
|
"packages/analyzer/dist/rules/_shared/csharp-helpers.js"() {
|
|
@@ -141650,7 +141950,7 @@ function isTypeConstantAccess(node2) {
|
|
|
141650
141950
|
const receiver = lastSegment(node2.childForFieldName("expression")?.text ?? "");
|
|
141651
141951
|
return /^[A-Z]/.test(receiver);
|
|
141652
141952
|
}
|
|
141653
|
-
function
|
|
141953
|
+
function leafName2(node2) {
|
|
141654
141954
|
if (node2.type === "identifier")
|
|
141655
141955
|
return node2.text;
|
|
141656
141956
|
if (node2.type === "member_access_expression")
|
|
@@ -141686,8 +141986,8 @@ var init_timing_attack_comparison2 = __esm({
|
|
|
141686
141986
|
return null;
|
|
141687
141987
|
if (isTypeConstantAccess(left) || isTypeConstantAccess(right))
|
|
141688
141988
|
return null;
|
|
141689
|
-
const leftLeaf =
|
|
141690
|
-
const rightLeaf =
|
|
141989
|
+
const leftLeaf = leafName2(left);
|
|
141990
|
+
const rightLeaf = leafName2(right);
|
|
141691
141991
|
if (leftLeaf === "Length" || rightLeaf === "Length")
|
|
141692
141992
|
return null;
|
|
141693
141993
|
if (ENUM_CONSTANT_PATTERN2.test(leftLeaf) || ENUM_CONSTANT_PATTERN2.test(rightLeaf))
|
|
@@ -141706,7 +142006,7 @@ var init_timing_attack_comparison2 = __esm({
|
|
|
141706
142006
|
});
|
|
141707
142007
|
|
|
141708
142008
|
// packages/analyzer/dist/rules/security/visitors/csharp/insecure-random.js
|
|
141709
|
-
function
|
|
142009
|
+
function isTestFile2(filePath) {
|
|
141710
142010
|
const fileName = filePath.replace(/^.*[\\/]/, "");
|
|
141711
142011
|
return /Tests?\.cs$/i.test(fileName) || /(?:TestSetup|TestFixture|Fixtures?|FunctionalTests|IntegrationTests|UnitTests|TestHelpers?)/.test(fileName);
|
|
141712
142012
|
}
|
|
@@ -141748,7 +142048,7 @@ var init_insecure_random3 = __esm({
|
|
|
141748
142048
|
languages: ["csharp"],
|
|
141749
142049
|
nodeTypes: ["object_creation_expression", "member_access_expression"],
|
|
141750
142050
|
visit(node2, filePath, sourceCode) {
|
|
141751
|
-
if (
|
|
142051
|
+
if (isTestFile2(filePath))
|
|
141752
142052
|
return null;
|
|
141753
142053
|
if (node2.type === "object_creation_expression") {
|
|
141754
142054
|
if (getCreatedTypeName(node2) !== "Random")
|
|
@@ -144219,11 +144519,14 @@ var init_empty_catch3 = __esm({
|
|
|
144219
144519
|
const stmt = tryStatements[0];
|
|
144220
144520
|
if (/\b(Parse|TryParse|Deserialize)\s*[<(]/.test(stmt.text))
|
|
144221
144521
|
return null;
|
|
144222
|
-
|
|
144223
|
-
if (expr?.type === "
|
|
144522
|
+
let expr = stmt.namedChildren[0];
|
|
144523
|
+
if (expr?.type === "await_expression")
|
|
144524
|
+
expr = expr.namedChildren[0];
|
|
144525
|
+
const isBestEffort = (name) => BEST_EFFORT_METHODS.has(name.replace(/Async$/, ""));
|
|
144526
|
+
if (expr?.type === "invocation_expression" && isBestEffort(getCSharpMethodName(expr))) {
|
|
144224
144527
|
return null;
|
|
144225
144528
|
}
|
|
144226
|
-
if (expr?.type === "conditional_access_expression" &&
|
|
144529
|
+
if (expr?.type === "conditional_access_expression" && isBestEffort((expr.text.match(/\.(\w+)\s*\([^()]*\)\s*$/) ?? [])[1] ?? "")) {
|
|
144227
144530
|
return null;
|
|
144228
144531
|
}
|
|
144229
144532
|
}
|
|
@@ -144287,6 +144590,8 @@ var init_self_assignment3 = __esm({
|
|
|
144287
144590
|
languages: ["csharp"],
|
|
144288
144591
|
nodeTypes: ["assignment_expression"],
|
|
144289
144592
|
visit(node2, filePath, sourceCode) {
|
|
144593
|
+
if (node2.parent?.type === "initializer_expression")
|
|
144594
|
+
return null;
|
|
144290
144595
|
const left = node2.childForFieldName("left");
|
|
144291
144596
|
const right = node2.childForFieldName("right");
|
|
144292
144597
|
const operator = node2.childForFieldName("operator");
|
|
@@ -145294,7 +145599,7 @@ var init_infinite_recursion2 = __esm({
|
|
|
145294
145599
|
return null;
|
|
145295
145600
|
if (node2.type === "property_declaration") {
|
|
145296
145601
|
const arrow = node2.childForFieldName("value");
|
|
145297
|
-
const arrowExpr = arrow?.namedChildren[0];
|
|
145602
|
+
const arrowExpr = arrow?.type === "arrow_expression_clause" ? arrow.namedChildren[0] : null;
|
|
145298
145603
|
if (arrowExpr?.type === "identifier" && arrowExpr.text === name) {
|
|
145299
145604
|
return makeViolation(this.ruleKey, node2, filePath, "critical", "Infinite recursion", `Property \`${name}\` returns itself \u2014 reading it recurses until StackOverflowException.`, sourceCode, "Return the backing field (or use an auto-property) instead of the property itself.");
|
|
145300
145605
|
}
|
|
@@ -147359,8 +147664,11 @@ var init_useless_finally2 = __esm({
|
|
|
147359
147664
|
});
|
|
147360
147665
|
|
|
147361
147666
|
// packages/analyzer/dist/rules/bugs/visitors/csharp/fstring-missing-placeholders.js
|
|
147362
|
-
function
|
|
147363
|
-
|
|
147667
|
+
function hasRealHole(node2) {
|
|
147668
|
+
if (node2.namedChildren.some((c) => c?.type === "interpolation"))
|
|
147669
|
+
return true;
|
|
147670
|
+
const stripped = node2.text.replace(/\{\{/g, "").replace(/\}\}/g, "");
|
|
147671
|
+
return stripped.includes("{");
|
|
147364
147672
|
}
|
|
147365
147673
|
var csharpFstringMissingPlaceholdersVisitor;
|
|
147366
147674
|
var init_fstring_missing_placeholders2 = __esm({
|
|
@@ -147372,7 +147680,7 @@ var init_fstring_missing_placeholders2 = __esm({
|
|
|
147372
147680
|
languages: ["csharp"],
|
|
147373
147681
|
nodeTypes: ["interpolated_string_expression"],
|
|
147374
147682
|
visit(node2, filePath, sourceCode) {
|
|
147375
|
-
if (
|
|
147683
|
+
if (hasRealHole(node2))
|
|
147376
147684
|
return null;
|
|
147377
147685
|
let top = node2;
|
|
147378
147686
|
while (top.parent?.type === "binary_expression" && top.parent.childForFieldName("operator")?.text === "+") {
|
|
@@ -147383,7 +147691,7 @@ var init_fstring_missing_placeholders2 = __esm({
|
|
|
147383
147691
|
const walk20 = (n) => {
|
|
147384
147692
|
if (siblingHasHoles)
|
|
147385
147693
|
return;
|
|
147386
|
-
if (n.type === "interpolated_string_expression" && n.id !== node2.id &&
|
|
147694
|
+
if (n.type === "interpolated_string_expression" && n.id !== node2.id && hasRealHole(n)) {
|
|
147387
147695
|
siblingHasHoles = true;
|
|
147388
147696
|
return;
|
|
147389
147697
|
}
|
|
@@ -148863,6 +149171,32 @@ var init_chained_orderby_loses_ordering = __esm({
|
|
|
148863
149171
|
});
|
|
148864
149172
|
|
|
148865
149173
|
// packages/analyzer/dist/rules/bugs/visitors/csharp/check-against-value-being-assigned.js
|
|
149174
|
+
function enclosingTypeDecl(node2) {
|
|
149175
|
+
let p = node2.parent;
|
|
149176
|
+
while (p) {
|
|
149177
|
+
if (p.type === "class_declaration" || p.type === "struct_declaration" || p.type === "record_declaration") {
|
|
149178
|
+
return p;
|
|
149179
|
+
}
|
|
149180
|
+
p = p.parent;
|
|
149181
|
+
}
|
|
149182
|
+
return null;
|
|
149183
|
+
}
|
|
149184
|
+
function selfMemberName(target) {
|
|
149185
|
+
if (target.type === "identifier")
|
|
149186
|
+
return target.text;
|
|
149187
|
+
if (target.type === "member_access_expression") {
|
|
149188
|
+
const obj = target.childForFieldName("expression");
|
|
149189
|
+
if (obj?.type === "this")
|
|
149190
|
+
return target.childForFieldName("name")?.text ?? null;
|
|
149191
|
+
}
|
|
149192
|
+
return null;
|
|
149193
|
+
}
|
|
149194
|
+
function enclosingTypeHasProperty(typeDecl, name) {
|
|
149195
|
+
const body = typeDecl.childForFieldName("body") ?? typeDecl.namedChildren.find((c) => c?.type === "declaration_list");
|
|
149196
|
+
if (!body)
|
|
149197
|
+
return false;
|
|
149198
|
+
return body.namedChildren.some((m) => m?.type === "property_declaration" && m.childForFieldName("name")?.text === name);
|
|
149199
|
+
}
|
|
148866
149200
|
function singleStatement(consequence) {
|
|
148867
149201
|
if (consequence.type === "block") {
|
|
148868
149202
|
const stmts = consequence.namedChildren.filter((c) => c?.type !== "comment");
|
|
@@ -148910,6 +149244,12 @@ var init_check_against_value_being_assigned = __esm({
|
|
|
148910
149244
|
const sameBA = condLeft.text === value.text && condRight.text === target.text;
|
|
148911
149245
|
if (!sameAB && !sameBA)
|
|
148912
149246
|
return null;
|
|
149247
|
+
const selfName = selfMemberName(target);
|
|
149248
|
+
if (selfName) {
|
|
149249
|
+
const typeDecl = enclosingTypeDecl(node2);
|
|
149250
|
+
if (typeDecl && enclosingTypeHasProperty(typeDecl, selfName))
|
|
149251
|
+
return null;
|
|
149252
|
+
}
|
|
148913
149253
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Redundant check against the value being assigned", `\`if (${condition.text}) ${assign.text};\` guards a plain assignment with a check against the same value \u2014 assigning unconditionally has the identical effect.`, sourceCode, "Remove the guard and assign unconditionally, or fix the condition if a different check was intended.");
|
|
148914
149254
|
}
|
|
148915
149255
|
};
|
|
@@ -149399,7 +149739,8 @@ var init_redundant_base_call = __esm({
|
|
|
149399
149739
|
const receiver = node2.childForFieldName("expression");
|
|
149400
149740
|
if (receiver?.type !== "base" && receiver?.text !== "base")
|
|
149401
149741
|
return null;
|
|
149402
|
-
const
|
|
149742
|
+
const nameNode = node2.childForFieldName("name");
|
|
149743
|
+
const memberName3 = nameNode?.type === "generic_name" ? nameNode.namedChildren.find((c) => c?.type === "identifier")?.text : nameNode?.text;
|
|
149403
149744
|
if (!memberName3)
|
|
149404
149745
|
return null;
|
|
149405
149746
|
const typeDecl = enclosingType(node2);
|
|
@@ -149734,6 +150075,19 @@ function baseEntryName(entry) {
|
|
|
149734
150075
|
}
|
|
149735
150076
|
return null;
|
|
149736
150077
|
}
|
|
150078
|
+
function baseEntryArity(entry) {
|
|
150079
|
+
const generic = entry.type === "generic_name" ? entry : entry.type === "qualified_name" ? entry.childForFieldName("name") : null;
|
|
150080
|
+
if (generic?.type !== "generic_name")
|
|
150081
|
+
return 0;
|
|
150082
|
+
const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
|
|
150083
|
+
return args ? args.namedChildren.filter((c) => c != null).length : 0;
|
|
150084
|
+
}
|
|
150085
|
+
function declaredArity(node2) {
|
|
150086
|
+
const params = node2.childForFieldName("type_parameters");
|
|
150087
|
+
if (!params)
|
|
150088
|
+
return 0;
|
|
150089
|
+
return params.namedChildren.filter((c) => c?.type === "type_parameter").length;
|
|
150090
|
+
}
|
|
149737
150091
|
var csharpRecursiveTypeInheritanceVisitor;
|
|
149738
150092
|
var init_recursive_type_inheritance = __esm({
|
|
149739
150093
|
"packages/analyzer/dist/rules/bugs/visitors/csharp/recursive-type-inheritance.js"() {
|
|
@@ -149755,6 +150109,8 @@ var init_recursive_type_inheritance = __esm({
|
|
|
149755
150109
|
return null;
|
|
149756
150110
|
if (baseEntryName(firstBase) !== typeName2)
|
|
149757
150111
|
return null;
|
|
150112
|
+
if (baseEntryArity(firstBase) !== declaredArity(node2))
|
|
150113
|
+
return null;
|
|
149758
150114
|
return makeViolation(this.ruleKey, firstBase, filePath, "high", "Recursive type inheritance", `\`${typeName2}\` lists itself as its own base type, producing a nonsensical recursive inheritance chain.`, sourceCode, "Remove the self reference from the base list.");
|
|
149759
150115
|
}
|
|
149760
150116
|
};
|
|
@@ -151982,6 +152338,20 @@ function isCSharpGeneratedSource(filePath, sourceCode) {
|
|
|
151982
152338
|
return true;
|
|
151983
152339
|
return sourceCode.slice(0, 600).includes("<auto-generated");
|
|
151984
152340
|
}
|
|
152341
|
+
function isCSharpEfMigration(node2) {
|
|
152342
|
+
const cls = getCSharpEnclosingClass(node2);
|
|
152343
|
+
if (!cls)
|
|
152344
|
+
return false;
|
|
152345
|
+
const baseList = cls.namedChildren.find((c) => c?.type === "base_list");
|
|
152346
|
+
if (!baseList)
|
|
152347
|
+
return false;
|
|
152348
|
+
return baseList.namedChildren.some((base) => {
|
|
152349
|
+
if (!base)
|
|
152350
|
+
return false;
|
|
152351
|
+
const simple = base.text.split(".").pop() ?? base.text;
|
|
152352
|
+
return simple === "Migration";
|
|
152353
|
+
});
|
|
152354
|
+
}
|
|
151985
152355
|
function isCSharpPrivateMember(node2) {
|
|
151986
152356
|
const accessibility = [];
|
|
151987
152357
|
for (const child of node2.children) {
|
|
@@ -154545,17 +154915,13 @@ var init_collapsible_if3 = __esm({
|
|
|
154545
154915
|
});
|
|
154546
154916
|
|
|
154547
154917
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js
|
|
154548
|
-
var
|
|
154549
|
-
var
|
|
154918
|
+
var isLineComment3, csharpCommentedOutCodeVisitor;
|
|
154919
|
+
var init_commented_out_code4 = __esm({
|
|
154550
154920
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js"() {
|
|
154551
154921
|
"use strict";
|
|
154552
154922
|
init_types3();
|
|
154553
|
-
|
|
154554
|
-
|
|
154555
|
-
/[;{}]\s*$/,
|
|
154556
|
-
/=>/,
|
|
154557
|
-
/\w+\(.*\)\s*[;{]?\s*$/
|
|
154558
|
-
];
|
|
154923
|
+
init_commented_out_code();
|
|
154924
|
+
isLineComment3 = (t) => t.startsWith("//") && !t.startsWith("///");
|
|
154559
154925
|
csharpCommentedOutCodeVisitor = {
|
|
154560
154926
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
154561
154927
|
languages: ["csharp"],
|
|
@@ -154564,15 +154930,19 @@ var init_commented_out_code3 = __esm({
|
|
|
154564
154930
|
const text4 = node2.text;
|
|
154565
154931
|
if (text4.startsWith("///") || text4.startsWith("/**"))
|
|
154566
154932
|
return null;
|
|
154567
|
-
let inner
|
|
154568
|
-
if (
|
|
154569
|
-
inner =
|
|
154570
|
-
|
|
154571
|
-
|
|
154572
|
-
if (
|
|
154933
|
+
let inner;
|
|
154934
|
+
if (isLineComment3(text4)) {
|
|
154935
|
+
inner = lineCommentRunInner(node2, { isLineComment: isLineComment3, strip: (t) => t.replace(/^\/\/+/, "") });
|
|
154936
|
+
if (inner === null)
|
|
154937
|
+
return null;
|
|
154938
|
+
} else if (text4.startsWith("/*")) {
|
|
154939
|
+
inner = text4.slice(2, -2);
|
|
154940
|
+
} else {
|
|
154573
154941
|
return null;
|
|
154574
|
-
|
|
154575
|
-
if (
|
|
154942
|
+
}
|
|
154943
|
+
if (inner.trim().length < 10)
|
|
154944
|
+
return null;
|
|
154945
|
+
if (isCommentedOutCode(inner, {})) {
|
|
154576
154946
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
154577
154947
|
}
|
|
154578
154948
|
return null;
|
|
@@ -155280,6 +155650,13 @@ function csharpEmptyBodyIsIntentional(node2, body) {
|
|
|
155280
155650
|
}
|
|
155281
155651
|
if (isCSharpNoOpCallbackPosition(node2))
|
|
155282
155652
|
return true;
|
|
155653
|
+
let enclosingType3 = node2.parent;
|
|
155654
|
+
while (enclosingType3 && enclosingType3.type !== "class_declaration" && enclosingType3.type !== "struct_declaration" && enclosingType3.type !== "record_declaration" && enclosingType3.type !== "record_struct_declaration") {
|
|
155655
|
+
enclosingType3 = enclosingType3.parent;
|
|
155656
|
+
}
|
|
155657
|
+
const typeName2 = enclosingType3?.childForFieldName("name")?.text;
|
|
155658
|
+
if (typeName2 && /^(Null|Noop|NoOp|Empty)[A-Z0-9]/.test(typeName2))
|
|
155659
|
+
return true;
|
|
155283
155660
|
if (node2.type === "method_declaration") {
|
|
155284
155661
|
if (hasCSharpModifier(node2, "virtual") || hasCSharpModifier(node2, "override") || hasCSharpModifier(node2, "partial"))
|
|
155285
155662
|
return true;
|
|
@@ -155557,6 +155934,7 @@ var init_hardcoded_url2 = __esm({
|
|
|
155557
155934
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/hardcoded-url.js"() {
|
|
155558
155935
|
"use strict";
|
|
155559
155936
|
init_types3();
|
|
155937
|
+
init_csharp_helpers();
|
|
155560
155938
|
NAMESPACE_URI_HOSTS = /w3\.org|schema\.org|xmlns|openxmlformats|xmlsoap|purl\.org|tempuri\.org|schemas\.microsoft\.com|tools\.ietf\.org|datatracker\.ietf\.org|rfc-editor\.org/;
|
|
155561
155939
|
STABLE_API_HOSTS = /googleapis\.com|maps\.google|api\.stripe\.com|api\.twilio\.com|api\.sendgrid\.com|api\.github\.com|cdn\.|fonts\.googleapis|cloudflare|unpkg\.com|cdnjs\.cloudflare|jsdelivr\.net|nuget\.org/;
|
|
155562
155940
|
PLACEHOLDER_NAME = /default|placeholder|example|sample|template|fallback|demo|test|mock|stub|dummy/;
|
|
@@ -155568,6 +155946,8 @@ var init_hardcoded_url2 = __esm({
|
|
|
155568
155946
|
const text4 = node2.text;
|
|
155569
155947
|
if (!/https?:\/\/[a-zA-Z0-9]/.test(text4))
|
|
155570
155948
|
return null;
|
|
155949
|
+
if (isCSharpTestPath(filePath))
|
|
155950
|
+
return null;
|
|
155571
155951
|
if (text4.includes("example.com") || text4.includes("localhost") || text4.includes("127.0.0.1") || text4.includes("placeholder"))
|
|
155572
155952
|
return null;
|
|
155573
155953
|
if (NAMESPACE_URI_HOSTS.test(text4))
|
|
@@ -155588,6 +155968,42 @@ var init_hardcoded_url2 = __esm({
|
|
|
155588
155968
|
});
|
|
155589
155969
|
|
|
155590
155970
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js
|
|
155971
|
+
function isTrivialExpr(e) {
|
|
155972
|
+
switch (e.type) {
|
|
155973
|
+
case "integer_literal":
|
|
155974
|
+
case "real_literal":
|
|
155975
|
+
case "boolean_literal":
|
|
155976
|
+
case "string_literal":
|
|
155977
|
+
case "verbatim_string_literal":
|
|
155978
|
+
case "null_literal":
|
|
155979
|
+
case "character_literal":
|
|
155980
|
+
case "identifier":
|
|
155981
|
+
case "this_expression":
|
|
155982
|
+
case "default_expression":
|
|
155983
|
+
case "member_access_expression":
|
|
155984
|
+
return true;
|
|
155985
|
+
case "invocation_expression": {
|
|
155986
|
+
const args = e.childForFieldName("arguments");
|
|
155987
|
+
if (!args)
|
|
155988
|
+
return true;
|
|
155989
|
+
return args.namedChildren.every((a) => {
|
|
155990
|
+
if (a?.type !== "argument")
|
|
155991
|
+
return true;
|
|
155992
|
+
const value = a.namedChildren.find((c) => c && c.type !== "name_colon");
|
|
155993
|
+
return !value || isTrivialExpr(value);
|
|
155994
|
+
});
|
|
155995
|
+
}
|
|
155996
|
+
default:
|
|
155997
|
+
return false;
|
|
155998
|
+
}
|
|
155999
|
+
}
|
|
156000
|
+
function isTrivialReturnBody(body) {
|
|
156001
|
+
const stmts = body.namedChildren.filter((c) => c && c.type !== "comment");
|
|
156002
|
+
if (stmts.length !== 1 || stmts[0]?.type !== "return_statement")
|
|
156003
|
+
return false;
|
|
156004
|
+
const expr = stmts[0].namedChildren.find((c) => c && c.type !== "comment");
|
|
156005
|
+
return !expr || isTrivialExpr(expr);
|
|
156006
|
+
}
|
|
155591
156007
|
var COMPARED_TYPES, csharpIdenticalFunctionsVisitor;
|
|
155592
156008
|
var init_identical_functions2 = __esm({
|
|
155593
156009
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js"() {
|
|
@@ -155606,7 +156022,7 @@ var init_identical_functions2 = __esm({
|
|
|
155606
156022
|
const body = n.childForFieldName("body");
|
|
155607
156023
|
if (body && body.type === "block" && body.namedChildCount > 0) {
|
|
155608
156024
|
const onlyThrow = body.namedChildCount === 1 && body.namedChildren[0]?.type === "throw_statement";
|
|
155609
|
-
if (!onlyThrow) {
|
|
156025
|
+
if (!onlyThrow && !isTrivialReturnBody(body)) {
|
|
155610
156026
|
const normalized = body.text.replace(/\s+/g, " ").trim();
|
|
155611
156027
|
bodies.push({ body: normalized, fnNode: n });
|
|
155612
156028
|
}
|
|
@@ -155796,6 +156212,8 @@ var init_magic_number2 = __esm({
|
|
|
155796
156212
|
return null;
|
|
155797
156213
|
if (parentType === "argument" && parent.parent?.type === "bracketed_argument_list")
|
|
155798
156214
|
return null;
|
|
156215
|
+
if (parentType === "argument" && parent.childForFieldName("name"))
|
|
156216
|
+
return null;
|
|
155799
156217
|
if (parentType === "binary_expression" && TIME_FACTORS2.has(val)) {
|
|
155800
156218
|
const op = parent.childForFieldName("operator");
|
|
155801
156219
|
if (op?.text === "*") {
|
|
@@ -156117,6 +156535,8 @@ var init_no_extraneous_class2 = __esm({
|
|
|
156117
156535
|
return null;
|
|
156118
156536
|
if (node2.namedChildren.some((c) => c?.type === "base_list"))
|
|
156119
156537
|
return null;
|
|
156538
|
+
if (node2.namedChildren.some((c) => c?.type === "attribute_list"))
|
|
156539
|
+
return null;
|
|
156120
156540
|
const body = node2.childForFieldName("body");
|
|
156121
156541
|
if (!body)
|
|
156122
156542
|
return null;
|
|
@@ -156375,7 +156795,8 @@ var init_redundant_jump3 = __esm({
|
|
|
156375
156795
|
if (!grandparent)
|
|
156376
156796
|
return null;
|
|
156377
156797
|
if (node2.type === "return_statement") {
|
|
156378
|
-
|
|
156798
|
+
const hasExpression = node2.children.some((c) => c != null && c.type !== "return" && c.type !== ";");
|
|
156799
|
+
if (hasExpression)
|
|
156379
156800
|
return null;
|
|
156380
156801
|
if (!isCSharpFunctionBoundary(grandparent.type))
|
|
156381
156802
|
return null;
|
|
@@ -157740,6 +158161,8 @@ var init_too_many_lines3 = __esm({
|
|
|
157740
158161
|
const bodyNode = getCSharpFunctionBody(node2);
|
|
157741
158162
|
if (!bodyNode)
|
|
157742
158163
|
return null;
|
|
158164
|
+
if (isCSharpGeneratedSource(filePath, sourceCode) || isCSharpEfMigration(node2))
|
|
158165
|
+
return null;
|
|
157743
158166
|
const lineCount = bodyNode.endPosition.row - bodyNode.startPosition.row + 1;
|
|
157744
158167
|
if (lineCount > 50) {
|
|
157745
158168
|
const name = getCSharpFunctionName(node2);
|
|
@@ -158387,93 +158810,6 @@ var init_unused_collection2 = __esm({
|
|
|
158387
158810
|
}
|
|
158388
158811
|
});
|
|
158389
158812
|
|
|
158390
|
-
// packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js
|
|
158391
|
-
var csharpUnusedFunctionParameterVisitor;
|
|
158392
|
-
var init_unused_function_parameter2 = __esm({
|
|
158393
|
-
"packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js"() {
|
|
158394
|
-
"use strict";
|
|
158395
|
-
init_types3();
|
|
158396
|
-
init_csharp_helpers();
|
|
158397
|
-
csharpUnusedFunctionParameterVisitor = {
|
|
158398
|
-
ruleKey: "code-quality/deterministic/unused-function-parameter",
|
|
158399
|
-
languages: ["csharp"],
|
|
158400
|
-
nodeTypes: ["method_declaration", "constructor_declaration", "local_function_statement"],
|
|
158401
|
-
visit(node2, filePath, sourceCode) {
|
|
158402
|
-
const params = node2.childForFieldName("parameters");
|
|
158403
|
-
if (!params || params.namedChildCount === 0)
|
|
158404
|
-
return null;
|
|
158405
|
-
const body = node2.childForFieldName("body");
|
|
158406
|
-
if (!body)
|
|
158407
|
-
return null;
|
|
158408
|
-
if (body.type === "block" && body.namedChildCount === 0)
|
|
158409
|
-
return null;
|
|
158410
|
-
if (/NotImplementedException|NotSupportedException/.test(body.text))
|
|
158411
|
-
return null;
|
|
158412
|
-
if (body.type === "block" && body.namedChildCount === 1 && body.namedChildren[0]?.type === "return_statement" && /Task\.CompletedTask|Task\.FromResult|ValueTask\.CompletedTask/.test(body.namedChildren[0].text)) {
|
|
158413
|
-
return null;
|
|
158414
|
-
}
|
|
158415
|
-
if (node2.type === "method_declaration") {
|
|
158416
|
-
for (const mod of ["override", "virtual", "abstract", "partial", "extern"]) {
|
|
158417
|
-
if (hasCSharpModifier(node2, mod))
|
|
158418
|
-
return null;
|
|
158419
|
-
}
|
|
158420
|
-
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
158421
|
-
return null;
|
|
158422
|
-
if (node2.childForFieldName("name")?.text === "Main")
|
|
158423
|
-
return null;
|
|
158424
|
-
}
|
|
158425
|
-
const paramList = [];
|
|
158426
|
-
for (const param of params.namedChildren) {
|
|
158427
|
-
if (!param || param.type !== "parameter")
|
|
158428
|
-
continue;
|
|
158429
|
-
if (param.children.some((c) => c?.type === "this" || c?.type === "modifier" && c.text === "this"))
|
|
158430
|
-
continue;
|
|
158431
|
-
if (param.childForFieldName("type")?.text === "CancellationToken")
|
|
158432
|
-
continue;
|
|
158433
|
-
const nameNode = param.childForFieldName("name");
|
|
158434
|
-
if (nameNode?.type === "identifier")
|
|
158435
|
-
paramList.push({ name: nameNode.text, node: param });
|
|
158436
|
-
}
|
|
158437
|
-
if (paramList.length === 0)
|
|
158438
|
-
return null;
|
|
158439
|
-
if (paramList.length === 2) {
|
|
158440
|
-
const first2 = params.namedChildren.find((c) => c?.type === "parameter");
|
|
158441
|
-
const firstType = first2?.childForFieldName("type")?.text;
|
|
158442
|
-
const secondType = paramList[1] ? paramList[1].node.childForFieldName("type")?.text : void 0;
|
|
158443
|
-
if ((firstType === "object" || firstType === "object?") && secondType?.endsWith("EventArgs"))
|
|
158444
|
-
return null;
|
|
158445
|
-
}
|
|
158446
|
-
const used = /* @__PURE__ */ new Set();
|
|
158447
|
-
function collectReads(n) {
|
|
158448
|
-
if (n.type === "identifier") {
|
|
158449
|
-
const parent = n.parent;
|
|
158450
|
-
const isMemberName = parent && (parent.type === "member_access_expression" || parent.type === "qualified_name" || parent.type === "member_binding_expression") && parent.childForFieldName("name")?.id === n.id;
|
|
158451
|
-
if (!isMemberName)
|
|
158452
|
-
used.add(n.text);
|
|
158453
|
-
}
|
|
158454
|
-
for (let i = 0; i < n.namedChildCount; i++) {
|
|
158455
|
-
const child = n.namedChild(i);
|
|
158456
|
-
if (child)
|
|
158457
|
-
collectReads(child);
|
|
158458
|
-
}
|
|
158459
|
-
}
|
|
158460
|
-
collectReads(body);
|
|
158461
|
-
const initializer = node2.namedChildren.find((c) => c?.type === "constructor_initializer");
|
|
158462
|
-
if (initializer)
|
|
158463
|
-
collectReads(initializer);
|
|
158464
|
-
for (const { name, node: paramNode } of paramList) {
|
|
158465
|
-
if (name.startsWith("_"))
|
|
158466
|
-
continue;
|
|
158467
|
-
if (!used.has(name)) {
|
|
158468
|
-
return makeViolation(this.ruleKey, paramNode, filePath, "low", `Unused parameter \`${name}\``, `Parameter \`${name}\` is never used in the method body. Remove it or rename it to \`_\` if the signature is fixed.`, sourceCode, `Remove unused parameter \`${name}\` or rename it to \`_${name}\`.`);
|
|
158469
|
-
}
|
|
158470
|
-
}
|
|
158471
|
-
return null;
|
|
158472
|
-
}
|
|
158473
|
-
};
|
|
158474
|
-
}
|
|
158475
|
-
});
|
|
158476
|
-
|
|
158477
158813
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-private-member.js
|
|
158478
158814
|
var csharpUnusedPrivateMemberVisitor;
|
|
158479
158815
|
var init_unused_private_member2 = __esm({
|
|
@@ -158498,6 +158834,8 @@ var init_unused_private_member2 = __esm({
|
|
|
158498
158834
|
for (const member of body.namedChildren) {
|
|
158499
158835
|
if (!member)
|
|
158500
158836
|
continue;
|
|
158837
|
+
if (member.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
158838
|
+
continue;
|
|
158501
158839
|
if (!isCSharpPrivateMember(member))
|
|
158502
158840
|
continue;
|
|
158503
158841
|
if (getCSharpDeclAttributeNames(member).length > 0)
|
|
@@ -158895,8 +159233,17 @@ var init_abstract_class_public_constructor = __esm({
|
|
|
158895
159233
|
});
|
|
158896
159234
|
|
|
158897
159235
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/abstract-class-without-abstract-members.js
|
|
158898
|
-
function
|
|
158899
|
-
return body.namedChildren.some((c) => c && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c,
|
|
159236
|
+
function hasMemberWithModifier(body, modifier) {
|
|
159237
|
+
return body.namedChildren.some((c) => c != null && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c, modifier));
|
|
159238
|
+
}
|
|
159239
|
+
function hasProtectedConstructor(body) {
|
|
159240
|
+
return body.namedChildren.some((c) => c?.type === "constructor_declaration" && hasCSharpModifier(c, "protected"));
|
|
159241
|
+
}
|
|
159242
|
+
function hasBaseList(classDecl) {
|
|
159243
|
+
return classDecl.namedChildren.some((c) => c?.type === "base_list");
|
|
159244
|
+
}
|
|
159245
|
+
function hasExtensionSignal(classDecl, body) {
|
|
159246
|
+
return hasMemberWithModifier(body, "abstract") || hasMemberWithModifier(body, "virtual") || hasMemberWithModifier(body, "override") || hasProtectedConstructor(body) || hasBaseList(classDecl);
|
|
158900
159247
|
}
|
|
158901
159248
|
var MEMBER_TYPES2, csharpAbstractClassWithoutAbstractMembersVisitor;
|
|
158902
159249
|
var init_abstract_class_without_abstract_members = __esm({
|
|
@@ -158908,6 +159255,7 @@ var init_abstract_class_without_abstract_members = __esm({
|
|
|
158908
159255
|
"method_declaration",
|
|
158909
159256
|
"property_declaration",
|
|
158910
159257
|
"event_declaration",
|
|
159258
|
+
"event_field_declaration",
|
|
158911
159259
|
"indexer_declaration"
|
|
158912
159260
|
]);
|
|
158913
159261
|
csharpAbstractClassWithoutAbstractMembersVisitor = {
|
|
@@ -158922,10 +159270,10 @@ var init_abstract_class_without_abstract_members = __esm({
|
|
|
158922
159270
|
const body = node2.childForFieldName("body");
|
|
158923
159271
|
if (!body)
|
|
158924
159272
|
return null;
|
|
158925
|
-
if (
|
|
159273
|
+
if (hasExtensionSignal(node2, body))
|
|
158926
159274
|
return null;
|
|
158927
159275
|
const name = node2.childForFieldName("name")?.text ?? "class";
|
|
158928
|
-
return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members, so it should be a concrete
|
|
159276
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members and no other reason to be abstract (no virtual/override member, no protected constructor, no base type), so it should be a concrete class or a static holder instead.`, sourceCode, "Add abstract members, drop the `abstract` modifier, or convert the type to an interface.");
|
|
158929
159277
|
}
|
|
158930
159278
|
};
|
|
158931
159279
|
}
|
|
@@ -159843,6 +160191,8 @@ var init_static_holder_type_has_constructor = __esm({
|
|
|
159843
160191
|
return null;
|
|
159844
160192
|
if (hasCSharpModifier(node2, "abstract"))
|
|
159845
160193
|
return null;
|
|
160194
|
+
if (node2.namedChildren.some((c) => c?.type === "base_list"))
|
|
160195
|
+
return null;
|
|
159846
160196
|
const body = node2.namedChildren.find((c) => c?.type === "declaration_list");
|
|
159847
160197
|
if (!body)
|
|
159848
160198
|
return null;
|
|
@@ -161726,6 +162076,15 @@ var init_extension_method_on_object = __esm({
|
|
|
161726
162076
|
});
|
|
161727
162077
|
|
|
161728
162078
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js
|
|
162079
|
+
function isInterfaceMember(method) {
|
|
162080
|
+
let current = method.parent;
|
|
162081
|
+
while (current) {
|
|
162082
|
+
if (TYPE_DECL_TYPES2.has(current.type))
|
|
162083
|
+
return current.type === "interface_declaration";
|
|
162084
|
+
current = current.parent;
|
|
162085
|
+
}
|
|
162086
|
+
return false;
|
|
162087
|
+
}
|
|
161729
162088
|
function identifiersIn2(node2) {
|
|
161730
162089
|
const used = /* @__PURE__ */ new Set();
|
|
161731
162090
|
if (!node2)
|
|
@@ -161747,14 +162106,23 @@ function parameterTypeIdentifiers(method) {
|
|
|
161747
162106
|
for (const id of identifiersIn2(param.childForFieldName("type")))
|
|
161748
162107
|
used.add(id);
|
|
161749
162108
|
}
|
|
162109
|
+
for (const id of identifiersIn2(params.childForFieldName("type")))
|
|
162110
|
+
used.add(id);
|
|
161750
162111
|
return used;
|
|
161751
162112
|
}
|
|
161752
|
-
var csharpGenericParameterNotInferableVisitor;
|
|
162113
|
+
var TYPE_DECL_TYPES2, csharpGenericParameterNotInferableVisitor;
|
|
161753
162114
|
var init_generic_parameter_not_inferable = __esm({
|
|
161754
162115
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js"() {
|
|
161755
162116
|
"use strict";
|
|
161756
162117
|
init_types3();
|
|
161757
162118
|
init_csharp_helpers();
|
|
162119
|
+
TYPE_DECL_TYPES2 = /* @__PURE__ */ new Set([
|
|
162120
|
+
"interface_declaration",
|
|
162121
|
+
"class_declaration",
|
|
162122
|
+
"struct_declaration",
|
|
162123
|
+
"record_declaration",
|
|
162124
|
+
"record_struct_declaration"
|
|
162125
|
+
]);
|
|
161758
162126
|
csharpGenericParameterNotInferableVisitor = {
|
|
161759
162127
|
ruleKey: "code-quality/deterministic/generic-parameter-not-inferable",
|
|
161760
162128
|
languages: ["csharp"],
|
|
@@ -161763,6 +162131,8 @@ var init_generic_parameter_not_inferable = __esm({
|
|
|
161763
162131
|
const typeParams = node2.namedChildren.find((c) => c?.type === "type_parameter_list");
|
|
161764
162132
|
if (!typeParams)
|
|
161765
162133
|
return null;
|
|
162134
|
+
if (isInterfaceMember(node2))
|
|
162135
|
+
return null;
|
|
161766
162136
|
const usedInParams = parameterTypeIdentifiers(node2);
|
|
161767
162137
|
const nameNode = node2.childForFieldName("name");
|
|
161768
162138
|
const usedInReturn = /* @__PURE__ */ new Set();
|
|
@@ -161774,15 +162144,13 @@ var init_generic_parameter_not_inferable = __esm({
|
|
|
161774
162144
|
for (const id of identifiersIn2(child))
|
|
161775
162145
|
usedInReturn.add(id);
|
|
161776
162146
|
}
|
|
161777
|
-
const usedInBody = identifiersIn2(node2.childForFieldName("body"));
|
|
161778
162147
|
for (const tp of typeParams.namedChildren) {
|
|
161779
162148
|
if (tp?.type !== "type_parameter")
|
|
161780
162149
|
continue;
|
|
161781
162150
|
const name = tp.namedChildren.find((c) => c?.type === "identifier")?.text;
|
|
161782
162151
|
if (!name)
|
|
161783
162152
|
continue;
|
|
161784
|
-
|
|
161785
|
-
if (!usedInParams.has(name) && usedElsewhere) {
|
|
162153
|
+
if (!usedInParams.has(name) && usedInReturn.has(name)) {
|
|
161786
162154
|
return makeViolation(this.ruleKey, tp, filePath, "low", "Type parameter not inferable", `Type parameter \`${name}\` appears in no method parameter, so callers must always specify it explicitly.`, sourceCode, `Use \`${name}\` in a parameter type, or accept that callers must specify it and consider a non-generic design.`);
|
|
161787
162155
|
}
|
|
161788
162156
|
}
|
|
@@ -161895,6 +162263,9 @@ var init_native_method_not_wrapped = __esm({
|
|
|
161895
162263
|
});
|
|
161896
162264
|
|
|
161897
162265
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js
|
|
162266
|
+
function genericBaseName2(generic) {
|
|
162267
|
+
return generic.namedChildren.find((c) => c?.type === "identifier")?.text ?? "";
|
|
162268
|
+
}
|
|
161898
162269
|
function asGenericName(typeNode) {
|
|
161899
162270
|
if (!typeNode)
|
|
161900
162271
|
return null;
|
|
@@ -161910,16 +162281,19 @@ function hasNestedGeneric(typeNode) {
|
|
|
161910
162281
|
const generic = asGenericName(typeNode);
|
|
161911
162282
|
if (!generic)
|
|
161912
162283
|
return false;
|
|
162284
|
+
if (DELEGATE_OR_EXPRESSION_GENERICS.has(genericBaseName2(generic)))
|
|
162285
|
+
return false;
|
|
161913
162286
|
const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
|
|
161914
162287
|
if (!args)
|
|
161915
162288
|
return false;
|
|
161916
162289
|
return args.namedChildren.some((arg) => asGenericName(arg) !== null);
|
|
161917
162290
|
}
|
|
161918
|
-
var csharpNestedGenericParameterVisitor;
|
|
162291
|
+
var DELEGATE_OR_EXPRESSION_GENERICS, csharpNestedGenericParameterVisitor;
|
|
161919
162292
|
var init_nested_generic_parameter = __esm({
|
|
161920
162293
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js"() {
|
|
161921
162294
|
"use strict";
|
|
161922
162295
|
init_types3();
|
|
162296
|
+
DELEGATE_OR_EXPRESSION_GENERICS = /* @__PURE__ */ new Set(["Func", "Action", "Predicate", "Expression"]);
|
|
161923
162297
|
csharpNestedGenericParameterVisitor = {
|
|
161924
162298
|
ruleKey: "code-quality/deterministic/nested-generic-parameter",
|
|
161925
162299
|
languages: ["csharp"],
|
|
@@ -162039,6 +162413,39 @@ function enclosingTypeBody(node2) {
|
|
|
162039
162413
|
const body = node2.parent;
|
|
162040
162414
|
return body?.type === "declaration_list" ? body : null;
|
|
162041
162415
|
}
|
|
162416
|
+
function isZeroArgCallTo(expr, getName) {
|
|
162417
|
+
if (expr?.type !== "invocation_expression")
|
|
162418
|
+
return false;
|
|
162419
|
+
const fn = expr.childForFieldName("function") ?? expr.namedChildren[0];
|
|
162420
|
+
const fnText = fn?.text;
|
|
162421
|
+
if (fnText !== getName && fnText !== `this.${getName}`)
|
|
162422
|
+
return false;
|
|
162423
|
+
const args = expr.childForFieldName("arguments") ?? expr.namedChildren.find((c) => c?.type === "argument_list");
|
|
162424
|
+
const argCount = args?.namedChildren.filter((c) => c?.type === "argument").length ?? 0;
|
|
162425
|
+
return argCount === 0;
|
|
162426
|
+
}
|
|
162427
|
+
function getterDelegatesTo(prop, getName) {
|
|
162428
|
+
const propArrow = prop.namedChildren.find((c) => c?.type === "arrow_expression_clause");
|
|
162429
|
+
if (propArrow)
|
|
162430
|
+
return isZeroArgCallTo(propArrow.namedChildren.find(Boolean), getName);
|
|
162431
|
+
const accessorList = prop.namedChildren.find((c) => c?.type === "accessor_list");
|
|
162432
|
+
if (!accessorList)
|
|
162433
|
+
return false;
|
|
162434
|
+
const getter = accessorList.namedChildren.find((a) => a?.type === "accessor_declaration" && a.children.some((c) => c?.text === "get"));
|
|
162435
|
+
if (!getter)
|
|
162436
|
+
return false;
|
|
162437
|
+
const getArrow = getter.namedChildren.find((c) => c?.type === "arrow_expression_clause");
|
|
162438
|
+
if (getArrow)
|
|
162439
|
+
return isZeroArgCallTo(getArrow.namedChildren.find(Boolean), getName);
|
|
162440
|
+
const block = getter.namedChildren.find((c) => c?.type === "block");
|
|
162441
|
+
if (block) {
|
|
162442
|
+
const stmts = block.namedChildren.filter((c) => c?.type !== "comment");
|
|
162443
|
+
if (stmts.length === 1 && stmts[0]?.type === "return_statement") {
|
|
162444
|
+
return isZeroArgCallTo(stmts[0].namedChildren.find(Boolean), getName);
|
|
162445
|
+
}
|
|
162446
|
+
}
|
|
162447
|
+
return false;
|
|
162448
|
+
}
|
|
162042
162449
|
var csharpPropertyNameMatchesGetMethodVisitor;
|
|
162043
162450
|
var init_property_name_matches_get_method = __esm({
|
|
162044
162451
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/property-name-matches-get-method.js"() {
|
|
@@ -162066,6 +162473,8 @@ var init_property_name_matches_get_method = __esm({
|
|
|
162066
162473
|
});
|
|
162067
162474
|
if (!hasGetMethod)
|
|
162068
162475
|
return null;
|
|
162476
|
+
if (getterDelegatesTo(node2, target))
|
|
162477
|
+
return null;
|
|
162069
162478
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Property name matches a Get method", `Property \`${propName}\` coexists with a parameterless \`${target}()\` method \u2014 a confusing duplication of how the value is exposed.`, sourceCode, `Keep one accessor: either the property \`${propName}\` or the method \`${target}\`, not both.`);
|
|
162070
162479
|
}
|
|
162071
162480
|
};
|
|
@@ -162073,6 +162482,50 @@ var init_property_name_matches_get_method = __esm({
|
|
|
162073
162482
|
});
|
|
162074
162483
|
|
|
162075
162484
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js
|
|
162485
|
+
function constFieldNames(fieldDecl) {
|
|
162486
|
+
const names = [];
|
|
162487
|
+
const varDecl = fieldDecl.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
162488
|
+
if (!varDecl)
|
|
162489
|
+
return names;
|
|
162490
|
+
for (const vd of varDecl.namedChildren) {
|
|
162491
|
+
if (vd?.type !== "variable_declarator")
|
|
162492
|
+
continue;
|
|
162493
|
+
const n = vd.childForFieldName("name")?.text ?? vd.namedChildren.find((c) => c?.type === "identifier")?.text;
|
|
162494
|
+
if (n)
|
|
162495
|
+
names.push(n);
|
|
162496
|
+
}
|
|
162497
|
+
return names;
|
|
162498
|
+
}
|
|
162499
|
+
function referencedBySiblingConst(node2, names) {
|
|
162500
|
+
let root = node2;
|
|
162501
|
+
while (root.parent)
|
|
162502
|
+
root = root.parent;
|
|
162503
|
+
let found = false;
|
|
162504
|
+
const scan3 = (n) => {
|
|
162505
|
+
if (found)
|
|
162506
|
+
return;
|
|
162507
|
+
if (n.type === "field_declaration" && n.id !== node2.id && hasCSharpModifier(n, "const")) {
|
|
162508
|
+
const varDecl = n.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
162509
|
+
if (varDecl) {
|
|
162510
|
+
for (const vd of varDecl.namedChildren) {
|
|
162511
|
+
if (vd?.type !== "variable_declarator")
|
|
162512
|
+
continue;
|
|
162513
|
+
const declaredName = vd.childForFieldName("name");
|
|
162514
|
+
walkCSharp(vd, (id) => {
|
|
162515
|
+
if (id.type === "identifier" && id.id !== declaredName?.id && names.includes(id.text)) {
|
|
162516
|
+
found = true;
|
|
162517
|
+
}
|
|
162518
|
+
});
|
|
162519
|
+
}
|
|
162520
|
+
}
|
|
162521
|
+
}
|
|
162522
|
+
for (const c of n.namedChildren)
|
|
162523
|
+
if (c)
|
|
162524
|
+
scan3(c);
|
|
162525
|
+
};
|
|
162526
|
+
scan3(root);
|
|
162527
|
+
return found;
|
|
162528
|
+
}
|
|
162076
162529
|
var csharpPublicConstVersioningHazardVisitor;
|
|
162077
162530
|
var init_public_const_versioning_hazard = __esm({
|
|
162078
162531
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js"() {
|
|
@@ -162088,6 +162541,9 @@ var init_public_const_versioning_hazard = __esm({
|
|
|
162088
162541
|
return null;
|
|
162089
162542
|
if (!hasCSharpModifier(node2, "public") && !hasCSharpModifier(node2, "protected"))
|
|
162090
162543
|
return null;
|
|
162544
|
+
const names = constFieldNames(node2);
|
|
162545
|
+
if (names.length > 0 && referencedBySiblingConst(node2, names))
|
|
162546
|
+
return null;
|
|
162091
162547
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Public constant versioning hazard", "A `public const` value is inlined into consumers at compile time and will not update unless they recompile \u2014 prefer `static readonly`.", sourceCode, "Replace the `public const` with a `public static readonly` field.");
|
|
162092
162548
|
}
|
|
162093
162549
|
};
|
|
@@ -162763,12 +163219,12 @@ var init_missing_access_modifier = __esm({
|
|
|
162763
163219
|
return null;
|
|
162764
163220
|
if (node2.parent?.parent?.type === "interface_declaration")
|
|
162765
163221
|
return null;
|
|
163222
|
+
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
163223
|
+
return null;
|
|
162766
163224
|
if (node2.type === "method_declaration") {
|
|
162767
163225
|
const nm = node2.childForFieldName("name");
|
|
162768
163226
|
if (nm?.type === "qualified_name" || (nm?.text ?? "").includes("."))
|
|
162769
163227
|
return null;
|
|
162770
|
-
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
162771
|
-
return null;
|
|
162772
163228
|
}
|
|
162773
163229
|
if (node2.type === "constructor_declaration" && mods.includes("static"))
|
|
162774
163230
|
return null;
|
|
@@ -162832,7 +163288,7 @@ function topLevelTypeIdentifiers(root) {
|
|
|
162832
163288
|
const body = m.namedChildren.find((c) => c?.type === "declaration_list");
|
|
162833
163289
|
if (body)
|
|
162834
163290
|
collectFrom(body);
|
|
162835
|
-
} else if (
|
|
163291
|
+
} else if (TYPE_DECL_TYPES3.has(m.type)) {
|
|
162836
163292
|
const id = m.childForFieldName("name");
|
|
162837
163293
|
if (id)
|
|
162838
163294
|
out.push(id);
|
|
@@ -162845,6 +163301,8 @@ function topLevelTypeIdentifiers(root) {
|
|
|
162845
163301
|
function nameMatches(fileName, typeName2) {
|
|
162846
163302
|
if (fileName === typeName2)
|
|
162847
163303
|
return true;
|
|
163304
|
+
if (typeName2 === `${fileName}Attribute`)
|
|
163305
|
+
return true;
|
|
162848
163306
|
const brace = fileName.indexOf("{");
|
|
162849
163307
|
if (brace > 0 && fileName.slice(0, brace) === typeName2)
|
|
162850
163308
|
return true;
|
|
@@ -162853,12 +163311,12 @@ function nameMatches(fileName, typeName2) {
|
|
|
162853
163311
|
return true;
|
|
162854
163312
|
return false;
|
|
162855
163313
|
}
|
|
162856
|
-
var
|
|
163314
|
+
var TYPE_DECL_TYPES3, csharpFilenameTypeMismatchVisitor;
|
|
162857
163315
|
var init_csharp_filename_type_mismatch = __esm({
|
|
162858
163316
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/csharp-filename-type-mismatch.js"() {
|
|
162859
163317
|
"use strict";
|
|
162860
163318
|
init_types3();
|
|
162861
|
-
|
|
163319
|
+
TYPE_DECL_TYPES3 = /* @__PURE__ */ new Set([
|
|
162862
163320
|
"class_declaration",
|
|
162863
163321
|
"struct_declaration",
|
|
162864
163322
|
"interface_declaration",
|
|
@@ -162872,13 +163330,18 @@ var init_csharp_filename_type_mismatch = __esm({
|
|
|
162872
163330
|
nodeTypes: ["compilation_unit"],
|
|
162873
163331
|
visit(node2, filePath, sourceCode) {
|
|
162874
163332
|
const base = filePath.split("/").pop() ?? "";
|
|
162875
|
-
const
|
|
163333
|
+
const lower = base.toLowerCase();
|
|
163334
|
+
const compoundExt = [".cshtml.cs", ".razor.cs"].find((ext2) => lower.endsWith(ext2));
|
|
163335
|
+
const fileName = compoundExt ? base.slice(0, base.length - compoundExt.length) : base.replace(/\.[^.]*$/, "");
|
|
162876
163336
|
if (!fileName)
|
|
162877
163337
|
return null;
|
|
163338
|
+
const migrationName = fileName.match(/^\d{14}_(.+)$/)?.[1];
|
|
163339
|
+
const candidateNames2 = migrationName ? [fileName, migrationName] : [fileName];
|
|
162878
163340
|
const identifiers = topLevelTypeIdentifiers(node2);
|
|
162879
163341
|
if (identifiers.length === 0)
|
|
162880
163342
|
return null;
|
|
162881
|
-
|
|
163343
|
+
const matches = (id) => candidateNames2.some((fn) => nameMatches(fn, id.text)) || compoundExt !== void 0 && id.text === `${fileName}Model`;
|
|
163344
|
+
if (identifiers.some(matches))
|
|
162882
163345
|
return null;
|
|
162883
163346
|
const first2 = identifiers[0];
|
|
162884
163347
|
return makeViolation(this.ruleKey, first2, filePath, "low", "File name does not match a contained type", `File '${fileName}' declares type '${first2.text}' but no top-level type matches the file name; name the file after a type so it is easy to locate.`, sourceCode, `Rename the file after a contained type, e.g. ${first2.text}.cs.`);
|
|
@@ -163331,7 +163794,7 @@ var init_csharp6 = __esm({
|
|
|
163331
163794
|
init_cognitive_complexity3();
|
|
163332
163795
|
init_collapsible_else_if2();
|
|
163333
163796
|
init_collapsible_if3();
|
|
163334
|
-
|
|
163797
|
+
init_commented_out_code4();
|
|
163335
163798
|
init_comparison_of_constant2();
|
|
163336
163799
|
init_console_log2();
|
|
163337
163800
|
init_contradictory_boolean_expression2();
|
|
@@ -163414,7 +163877,6 @@ var init_csharp6 = __esm({
|
|
|
163414
163877
|
init_unnecessary_namespace_qualifier2();
|
|
163415
163878
|
init_unsafe_any_usage2();
|
|
163416
163879
|
init_unused_collection2();
|
|
163417
|
-
init_unused_function_parameter2();
|
|
163418
163880
|
init_unused_private_member2();
|
|
163419
163881
|
init_unused_private_method2();
|
|
163420
163882
|
init_unused_private_nested_class2();
|
|
@@ -163673,7 +164135,6 @@ var init_csharp6 = __esm({
|
|
|
163673
164135
|
csharpUnnecessaryNamespaceQualifierVisitor,
|
|
163674
164136
|
csharpUnsafeAnyUsageVisitor,
|
|
163675
164137
|
csharpUnusedCollectionVisitor,
|
|
163676
|
-
csharpUnusedFunctionParameterVisitor,
|
|
163677
164138
|
csharpUnusedPrivateMemberVisitor,
|
|
163678
164139
|
csharpUnusedPrivateMethodVisitor,
|
|
163679
164140
|
csharpUnusedPrivateNestedClassVisitor,
|
|
@@ -165855,6 +166316,11 @@ function allElementsConstant(creation) {
|
|
|
165855
166316
|
return false;
|
|
165856
166317
|
return elements.every((e) => LITERAL_TYPES6.has(e.type));
|
|
165857
166318
|
}
|
|
166319
|
+
function isGeneratedOrMigrationFile(filePath) {
|
|
166320
|
+
if (/(^|[\\/])Migrations[\\/]/.test(filePath))
|
|
166321
|
+
return true;
|
|
166322
|
+
return /\.(designer|g|g\.i|generated)\.cs$/i.test(filePath);
|
|
166323
|
+
}
|
|
165858
166324
|
var LITERAL_TYPES6, csharpConstantArrayArgumentVisitor;
|
|
165859
166325
|
var init_constant_array_argument = __esm({
|
|
165860
166326
|
"packages/analyzer/dist/rules/performance/visitors/csharp/constant-array-argument.js"() {
|
|
@@ -165875,6 +166341,8 @@ var init_constant_array_argument = __esm({
|
|
|
165875
166341
|
languages: ["csharp"],
|
|
165876
166342
|
nodeTypes: ["array_creation_expression", "implicit_array_creation_expression"],
|
|
165877
166343
|
visit(node2, filePath, sourceCode) {
|
|
166344
|
+
if (isGeneratedOrMigrationFile(filePath))
|
|
166345
|
+
return null;
|
|
165878
166346
|
if (node2.parent?.type !== "argument")
|
|
165879
166347
|
return null;
|
|
165880
166348
|
if (!getCSharpEnclosingFunction(node2))
|
|
@@ -166663,7 +167131,36 @@ var init_missing_finally_cleanup2 = __esm({
|
|
|
166663
167131
|
});
|
|
166664
167132
|
|
|
166665
167133
|
// packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js
|
|
166666
|
-
|
|
167134
|
+
function bodyDeclaresMember(body, name) {
|
|
167135
|
+
for (const member of body.namedChildren) {
|
|
167136
|
+
if (!member)
|
|
167137
|
+
continue;
|
|
167138
|
+
if (member.type === "property_declaration") {
|
|
167139
|
+
if (member.childForFieldName("name")?.text === name)
|
|
167140
|
+
return true;
|
|
167141
|
+
} else if (member.type === "field_declaration") {
|
|
167142
|
+
const decl = member.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
167143
|
+
for (const d of decl?.namedChildren ?? []) {
|
|
167144
|
+
if (d?.type === "variable_declarator" && d.childForFieldName("name")?.text === name)
|
|
167145
|
+
return true;
|
|
167146
|
+
}
|
|
167147
|
+
}
|
|
167148
|
+
}
|
|
167149
|
+
return false;
|
|
167150
|
+
}
|
|
167151
|
+
function enclosingTypeDeclaresMember(node2, name) {
|
|
167152
|
+
let current = node2.parent;
|
|
167153
|
+
while (current) {
|
|
167154
|
+
if (TYPE_DECLS2.has(current.type)) {
|
|
167155
|
+
const body = current.childForFieldName("body");
|
|
167156
|
+
if (body && bodyDeclaresMember(body, name))
|
|
167157
|
+
return true;
|
|
167158
|
+
}
|
|
167159
|
+
current = current.parent;
|
|
167160
|
+
}
|
|
167161
|
+
return false;
|
|
167162
|
+
}
|
|
167163
|
+
var JSON_PARSERS, TYPE_DECLS2, csharpUnsafeJsonParseVisitor;
|
|
166667
167164
|
var init_unsafe_json_parse3 = __esm({
|
|
166668
167165
|
"packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js"() {
|
|
166669
167166
|
"use strict";
|
|
@@ -166678,15 +167175,25 @@ var init_unsafe_json_parse3 = __esm({
|
|
|
166678
167175
|
JsonDocument: /* @__PURE__ */ new Set(["Parse", "ParseAsync"]),
|
|
166679
167176
|
JsonNode: /* @__PURE__ */ new Set(["Parse"])
|
|
166680
167177
|
};
|
|
167178
|
+
TYPE_DECLS2 = /* @__PURE__ */ new Set([
|
|
167179
|
+
"class_declaration",
|
|
167180
|
+
"struct_declaration",
|
|
167181
|
+
"record_declaration",
|
|
167182
|
+
"record_struct_declaration"
|
|
167183
|
+
]);
|
|
166681
167184
|
csharpUnsafeJsonParseVisitor = {
|
|
166682
167185
|
ruleKey: "reliability/deterministic/unsafe-json-parse",
|
|
166683
167186
|
languages: ["csharp"],
|
|
166684
167187
|
nodeTypes: ["invocation_expression"],
|
|
166685
167188
|
visit(node2, filePath, sourceCode) {
|
|
166686
|
-
const
|
|
167189
|
+
const rawReceiver = getCSharpReceiver(node2);
|
|
167190
|
+
const receiver = simpleTypeName5(rawReceiver);
|
|
166687
167191
|
const method = getCSharpMethodName(node2);
|
|
166688
167192
|
if (!JSON_PARSERS[receiver]?.has(method))
|
|
166689
167193
|
return null;
|
|
167194
|
+
const bareReceiver = rawReceiver.startsWith("this.") ? rawReceiver.slice(5) : rawReceiver;
|
|
167195
|
+
if (!bareReceiver.includes(".") && enclosingTypeDeclaresMember(node2, bareReceiver))
|
|
167196
|
+
return null;
|
|
166690
167197
|
const firstArg = getCSharpArguments(node2)[0];
|
|
166691
167198
|
if (firstArg?.type === "invocation_expression") {
|
|
166692
167199
|
const innerReceiver = simpleTypeName5(getCSharpReceiver(firstArg));
|
|
@@ -168393,18 +168900,10 @@ var init_logger_field_naming = __esm({
|
|
|
168393
168900
|
csharpLoggerFieldNamingVisitor = {
|
|
168394
168901
|
ruleKey: "style/deterministic/logger-field-naming",
|
|
168395
168902
|
languages: ["csharp"],
|
|
168396
|
-
nodeTypes: ["field_declaration"
|
|
168903
|
+
nodeTypes: ["field_declaration"],
|
|
168397
168904
|
visit(node2, filePath, sourceCode) {
|
|
168398
168905
|
if (isAutoGeneratedCSharpFile(sourceCode))
|
|
168399
168906
|
return null;
|
|
168400
|
-
if (node2.type === "property_declaration") {
|
|
168401
|
-
if (!isLoggerType(node2.childForFieldName("type")))
|
|
168402
|
-
return null;
|
|
168403
|
-
const name = node2.childForFieldName("name");
|
|
168404
|
-
if (!name || ACCEPTED.test(name.text))
|
|
168405
|
-
return null;
|
|
168406
|
-
return makeViolation(this.ruleKey, name, filePath, "low", "Logger does not follow the naming convention", `Logger '${name.text}' does not follow the convention for logger members (camelCase, optionally _-prefixed).`, sourceCode, `Rename the logger to a camelCase name such as '_logger'.`);
|
|
168407
|
-
}
|
|
168408
168907
|
const declaration = node2.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
168409
168908
|
if (!declaration)
|
|
168410
168909
|
return null;
|
|
@@ -169212,15 +169711,28 @@ function isNestedInType(node2) {
|
|
|
169212
169711
|
const parent = node2.parent;
|
|
169213
169712
|
if (parent?.type !== "declaration_list")
|
|
169214
169713
|
return false;
|
|
169215
|
-
return parent.parent != null &&
|
|
169714
|
+
return parent.parent != null && TYPE_DECL_TYPES4.has(parent.parent.type);
|
|
169216
169715
|
}
|
|
169217
|
-
|
|
169716
|
+
function isConstantContainer(node2) {
|
|
169717
|
+
if (node2.type !== "class_declaration")
|
|
169718
|
+
return false;
|
|
169719
|
+
if (!hasCSharpModifier(node2, "static"))
|
|
169720
|
+
return false;
|
|
169721
|
+
const body = node2.childForFieldName("body");
|
|
169722
|
+
if (!body)
|
|
169723
|
+
return false;
|
|
169724
|
+
const members = body.namedChildren.filter((c) => c && c.type !== "comment");
|
|
169725
|
+
if (members.length === 0)
|
|
169726
|
+
return false;
|
|
169727
|
+
return members.every((m) => m?.type === "field_declaration" && (hasCSharpModifier(m, "const") || hasCSharpModifier(m, "static") && hasCSharpModifier(m, "readonly")));
|
|
169728
|
+
}
|
|
169729
|
+
var TYPE_DECL_TYPES4, csharpNestedTypePubliclyVisibleVisitor;
|
|
169218
169730
|
var init_nested_type_publicly_visible = __esm({
|
|
169219
169731
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/nested-type-publicly-visible.js"() {
|
|
169220
169732
|
"use strict";
|
|
169221
169733
|
init_types3();
|
|
169222
169734
|
init_csharp_helpers();
|
|
169223
|
-
|
|
169735
|
+
TYPE_DECL_TYPES4 = /* @__PURE__ */ new Set([
|
|
169224
169736
|
"class_declaration",
|
|
169225
169737
|
"struct_declaration",
|
|
169226
169738
|
"interface_declaration",
|
|
@@ -169238,6 +169750,8 @@ var init_nested_type_publicly_visible = __esm({
|
|
|
169238
169750
|
return null;
|
|
169239
169751
|
if (getCSharpAttributeNames(node2).length > 0)
|
|
169240
169752
|
return null;
|
|
169753
|
+
if (isConstantContainer(node2))
|
|
169754
|
+
return null;
|
|
169241
169755
|
const name = node2.childForFieldName("name")?.text ?? "type";
|
|
169242
169756
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Publicly visible nested type", `Nested type '${name}' is publicly visible, exposing an implementation detail of its containing type.`, sourceCode, `Make '${name}' private or internal, or lift it out to its own top-level type if it is a real part of the API.`);
|
|
169243
169757
|
}
|
|
@@ -169325,6 +169839,8 @@ var init_type_outside_namespace = __esm({
|
|
|
169325
169839
|
|
|
169326
169840
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js
|
|
169327
169841
|
function nameLooksLikeUri(name) {
|
|
169842
|
+
if (PREPOSITION_BEFORE_URI.test(name))
|
|
169843
|
+
return false;
|
|
169328
169844
|
if (URI_NAME_PATTERN.test(name))
|
|
169329
169845
|
return true;
|
|
169330
169846
|
return /(Url|Uri|Endpoint|Webhook)$/.test(name);
|
|
@@ -169332,11 +169848,12 @@ function nameLooksLikeUri(name) {
|
|
|
169332
169848
|
function isStringType(typeNode) {
|
|
169333
169849
|
return typeNode?.type === "predefined_type" && typeNode.text === "string";
|
|
169334
169850
|
}
|
|
169335
|
-
var URI_NAME_PATTERN;
|
|
169851
|
+
var URI_NAME_PATTERN, PREPOSITION_BEFORE_URI;
|
|
169336
169852
|
var init_uri_helpers = __esm({
|
|
169337
169853
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js"() {
|
|
169338
169854
|
"use strict";
|
|
169339
169855
|
URI_NAME_PATTERN = /(^|[^a-z])(url|uri|uris|urls|endpoint|endpoints|webhook|webhookurl|callbackurl|redirecturi|redirecturl|hreflink|baseurl|baseaddress)$/i;
|
|
169856
|
+
PREPOSITION_BEFORE_URI = /(In|As|Of|At|By|Within)(Url|Uri|Uris|Urls|Endpoint|Endpoints|Webhook)$/;
|
|
169340
169857
|
}
|
|
169341
169858
|
});
|
|
169342
169859
|
|
|
@@ -169374,13 +169891,22 @@ var init_uri_parameter_as_string = __esm({
|
|
|
169374
169891
|
});
|
|
169375
169892
|
|
|
169376
169893
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js
|
|
169377
|
-
|
|
169894
|
+
function initializerProvesNotUri(valueNode) {
|
|
169895
|
+
if (!valueNode || !isCSharpStringNode(valueNode))
|
|
169896
|
+
return false;
|
|
169897
|
+
const text4 = getCSharpStringText(valueNode);
|
|
169898
|
+
if (text4 == null || text4.length === 0)
|
|
169899
|
+
return false;
|
|
169900
|
+
return !text4.includes("://");
|
|
169901
|
+
}
|
|
169902
|
+
var BINDING_ATTRIBUTES, csharpUriPropertyAsStringVisitor;
|
|
169378
169903
|
var init_uri_property_as_string = __esm({
|
|
169379
169904
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js"() {
|
|
169380
169905
|
"use strict";
|
|
169381
169906
|
init_types3();
|
|
169382
169907
|
init_csharp_helpers();
|
|
169383
169908
|
init_uri_helpers();
|
|
169909
|
+
BINDING_ATTRIBUTES = /* @__PURE__ */ new Set(["BindProperty", "FromQuery", "FromRoute", "FromForm", "ModelBinder"]);
|
|
169384
169910
|
csharpUriPropertyAsStringVisitor = {
|
|
169385
169911
|
ruleKey: "architecture/deterministic/uri-property-as-string",
|
|
169386
169912
|
languages: ["csharp"],
|
|
@@ -169393,6 +169919,10 @@ var init_uri_property_as_string = __esm({
|
|
|
169393
169919
|
const name = node2.childForFieldName("name")?.text;
|
|
169394
169920
|
if (!name || !nameLooksLikeUri(name))
|
|
169395
169921
|
return null;
|
|
169922
|
+
if (getCSharpAttributeNames(node2).some((a) => BINDING_ATTRIBUTES.has(a)))
|
|
169923
|
+
return null;
|
|
169924
|
+
if (initializerProvesNotUri(node2.childForFieldName("value")))
|
|
169925
|
+
return null;
|
|
169396
169926
|
return makeViolation(this.ruleKey, node2.childForFieldName("type"), filePath, "low", "URI property typed as string", `Property '${name}' holds a URI but is typed as string; use System.Uri to get parsing and validation.`, sourceCode, `Change the type of '${name}' from string to System.Uri.`);
|
|
169397
169927
|
}
|
|
169398
169928
|
};
|
|
@@ -169430,6 +169960,21 @@ function isModelType(classDecl) {
|
|
|
169430
169960
|
const name = classDecl.childForFieldName("name")?.text ?? "";
|
|
169431
169961
|
return MODEL_NAME_SUFFIX.test(name);
|
|
169432
169962
|
}
|
|
169963
|
+
function isServerConstructed(classDecl, body) {
|
|
169964
|
+
for (const member of body.namedChildren) {
|
|
169965
|
+
if (member?.type === "constructor_declaration") {
|
|
169966
|
+
const params = member.childForFieldName("parameters");
|
|
169967
|
+
if (params && params.namedChildCount > 0)
|
|
169968
|
+
return true;
|
|
169969
|
+
}
|
|
169970
|
+
if (member?.type === "method_declaration" && hasCSharpModifier(member, "static")) {
|
|
169971
|
+
const mName = member.childForFieldName("name")?.text ?? "";
|
|
169972
|
+
if (mName === "Create" || mName.startsWith("From"))
|
|
169973
|
+
return true;
|
|
169974
|
+
}
|
|
169975
|
+
}
|
|
169976
|
+
return false;
|
|
169977
|
+
}
|
|
169433
169978
|
function flaggablePropertyType(typeNode) {
|
|
169434
169979
|
if (typeNode?.type !== "predefined_type")
|
|
169435
169980
|
return null;
|
|
@@ -169442,7 +169987,7 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169442
169987
|
"use strict";
|
|
169443
169988
|
init_types3();
|
|
169444
169989
|
init_csharp_helpers();
|
|
169445
|
-
MODEL_NAME_SUFFIX = /(Request|
|
|
169990
|
+
MODEL_NAME_SUFFIX = /(Request|Input|Form|Command|Query|Payload)$/;
|
|
169446
169991
|
SCALAR_VALUE_TYPES = /* @__PURE__ */ new Set(["int", "long", "short", "byte", "uint", "ulong", "ushort", "sbyte", "decimal", "double", "float", "bool", "char"]);
|
|
169447
169992
|
csharpValueTypeActionParamUnderPostingVisitor = {
|
|
169448
169993
|
ruleKey: "architecture/deterministic/value-type-action-param-under-posting",
|
|
@@ -169454,6 +169999,8 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169454
169999
|
const body = node2.childForFieldName("body");
|
|
169455
170000
|
if (!body)
|
|
169456
170001
|
return null;
|
|
170002
|
+
if (isServerConstructed(node2, body))
|
|
170003
|
+
return null;
|
|
169457
170004
|
for (const member of body.namedChildren) {
|
|
169458
170005
|
if (member?.type !== "property_declaration")
|
|
169459
170006
|
continue;
|
|
@@ -169481,6 +170028,22 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169481
170028
|
});
|
|
169482
170029
|
|
|
169483
170030
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/action-missing-producesresponsetype.js
|
|
170031
|
+
function excludedFromApiExplorer(node2) {
|
|
170032
|
+
for (const child of node2.children) {
|
|
170033
|
+
if (child?.type !== "attribute_list")
|
|
170034
|
+
continue;
|
|
170035
|
+
for (const attr of child.namedChildren) {
|
|
170036
|
+
if (attr?.type !== "attribute")
|
|
170037
|
+
continue;
|
|
170038
|
+
const name = (attr.childForFieldName("name")?.text ?? "").split(".").pop();
|
|
170039
|
+
if (name !== "ApiExplorerSettings")
|
|
170040
|
+
continue;
|
|
170041
|
+
if (/\bIgnoreApi\s*=\s*true\b/i.test(attr.text))
|
|
170042
|
+
return true;
|
|
170043
|
+
}
|
|
170044
|
+
}
|
|
170045
|
+
return false;
|
|
170046
|
+
}
|
|
169484
170047
|
function attributeNames7(node2) {
|
|
169485
170048
|
const names = [];
|
|
169486
170049
|
for (const child of node2.children) {
|
|
@@ -169528,6 +170091,8 @@ var init_action_missing_producesresponsetype = __esm({
|
|
|
169528
170091
|
const classAttrs = attributeNames7(cls);
|
|
169529
170092
|
if (classAttrs.some((a) => a.startsWith("ProducesResponseType") || a === "ProducesDefaultResponseType"))
|
|
169530
170093
|
return null;
|
|
170094
|
+
if (excludedFromApiExplorer(node2) || excludedFromApiExplorer(cls))
|
|
170095
|
+
return null;
|
|
169531
170096
|
const name = node2.childForFieldName("name");
|
|
169532
170097
|
return makeViolation(this.ruleKey, name ?? node2, filePath, "low", "Controller action has no [ProducesResponseType]", `Action '${name?.text ?? ""}' declares no [ProducesResponseType]; its response shapes and status codes are missing from the API description.`, sourceCode, "Annotate the action with [ProducesResponseType] for each status code it can return.");
|
|
169533
170098
|
}
|
|
@@ -171470,52 +172035,96 @@ var init_pyproject_checker = __esm({
|
|
|
171470
172035
|
// packages/analyzer/dist/roslyn-host-client.js
|
|
171471
172036
|
import { spawn as spawn6 } from "child_process";
|
|
171472
172037
|
import { existsSync as existsSync9 } from "fs";
|
|
172038
|
+
import { tmpdir } from "os";
|
|
171473
172039
|
import { dirname as dirname8, resolve as resolve9 } from "path";
|
|
171474
172040
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
171475
172041
|
function resolveRoslynHostBinary() {
|
|
171476
172042
|
const override = process.env[HOST_BINARY_ENV];
|
|
171477
172043
|
if (override)
|
|
171478
172044
|
return existsSync9(override) ? override : null;
|
|
171479
|
-
const
|
|
171480
|
-
|
|
171481
|
-
|
|
171482
|
-
|
|
171483
|
-
|
|
171484
|
-
|
|
172045
|
+
const rels = [
|
|
172046
|
+
"roslyn-host/csharp-roslyn-host.dll",
|
|
172047
|
+
"tools/csharp-roslyn-host/bin/Release/net8.0/csharp-roslyn-host"
|
|
172048
|
+
];
|
|
172049
|
+
let dir = dirname8(fileURLToPath4(import.meta.url));
|
|
172050
|
+
for (let i = 0; i < 12; i++) {
|
|
172051
|
+
for (const rel of rels) {
|
|
172052
|
+
const candidate = resolve9(dir, rel);
|
|
172053
|
+
if (existsSync9(candidate))
|
|
172054
|
+
return candidate;
|
|
172055
|
+
}
|
|
172056
|
+
const parent = dirname8(dir);
|
|
172057
|
+
if (parent === dir)
|
|
172058
|
+
break;
|
|
172059
|
+
dir = parent;
|
|
171485
172060
|
}
|
|
171486
172061
|
return null;
|
|
171487
172062
|
}
|
|
171488
172063
|
function invokeHost(bin, request) {
|
|
171489
172064
|
return new Promise((resolvePromise, reject) => {
|
|
171490
|
-
const
|
|
172065
|
+
const viaDotnet = bin.endsWith(".dll");
|
|
172066
|
+
const cmd = viaDotnet ? "dotnet" : bin;
|
|
172067
|
+
const args = viaDotnet ? [bin] : [];
|
|
172068
|
+
const child = spawn6(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: tmpdir() });
|
|
171491
172069
|
let stdout = "";
|
|
171492
172070
|
let stderr = "";
|
|
172071
|
+
let settled = false;
|
|
172072
|
+
const fail = (err) => {
|
|
172073
|
+
if (settled)
|
|
172074
|
+
return;
|
|
172075
|
+
settled = true;
|
|
172076
|
+
reject(err);
|
|
172077
|
+
};
|
|
172078
|
+
const succeed = (violations) => {
|
|
172079
|
+
if (settled)
|
|
172080
|
+
return;
|
|
172081
|
+
settled = true;
|
|
172082
|
+
resolvePromise(violations);
|
|
172083
|
+
};
|
|
171493
172084
|
child.stdout.setEncoding("utf8");
|
|
171494
172085
|
child.stderr.setEncoding("utf8");
|
|
171495
172086
|
child.stdout.on("data", (d) => stdout += d);
|
|
171496
172087
|
child.stderr.on("data", (d) => stderr += d);
|
|
171497
|
-
child.on("error", (e) =>
|
|
171498
|
-
child.on("
|
|
171499
|
-
|
|
171500
|
-
|
|
171501
|
-
|
|
172088
|
+
child.on("error", (e) => fail(new RoslynHostUnavailableError(`Failed to start the Roslyn host (${viaDotnet ? `dotnet ${bin}` : bin}): ${e.message}. Is the .NET runtime installed?`)));
|
|
172089
|
+
child.stdin.on("error", () => {
|
|
172090
|
+
});
|
|
172091
|
+
child.on("close", (code, signalName) => {
|
|
172092
|
+
if (code !== 0 || signalName) {
|
|
172093
|
+
const reason = stderr.trim() || stdout.trim() || "(no output)";
|
|
172094
|
+
fail(new RoslynHostUnavailableError(`The Roslyn host exited ${signalName ? `via ${signalName}` : `with code ${code}`} before responding. Is a compatible .NET runtime installed? Host output: ${reason}`));
|
|
172095
|
+
return;
|
|
172096
|
+
}
|
|
172097
|
+
const lines = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
172098
|
+
if (lines.length === 0) {
|
|
172099
|
+
fail(new Error(`Roslyn host produced no output.${stderr ? ` stderr: ${stderr}` : ""}`));
|
|
171502
172100
|
return;
|
|
171503
172101
|
}
|
|
171504
172102
|
let resp;
|
|
171505
|
-
|
|
171506
|
-
|
|
171507
|
-
|
|
171508
|
-
|
|
172103
|
+
for (const l of lines) {
|
|
172104
|
+
try {
|
|
172105
|
+
const parsed = JSON.parse(l);
|
|
172106
|
+
if (parsed && typeof parsed.ok === "boolean") {
|
|
172107
|
+
resp = parsed;
|
|
172108
|
+
break;
|
|
172109
|
+
}
|
|
172110
|
+
} catch {
|
|
172111
|
+
}
|
|
172112
|
+
}
|
|
172113
|
+
if (!resp) {
|
|
172114
|
+
fail(new Error(`Roslyn host returned invalid JSON: ${lines[0]}`));
|
|
171509
172115
|
return;
|
|
171510
172116
|
}
|
|
171511
172117
|
if (!resp.ok) {
|
|
171512
|
-
|
|
172118
|
+
fail(new Error(`Roslyn host error: ${resp.error ?? "unknown"}`));
|
|
171513
172119
|
return;
|
|
171514
172120
|
}
|
|
171515
|
-
|
|
172121
|
+
succeed(resp.violations ?? []);
|
|
171516
172122
|
});
|
|
171517
|
-
|
|
171518
|
-
|
|
172123
|
+
try {
|
|
172124
|
+
child.stdin.write(JSON.stringify(request) + "\n");
|
|
172125
|
+
child.stdin.end();
|
|
172126
|
+
} catch {
|
|
172127
|
+
}
|
|
171519
172128
|
});
|
|
171520
172129
|
}
|
|
171521
172130
|
function runRoslynWorkspace(projectPath, rules) {
|
|
@@ -178074,7 +178683,7 @@ var init_schemas2 = __esm({
|
|
|
178074
178683
|
// packages/core/dist/services/llm/cli-provider.js
|
|
178075
178684
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
178076
178685
|
import { join as join10 } from "node:path";
|
|
178077
|
-
import { tmpdir } from "node:os";
|
|
178686
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
178078
178687
|
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
178079
178688
|
var import_cross_spawn, BaseCLIProvider, ClaudeCodeProvider;
|
|
178080
178689
|
var init_cli_provider = __esm({
|
|
@@ -178144,7 +178753,7 @@ var init_cli_provider = __esm({
|
|
|
178144
178753
|
constructor(transport) {
|
|
178145
178754
|
this.transport = transport;
|
|
178146
178755
|
if (process.env.TRUECOURSE_CLI_DEBUG) {
|
|
178147
|
-
this.debugDir = join10(
|
|
178756
|
+
this.debugDir = join10(tmpdir2(), "truecourse-cli-debug");
|
|
178148
178757
|
mkdirSync(this.debugDir, { recursive: true });
|
|
178149
178758
|
log.info(`[CLI] Debug output: ${this.debugDir}`);
|
|
178150
178759
|
}
|
|
@@ -178257,8 +178866,13 @@ var init_cli_provider = __esm({
|
|
|
178257
178866
|
unregisterChildProcess(this._repoId, child);
|
|
178258
178867
|
reject(new Error(`[CLI] Failed to spawn ${this.binaryName}: ${err.message}`));
|
|
178259
178868
|
});
|
|
178260
|
-
child.stdin.
|
|
178261
|
-
|
|
178869
|
+
child.stdin.on("error", () => {
|
|
178870
|
+
});
|
|
178871
|
+
try {
|
|
178872
|
+
child.stdin.write(prompt);
|
|
178873
|
+
child.stdin.end();
|
|
178874
|
+
} catch {
|
|
178875
|
+
}
|
|
178262
178876
|
});
|
|
178263
178877
|
}
|
|
178264
178878
|
/** Extract usage data from CLI JSON envelope if present. */
|
|
@@ -186525,7 +187139,7 @@ var DOMAIN_LABELS = {
|
|
|
186525
187139
|
"database": "Database",
|
|
186526
187140
|
"style": "Style"
|
|
186527
187141
|
};
|
|
186528
|
-
function buildAnalysisSteps(enabledCategories, enableLlmRules) {
|
|
187142
|
+
function buildAnalysisSteps(enabledCategories, enableLlmRules, hasCSharp) {
|
|
186529
187143
|
const steps = [
|
|
186530
187144
|
{ key: "parse", label: "Parsing repository" }
|
|
186531
187145
|
];
|
|
@@ -186536,6 +187150,9 @@ function buildAnalysisSteps(enabledCategories, enableLlmRules) {
|
|
|
186536
187150
|
for (const domain of activeDomains) {
|
|
186537
187151
|
steps.push({ key: domain, label: `${DOMAIN_LABELS[domain]} checks` });
|
|
186538
187152
|
}
|
|
187153
|
+
if (hasCSharp) {
|
|
187154
|
+
steps.push({ key: "csharp", label: "C# semantic analysis" });
|
|
187155
|
+
}
|
|
186539
187156
|
steps.push({ key: "persist", label: "Saving results" });
|
|
186540
187157
|
return steps;
|
|
186541
187158
|
}
|
|
@@ -186562,6 +187179,23 @@ var StepTracker = class {
|
|
|
186562
187179
|
this.emit();
|
|
186563
187180
|
}
|
|
186564
187181
|
}
|
|
187182
|
+
/**
|
|
187183
|
+
* Insert a step at runtime, just before `persist`, if it isn't already there.
|
|
187184
|
+
* Used for phases we can't declare up front — e.g. the C# semantic tier only
|
|
187185
|
+
* exists when the repo actually has C#, which isn't known until files are read.
|
|
187186
|
+
* No-op if the key already exists.
|
|
187187
|
+
*/
|
|
187188
|
+
ensureStep(key4, label) {
|
|
187189
|
+
if (this.steps.some((s) => s.key === key4))
|
|
187190
|
+
return;
|
|
187191
|
+
const persistIdx = this.steps.findIndex((s) => s.key === "persist");
|
|
187192
|
+
const step = { key: key4, label, status: "pending" };
|
|
187193
|
+
if (persistIdx === -1)
|
|
187194
|
+
this.steps.push(step);
|
|
187195
|
+
else
|
|
187196
|
+
this.steps.splice(persistIdx, 0, step);
|
|
187197
|
+
this.emit();
|
|
187198
|
+
}
|
|
186565
187199
|
setStatus(key4, status, detail) {
|
|
186566
187200
|
const step = this.steps.find((s) => s.key === key4);
|
|
186567
187201
|
if (step) {
|
|
@@ -195238,7 +195872,7 @@ import path17 from "node:path";
|
|
|
195238
195872
|
function writeEvidence(params) {
|
|
195239
195873
|
const dir = evidenceScenarioDir(params.repoRoot, params.runId, params.scenarioId);
|
|
195240
195874
|
fs18.mkdirSync(dir, { recursive: true });
|
|
195241
|
-
const
|
|
195875
|
+
const focus = params.failingStep != null ? params.steps.find((s) => s.index === params.failingStep) : params.steps[params.steps.length - 1];
|
|
195242
195876
|
const invocation = {
|
|
195243
195877
|
scenarioId: params.scenarioId,
|
|
195244
195878
|
title: params.title,
|
|
@@ -195258,11 +195892,11 @@ function writeEvidence(params) {
|
|
|
195258
195892
|
}))
|
|
195259
195893
|
};
|
|
195260
195894
|
writeFile(dir, "invocation.json", JSON.stringify(invocation, null, 2));
|
|
195261
|
-
if (
|
|
195262
|
-
writeFile(dir, "stdout.raw.txt",
|
|
195263
|
-
writeFile(dir, "stdout.txt",
|
|
195264
|
-
writeFile(dir, "stderr.raw.txt",
|
|
195265
|
-
writeFile(dir, "stderr.txt",
|
|
195895
|
+
if (focus) {
|
|
195896
|
+
writeFile(dir, "stdout.raw.txt", focus.rawStdout);
|
|
195897
|
+
writeFile(dir, "stdout.txt", focus.normStdout);
|
|
195898
|
+
writeFile(dir, "stderr.raw.txt", focus.rawStderr);
|
|
195899
|
+
writeFile(dir, "stderr.txt", focus.normStderr);
|
|
195266
195900
|
}
|
|
195267
195901
|
const diffLines = [];
|
|
195268
195902
|
if (params.outcome === "fail" && params.mismatch) {
|
|
@@ -195270,8 +195904,10 @@ function writeEvidence(params) {
|
|
|
195270
195904
|
diffLines.push(`expected: ${params.mismatch.expected}`);
|
|
195271
195905
|
diffLines.push(`actual: ${params.mismatch.actual}`, "");
|
|
195272
195906
|
diffLines.push(...params.mismatch.detail);
|
|
195273
|
-
} else if (params.infraMessage) {
|
|
195907
|
+
} else if (params.outcome === "error" && params.infraMessage) {
|
|
195274
195908
|
diffLines.push(`step ${params.failingStep} \u2014 infrastructure error`, "", params.infraMessage);
|
|
195909
|
+
} else if (params.outcome === "pass") {
|
|
195910
|
+
diffLines.push(`all ${params.steps.length} step${params.steps.length === 1 ? "" : "s"} met their expectations`);
|
|
195275
195911
|
}
|
|
195276
195912
|
writeFile(dir, "diff.txt", diffLines.join("\n") + "\n");
|
|
195277
195913
|
writeFile(dir, "files.txt", listSandboxFiles(params.sandboxCwd).join("\n") + "\n");
|
|
@@ -195385,7 +196021,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195385
196021
|
if (capture.spawnError || capture.timedOut) {
|
|
195386
196022
|
const infra = capture.timedOut ? `step timed out after ${ctx.stepTimeoutMs}ms` : `failed to spawn: ${capture.spawnError}`;
|
|
195387
196023
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
|
|
195388
|
-
const
|
|
196024
|
+
const evidencePath2 = writeEvidence({
|
|
195389
196025
|
repoRoot: ctx.repoRoot,
|
|
195390
196026
|
runId: ctx.runId,
|
|
195391
196027
|
scenarioId: scenario.id,
|
|
@@ -195403,7 +196039,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195403
196039
|
outcome: "error",
|
|
195404
196040
|
durationMs: Date.now() - start,
|
|
195405
196041
|
failure: { step: stepIndex, expected: "the step to run", actual: infra },
|
|
195406
|
-
evidencePath
|
|
196042
|
+
evidencePath: evidencePath2
|
|
195407
196043
|
};
|
|
195408
196044
|
}
|
|
195409
196045
|
const normStdout = normText(capture.stdout);
|
|
@@ -195418,7 +196054,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195418
196054
|
});
|
|
195419
196055
|
if (mismatch) {
|
|
195420
196056
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
|
|
195421
|
-
const
|
|
196057
|
+
const evidencePath2 = writeEvidence({
|
|
195422
196058
|
repoRoot: ctx.repoRoot,
|
|
195423
196059
|
runId: ctx.runId,
|
|
195424
196060
|
scenarioId: scenario.id,
|
|
@@ -195440,7 +196076,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195440
196076
|
expected: mismatch.expected,
|
|
195441
196077
|
actual: mismatch.actual
|
|
195442
196078
|
},
|
|
195443
|
-
evidencePath
|
|
196079
|
+
evidencePath: evidencePath2
|
|
195444
196080
|
};
|
|
195445
196081
|
}
|
|
195446
196082
|
}
|
|
@@ -195448,7 +196084,18 @@ async function runScenario(scenario, ctx) {
|
|
|
195448
196084
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, repeat3, lastCapture, normText));
|
|
195449
196085
|
}
|
|
195450
196086
|
}
|
|
195451
|
-
|
|
196087
|
+
const evidencePath = ctx.capturePassEvidence ? writeEvidence({
|
|
196088
|
+
repoRoot: ctx.repoRoot,
|
|
196089
|
+
runId: ctx.runId,
|
|
196090
|
+
scenarioId: scenario.id,
|
|
196091
|
+
title: scenario.title,
|
|
196092
|
+
binds: scenario.binds,
|
|
196093
|
+
outcome: "pass",
|
|
196094
|
+
steps: records,
|
|
196095
|
+
sandboxCwd: sandbox.cwd,
|
|
196096
|
+
envPins: ENV_PINS
|
|
196097
|
+
}) : void 0;
|
|
196098
|
+
return { ...base, outcome: "pass", durationMs: Date.now() - start, ...evidencePath ? { evidencePath } : {} };
|
|
195452
196099
|
} finally {
|
|
195453
196100
|
sandbox.cleanup();
|
|
195454
196101
|
}
|
|
@@ -195741,13 +196388,15 @@ async function runGuard(opts) {
|
|
|
195741
196388
|
settled += 1;
|
|
195742
196389
|
opts.onScenarioSettled?.(settled, selected.length, result);
|
|
195743
196390
|
}
|
|
196391
|
+
const capturePassEvidence = opts.persist !== false;
|
|
195744
196392
|
const executed = await mapWithConcurrency(executable, concurrency, async ({ scenario, resolution }) => {
|
|
195745
196393
|
const outcome = await runScenario(scenario, {
|
|
195746
196394
|
repoRoot,
|
|
195747
196395
|
runId,
|
|
195748
196396
|
resolvedEntry,
|
|
195749
196397
|
recipeEnv: loaded.recipe.env,
|
|
195750
|
-
stepTimeoutMs
|
|
196398
|
+
stepTimeoutMs,
|
|
196399
|
+
capturePassEvidence
|
|
195751
196400
|
});
|
|
195752
196401
|
const result = resolution.kind === "remap" ? { ...outcome, remappedTo: resolution.section.anchor } : outcome;
|
|
195753
196402
|
settled += 1;
|
|
@@ -205321,7 +205970,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
205321
205970
|
"obj",
|
|
205322
205971
|
".vs"
|
|
205323
205972
|
]);
|
|
205324
|
-
function
|
|
205973
|
+
function isTestFile3(filePath) {
|
|
205325
205974
|
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
205326
205975
|
const fileName = parts[parts.length - 1] ?? "";
|
|
205327
205976
|
if (/\.(test|spec)\.[mc]?[jt]sx?$/.test(fileName))
|
|
@@ -205355,7 +206004,7 @@ async function eachParsedSource(rootDir, visit) {
|
|
|
205355
206004
|
}
|
|
205356
206005
|
if (!entry.isFile())
|
|
205357
206006
|
continue;
|
|
205358
|
-
if (
|
|
206007
|
+
if (isTestFile3(full))
|
|
205359
206008
|
continue;
|
|
205360
206009
|
const lang = EXT_TO_LANG[path33.extname(entry.name)];
|
|
205361
206010
|
if (!lang)
|
|
@@ -205435,19 +206084,19 @@ function readCsharpString(node2, source) {
|
|
|
205435
206084
|
}
|
|
205436
206085
|
if (node2.type === "interpolated_string_expression") {
|
|
205437
206086
|
let text4 = "";
|
|
205438
|
-
let
|
|
206087
|
+
let hasInterpolation = false;
|
|
205439
206088
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
205440
206089
|
const c = node2.namedChild(i);
|
|
205441
206090
|
if (!c)
|
|
205442
206091
|
continue;
|
|
205443
206092
|
if (c.type === "interpolation") {
|
|
205444
206093
|
text4 += " /*INTERP*/ ";
|
|
205445
|
-
|
|
206094
|
+
hasInterpolation = true;
|
|
205446
206095
|
} else {
|
|
205447
206096
|
text4 += source.slice(c.startIndex, c.endIndex);
|
|
205448
206097
|
}
|
|
205449
206098
|
}
|
|
205450
|
-
return { text: text4, hasInterpolation
|
|
206099
|
+
return { text: text4, hasInterpolation };
|
|
205451
206100
|
}
|
|
205452
206101
|
if (node2.type === "binary_expression") {
|
|
205453
206102
|
const op = node2.childForFieldName("operator");
|
|
@@ -209370,14 +210019,14 @@ function getSqlText(node2, source) {
|
|
|
209370
210019
|
return { text: source.slice(node2.startIndex, node2.endIndex), hasInterpolation: false };
|
|
209371
210020
|
}
|
|
209372
210021
|
function extractFromSqlSource(sqlNode, filePath, source) {
|
|
209373
|
-
const { text: text4, hasInterpolation
|
|
209374
|
-
return buildQueriesFromSqlText(text4,
|
|
210022
|
+
const { text: text4, hasInterpolation } = getSqlText(sqlNode, source);
|
|
210023
|
+
return buildQueriesFromSqlText(text4, hasInterpolation, {
|
|
209375
210024
|
filePath,
|
|
209376
210025
|
lineStart: sqlNode.startPosition.row + 1,
|
|
209377
210026
|
lineEnd: sqlNode.endPosition.row + 1
|
|
209378
210027
|
});
|
|
209379
210028
|
}
|
|
209380
|
-
function buildQueriesFromSqlText(text4,
|
|
210029
|
+
function buildQueriesFromSqlText(text4, hasInterpolation, loc, adapterName = "raw-sql") {
|
|
209381
210030
|
if (!text4 || !/\bSELECT\b/i.test(text4))
|
|
209382
210031
|
return [];
|
|
209383
210032
|
const blocks = splitSelectBlocks(text4);
|
|
@@ -209396,7 +210045,7 @@ function buildQueriesFromSqlText(text4, hasInterpolation2, loc, adapterName = "r
|
|
|
209396
210045
|
else
|
|
209397
210046
|
unparseable.push({ reason: "unrecognised SQL predicate", raw: piece });
|
|
209398
210047
|
}
|
|
209399
|
-
if (
|
|
210048
|
+
if (hasInterpolation) {
|
|
209400
210049
|
unparseable.push({ reason: "string-interpolation substitution skipped", raw: "${...}" });
|
|
209401
210050
|
}
|
|
209402
210051
|
out.push({
|
|
@@ -209761,7 +210410,7 @@ function pySqlText(node2, source) {
|
|
|
209761
210410
|
function readPyStringNode(node2, source) {
|
|
209762
210411
|
if (node2.type === "concatenated_string") {
|
|
209763
210412
|
let text5 = "";
|
|
209764
|
-
let
|
|
210413
|
+
let hasInterpolation2 = false;
|
|
209765
210414
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
209766
210415
|
const c = node2.namedChild(i);
|
|
209767
210416
|
if (!c)
|
|
@@ -209769,15 +210418,15 @@ function readPyStringNode(node2, source) {
|
|
|
209769
210418
|
const part = readPyStringNode(c, source);
|
|
209770
210419
|
if (part) {
|
|
209771
210420
|
text5 += part.text;
|
|
209772
|
-
|
|
210421
|
+
hasInterpolation2 = hasInterpolation2 || part.hasInterpolation;
|
|
209773
210422
|
}
|
|
209774
210423
|
}
|
|
209775
|
-
return { text: text5, hasInterpolation:
|
|
210424
|
+
return { text: text5, hasInterpolation: hasInterpolation2 };
|
|
209776
210425
|
}
|
|
209777
210426
|
if (node2.type !== "string")
|
|
209778
210427
|
return null;
|
|
209779
210428
|
let text4 = "";
|
|
209780
|
-
let
|
|
210429
|
+
let hasInterpolation = false;
|
|
209781
210430
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
209782
210431
|
const c = node2.namedChild(i);
|
|
209783
210432
|
if (!c)
|
|
@@ -209786,10 +210435,10 @@ function readPyStringNode(node2, source) {
|
|
|
209786
210435
|
text4 += source.slice(c.startIndex, c.endIndex);
|
|
209787
210436
|
else if (c.type === "interpolation") {
|
|
209788
210437
|
text4 += " /*INTERP*/ ";
|
|
209789
|
-
|
|
210438
|
+
hasInterpolation = true;
|
|
209790
210439
|
}
|
|
209791
210440
|
}
|
|
209792
|
-
return { text: text4, hasInterpolation
|
|
210441
|
+
return { text: text4, hasInterpolation };
|
|
209793
210442
|
}
|
|
209794
210443
|
|
|
209795
210444
|
// packages/contract-verifier/dist/extractor/query/shared/python-orm.js
|
|
@@ -216899,7 +217548,7 @@ async function buildCodebaseScan(codeDir) {
|
|
|
216899
217548
|
|
|
216900
217549
|
// packages/contract-verifier/dist/extractor/index.js
|
|
216901
217550
|
var TS_EXT4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
216902
|
-
function
|
|
217551
|
+
function isTestFile4(filePath) {
|
|
216903
217552
|
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
216904
217553
|
const fileName = parts[parts.length - 1];
|
|
216905
217554
|
if (/\.(test|spec)\.(t|j)sx?$/.test(fileName))
|
|
@@ -216929,7 +217578,7 @@ async function extractOperationsFromDir(rootDir) {
|
|
|
216929
217578
|
const ext2 = path44.extname(entry.name);
|
|
216930
217579
|
if (!TS_EXT4.has(ext2))
|
|
216931
217580
|
continue;
|
|
216932
|
-
if (
|
|
217581
|
+
if (isTestFile4(full))
|
|
216933
217582
|
continue;
|
|
216934
217583
|
const source = fs43.readFileSync(full, "utf-8");
|
|
216935
217584
|
const lang = ext2 === ".ts" || ext2 === ".tsx" ? ext2 === ".tsx" ? "tsx" : "typescript" : "javascript";
|
|
@@ -226618,7 +227267,7 @@ var PROMPT_OVERHEAD_TOKENS = 500;
|
|
|
226618
227267
|
var TOKENS_PER_RULE = 50;
|
|
226619
227268
|
var TOKENS_PER_FILE_PATH = 25;
|
|
226620
227269
|
var MAX_CHARS_PER_BATCH = 1e5;
|
|
226621
|
-
function
|
|
227270
|
+
function isTestFile5(filePath) {
|
|
226622
227271
|
return TEST_PATTERNS.some((p) => filePath.includes(p));
|
|
226623
227272
|
}
|
|
226624
227273
|
function matchesFileFilter(fa, filter2, fileContent) {
|
|
@@ -226653,7 +227302,7 @@ function matchesFileFilter(fa, filter2, fileContent) {
|
|
|
226653
227302
|
return false;
|
|
226654
227303
|
}
|
|
226655
227304
|
if (filter2.isTestFile !== void 0) {
|
|
226656
|
-
if (filter2.isTestFile !==
|
|
227305
|
+
if (filter2.isTestFile !== isTestFile5(fa.filePath))
|
|
226657
227306
|
return false;
|
|
226658
227307
|
}
|
|
226659
227308
|
if (filter2.languages) {
|
|
@@ -231405,21 +232054,21 @@ function node(getModule) {
|
|
|
231405
232054
|
if (lineMatch) {
|
|
231406
232055
|
let object;
|
|
231407
232056
|
let method;
|
|
231408
|
-
let
|
|
232057
|
+
let functionName2;
|
|
231409
232058
|
let typeName2;
|
|
231410
232059
|
let methodName3;
|
|
231411
232060
|
if (lineMatch[1]) {
|
|
231412
|
-
|
|
231413
|
-
let methodStart =
|
|
231414
|
-
if (
|
|
232061
|
+
functionName2 = lineMatch[1];
|
|
232062
|
+
let methodStart = functionName2.lastIndexOf(".");
|
|
232063
|
+
if (functionName2[methodStart - 1] === ".") {
|
|
231415
232064
|
methodStart--;
|
|
231416
232065
|
}
|
|
231417
232066
|
if (methodStart > 0) {
|
|
231418
|
-
object =
|
|
231419
|
-
method =
|
|
232067
|
+
object = functionName2.slice(0, methodStart);
|
|
232068
|
+
method = functionName2.slice(methodStart + 1);
|
|
231420
232069
|
const objectEnd = object.indexOf(".Module");
|
|
231421
232070
|
if (objectEnd > 0) {
|
|
231422
|
-
|
|
232071
|
+
functionName2 = functionName2.slice(objectEnd + 1);
|
|
231423
232072
|
object = object.slice(0, objectEnd);
|
|
231424
232073
|
}
|
|
231425
232074
|
}
|
|
@@ -231431,11 +232080,11 @@ function node(getModule) {
|
|
|
231431
232080
|
}
|
|
231432
232081
|
if (method === "<anonymous>") {
|
|
231433
232082
|
methodName3 = void 0;
|
|
231434
|
-
|
|
232083
|
+
functionName2 = void 0;
|
|
231435
232084
|
}
|
|
231436
|
-
if (
|
|
232085
|
+
if (functionName2 === void 0) {
|
|
231437
232086
|
methodName3 = methodName3 || UNKNOWN_FUNCTION;
|
|
231438
|
-
|
|
232087
|
+
functionName2 = typeName2 ? `${typeName2}.${methodName3}` : methodName3;
|
|
231439
232088
|
}
|
|
231440
232089
|
let filename = lineMatch[2]?.startsWith("file://") ? lineMatch[2].slice(7) : lineMatch[2];
|
|
231441
232090
|
const isNative = lineMatch[5] === "native";
|
|
@@ -231448,7 +232097,7 @@ function node(getModule) {
|
|
|
231448
232097
|
return {
|
|
231449
232098
|
filename: filename ? decodeURI(filename) : void 0,
|
|
231450
232099
|
module: getModule ? getModule(filename) : void 0,
|
|
231451
|
-
function:
|
|
232100
|
+
function: functionName2,
|
|
231452
232101
|
lineno: _parseIntOrUndefined(lineMatch[3]),
|
|
231453
232102
|
colno: _parseIntOrUndefined(lineMatch[4]),
|
|
231454
232103
|
in_app: filenameIsInApp(filename || "", isNative),
|
|
@@ -231645,7 +232294,7 @@ function readToolVersion() {
|
|
|
231645
232294
|
if (cachedVersion)
|
|
231646
232295
|
return cachedVersion;
|
|
231647
232296
|
if (true) {
|
|
231648
|
-
cachedVersion = "0.7.0-next.
|
|
232297
|
+
cachedVersion = "0.7.0-next.5";
|
|
231649
232298
|
return cachedVersion;
|
|
231650
232299
|
}
|
|
231651
232300
|
try {
|
|
@@ -234043,7 +234692,7 @@ function findCSharpProjectFiles(repoPath) {
|
|
|
234043
234692
|
continue;
|
|
234044
234693
|
walk20(path63.join(dir, e.name), depth + 1);
|
|
234045
234694
|
} else if (e.isFile()) {
|
|
234046
|
-
if (e.name.endsWith(".sln"))
|
|
234695
|
+
if (e.name.endsWith(".sln") || e.name.endsWith(".slnx"))
|
|
234047
234696
|
solutions.push(path63.join(dir, e.name));
|
|
234048
234697
|
else if (e.name.endsWith(".csproj"))
|
|
234049
234698
|
projects.push(path63.join(dir, e.name));
|
|
@@ -234306,6 +234955,29 @@ async function runViolationPipeline(input) {
|
|
|
234306
234955
|
}
|
|
234307
234956
|
}
|
|
234308
234957
|
}
|
|
234958
|
+
{
|
|
234959
|
+
const detByDomain = /* @__PURE__ */ new Map();
|
|
234960
|
+
for (const v of allCodeViolations) {
|
|
234961
|
+
const d = v.ruleKey.split("/")[0];
|
|
234962
|
+
detByDomain.set(d, (detByDomain.get(d) ?? 0) + 1);
|
|
234963
|
+
}
|
|
234964
|
+
const archDet = serviceViolationResults.length + moduleViolationResults.length + methodViolationResults.length;
|
|
234965
|
+
if (archDet > 0)
|
|
234966
|
+
detByDomain.set("architecture", (detByDomain.get("architecture") ?? 0) + archDet);
|
|
234967
|
+
for (const domain of DOMAIN_ORDER) {
|
|
234968
|
+
const count = detByDomain.get(domain) ?? 0;
|
|
234969
|
+
tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
|
|
234970
|
+
}
|
|
234971
|
+
}
|
|
234972
|
+
const detViolationCount = allCodeViolations.length;
|
|
234973
|
+
let csharpStepActive = false;
|
|
234974
|
+
const startCsharpStep = (detail) => {
|
|
234975
|
+
if (!csharpStepActive) {
|
|
234976
|
+
tracker?.ensureStep("csharp", "C# semantic analysis");
|
|
234977
|
+
csharpStepActive = true;
|
|
234978
|
+
}
|
|
234979
|
+
tracker?.start("csharp", detail);
|
|
234980
|
+
};
|
|
234309
234981
|
const enabledHostRules = enabledCodeRules.filter((r) => r.engine === "roslyn-host");
|
|
234310
234982
|
if (enabledHostRules.length > 0) {
|
|
234311
234983
|
const csharpFiles = [];
|
|
@@ -234319,6 +234991,7 @@ async function runViolationPipeline(input) {
|
|
|
234319
234991
|
csharpFiles.push({ path: key4, text: fc.content });
|
|
234320
234992
|
}
|
|
234321
234993
|
if (csharpFiles.length > 0) {
|
|
234994
|
+
startCsharpStep(`Analyzing ${csharpFiles.length} C# files\u2026`);
|
|
234322
234995
|
const ruleByKey = new Map(enabledHostRules.map((r) => [r.key, r]));
|
|
234323
234996
|
const hostViolations = await runRoslynHost(csharpFiles, enabledHostRules.map((r) => r.key));
|
|
234324
234997
|
for (const v of hostViolations) {
|
|
@@ -234345,19 +235018,20 @@ async function runViolationPipeline(input) {
|
|
|
234345
235018
|
if (enabledWorkspaceRules.length > 0 && filesToScan.some((f2) => detectLanguage(f2.filePath) === "csharp")) {
|
|
234346
235019
|
const projectFiles = findCSharpProjectFiles(repoPath);
|
|
234347
235020
|
if (projectFiles.length === 0) {
|
|
234348
|
-
log.info("[Pipeline] C# present but no .csproj/.sln found \u2014 project-aware C# rules skipped");
|
|
235021
|
+
log.info("[Pipeline] C# present but no .csproj/.sln/.slnx found \u2014 project-aware C# rules skipped");
|
|
234349
235022
|
} else {
|
|
234350
235023
|
const ruleByKey = new Map(enabledWorkspaceRules.map((r) => [r.key, r]));
|
|
234351
235024
|
const ruleKeys = enabledWorkspaceRules.map((r) => r.key);
|
|
234352
|
-
for (const projectFile of projectFiles) {
|
|
235025
|
+
for (const [pi, projectFile] of projectFiles.entries()) {
|
|
235026
|
+
startCsharpStep(projectFiles.length > 1 ? `Loading project ${pi + 1}/${projectFiles.length}: ${path63.basename(projectFile)}\u2026` : `Loading project ${path63.basename(projectFile)}\u2026`);
|
|
234353
235027
|
let wsViolations;
|
|
234354
235028
|
try {
|
|
234355
235029
|
wsViolations = await runRoslynWorkspace(projectFile, ruleKeys);
|
|
234356
235030
|
} catch (err) {
|
|
235031
|
+
tracker?.error("csharp", `Project load failed: ${path63.basename(projectFile)}`);
|
|
234357
235032
|
if (err instanceof RoslynHostUnavailableError)
|
|
234358
235033
|
throw err;
|
|
234359
|
-
|
|
234360
|
-
continue;
|
|
235034
|
+
throw new Error(`Project-aware C# analysis failed for ${path63.basename(projectFile)}: ${err.message}. Restore the project (\`dotnet restore\`) and ensure its pinned .NET SDK is installed.`);
|
|
234361
235035
|
}
|
|
234362
235036
|
for (const v of wsViolations) {
|
|
234363
235037
|
const rule = ruleByKey.get(v.ruleKey);
|
|
@@ -234389,6 +235063,10 @@ async function runViolationPipeline(input) {
|
|
|
234389
235063
|
}
|
|
234390
235064
|
}
|
|
234391
235065
|
}
|
|
235066
|
+
if (csharpStepActive) {
|
|
235067
|
+
const csharpFindings = allCodeViolations.length - detViolationCount;
|
|
235068
|
+
tracker?.done("csharp", csharpFindings > 0 ? `${csharpFindings} violations` : "Clean");
|
|
235069
|
+
}
|
|
234392
235070
|
log.info(`[Pipeline] Code scan: ${allCodeViolations.length} violations from ${filesToScan.length} files (${enabledCodeRules.length} det rules, ${enabledLlmCodeRules.length} LLM rules)`);
|
|
234393
235071
|
if (enabledCodeRules.some((r) => r.key === "bugs/deterministic/invalid-pyproject-toml")) {
|
|
234394
235072
|
const pyprojectPath = path63.join(repoPath, "pyproject.toml");
|
|
@@ -234434,10 +235112,6 @@ async function runViolationPipeline(input) {
|
|
|
234434
235112
|
}
|
|
234435
235113
|
const totalDet = [...violationsByDomain.values()].reduce((a, b) => a + b, 0);
|
|
234436
235114
|
log.info(`[Pipeline] Totals: ${DOMAIN_ORDER.filter((d) => violationsByDomain.has(d)).map((d) => `${d}=${violationsByDomain.get(d)}`).join(", ")} (${totalDet})`);
|
|
234437
|
-
for (const domain of DOMAIN_ORDER) {
|
|
234438
|
-
const count = violationsByDomain.get(domain) ?? 0;
|
|
234439
|
-
tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
|
|
234440
|
-
}
|
|
234441
235115
|
onProgress?.({ step: "analyzing", percent: 84, detail: "Code checks done" });
|
|
234442
235116
|
const prevLlmCodeByFile = /* @__PURE__ */ new Map();
|
|
234443
235117
|
for (const cv of previousActiveCodeViolations) {
|