cognium-dev 3.135.0 → 3.137.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +851 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -22616,6 +22616,9 @@ class LanguageSourcesPass {
|
|
|
22616
22616
|
for (const finding of findGoMongoNosqlInjectionFindings(code, graph.ir.meta.file)) {
|
|
22617
22617
|
ctx.addFinding(finding);
|
|
22618
22618
|
}
|
|
22619
|
+
for (const finding of findGoLdapInjectionFindings(code, graph.ir.meta.file)) {
|
|
22620
|
+
ctx.addFinding(finding);
|
|
22621
|
+
}
|
|
22619
22622
|
}
|
|
22620
22623
|
if (language === "python") {
|
|
22621
22624
|
additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
|
|
@@ -22643,6 +22646,12 @@ class LanguageSourcesPass {
|
|
|
22643
22646
|
for (const finding of findPythonMongoengineWhereNosqlInjectionFindings(code, graph.ir.meta.file)) {
|
|
22644
22647
|
ctx.addFinding(finding);
|
|
22645
22648
|
}
|
|
22649
|
+
for (const finding of findPythonTaintedFormatStringFindings(code, graph.ir.meta.file)) {
|
|
22650
|
+
ctx.addFinding(finding);
|
|
22651
|
+
}
|
|
22652
|
+
for (const finding of findPythonHeaderCrlfInjectionFindings(code, graph.ir.meta.file)) {
|
|
22653
|
+
ctx.addFinding(finding);
|
|
22654
|
+
}
|
|
22646
22655
|
}
|
|
22647
22656
|
if (language === "rust") {
|
|
22648
22657
|
additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
|
|
@@ -22670,6 +22679,12 @@ class LanguageSourcesPass {
|
|
|
22670
22679
|
for (const finding of findRustEvalCrateCodeInjectionFindings(code, graph.ir.meta.file)) {
|
|
22671
22680
|
ctx.addFinding(finding);
|
|
22672
22681
|
}
|
|
22682
|
+
for (const finding of findRustLdapInjectionFindings(code, graph.ir.meta.file)) {
|
|
22683
|
+
ctx.addFinding(finding);
|
|
22684
|
+
}
|
|
22685
|
+
for (const finding of findRustLogInjectionFindings(code, graph.ir.meta.file)) {
|
|
22686
|
+
ctx.addFinding(finding);
|
|
22687
|
+
}
|
|
22673
22688
|
}
|
|
22674
22689
|
if (language === "javascript" || language === "typescript" || language === "htmljs") {
|
|
22675
22690
|
additionalSanitizers.push(...findJsSafeJsonParseSanitizers(code));
|
|
@@ -22694,6 +22709,12 @@ class LanguageSourcesPass {
|
|
|
22694
22709
|
for (const finding of findJsIndirectEvalCodeInjectionFindings(code, graph.ir.meta.file)) {
|
|
22695
22710
|
ctx.addFinding(finding);
|
|
22696
22711
|
}
|
|
22712
|
+
for (const finding of findJsUtilFormatFormatStringFindings(code, graph.ir.meta.file)) {
|
|
22713
|
+
ctx.addFinding(finding);
|
|
22714
|
+
}
|
|
22715
|
+
for (const finding of findJsLdapInjectionFindings(code, graph.ir.meta.file)) {
|
|
22716
|
+
ctx.addFinding(finding);
|
|
22717
|
+
}
|
|
22697
22718
|
}
|
|
22698
22719
|
if (language === "java") {
|
|
22699
22720
|
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
@@ -27036,6 +27057,835 @@ function findJavaUrlOpenStreamSsrfFindings(code, file) {
|
|
|
27036
27057
|
}
|
|
27037
27058
|
return findings;
|
|
27038
27059
|
}
|
|
27060
|
+
function findPythonTaintedFormatStringFindings(code, file) {
|
|
27061
|
+
const findings = [];
|
|
27062
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27063
|
+
return findings;
|
|
27064
|
+
if (!/\b(?:request\.(?:args|form|values|json|files|cookies|headers|data)|flask\.request)\b/.test(code)) {
|
|
27065
|
+
return findings;
|
|
27066
|
+
}
|
|
27067
|
+
const lines = code.split(`
|
|
27068
|
+
`);
|
|
27069
|
+
const reqExtractRe = /\b(?:request\.(?:args|form|values|json|files|cookies|headers|data)\b|flask\.request\b)/;
|
|
27070
|
+
const taintedVars = new Set;
|
|
27071
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27072
|
+
const before = taintedVars.size;
|
|
27073
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27074
|
+
const t = lines[i2].trim();
|
|
27075
|
+
if (t.startsWith("#"))
|
|
27076
|
+
continue;
|
|
27077
|
+
const a = t.match(/^(\w+)\s*=\s*(.+?)$/);
|
|
27078
|
+
if (!a)
|
|
27079
|
+
continue;
|
|
27080
|
+
const lhs = a[1];
|
|
27081
|
+
const rhs = a[2];
|
|
27082
|
+
if (taintedVars.has(lhs))
|
|
27083
|
+
continue;
|
|
27084
|
+
if (reqExtractRe.test(rhs)) {
|
|
27085
|
+
taintedVars.add(lhs);
|
|
27086
|
+
continue;
|
|
27087
|
+
}
|
|
27088
|
+
for (const v of taintedVars) {
|
|
27089
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27090
|
+
taintedVars.add(lhs);
|
|
27091
|
+
break;
|
|
27092
|
+
}
|
|
27093
|
+
}
|
|
27094
|
+
}
|
|
27095
|
+
if (taintedVars.size === before)
|
|
27096
|
+
break;
|
|
27097
|
+
}
|
|
27098
|
+
if (taintedVars.size === 0)
|
|
27099
|
+
return findings;
|
|
27100
|
+
const percentRe = /\b(\w+)\s*%\s*[\(\[\{"'\w]/;
|
|
27101
|
+
const dotFormatRe = /\b(\w+)\s*\.\s*format\s*\(/;
|
|
27102
|
+
const seen = new Set;
|
|
27103
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27104
|
+
const line = lines[i2];
|
|
27105
|
+
const t = line.trim();
|
|
27106
|
+
if (t.startsWith("#"))
|
|
27107
|
+
continue;
|
|
27108
|
+
const pm = line.match(percentRe);
|
|
27109
|
+
if (pm && taintedVars.has(pm[1])) {
|
|
27110
|
+
const key = `${i2 + 1}:percent`;
|
|
27111
|
+
if (!seen.has(key)) {
|
|
27112
|
+
seen.add(key);
|
|
27113
|
+
findings.push({
|
|
27114
|
+
id: `format_string-${file}-${i2 + 1}-py-percent`,
|
|
27115
|
+
pass: "language-sources",
|
|
27116
|
+
category: "security",
|
|
27117
|
+
rule_id: "format_string",
|
|
27118
|
+
cwe: "CWE-134",
|
|
27119
|
+
severity: "high",
|
|
27120
|
+
level: "error",
|
|
27121
|
+
message: "Format-string injection: Python `<tainted> % args` uses an " + "HTTP-request-controlled format string. Malformed specifiers can " + "crash the handler (TypeError) and `%(name)s` access can reveal " + "arbitrary mapping keys. Use a literal format string and pass " + "untrusted values as arguments only.",
|
|
27122
|
+
file,
|
|
27123
|
+
line: i2 + 1,
|
|
27124
|
+
snippet: line.trim()
|
|
27125
|
+
});
|
|
27126
|
+
}
|
|
27127
|
+
}
|
|
27128
|
+
const dm = line.match(dotFormatRe);
|
|
27129
|
+
if (dm && taintedVars.has(dm[1])) {
|
|
27130
|
+
const key = `${i2 + 1}:dotformat`;
|
|
27131
|
+
if (!seen.has(key)) {
|
|
27132
|
+
seen.add(key);
|
|
27133
|
+
findings.push({
|
|
27134
|
+
id: `format_string-${file}-${i2 + 1}-py-strformat`,
|
|
27135
|
+
pass: "language-sources",
|
|
27136
|
+
category: "security",
|
|
27137
|
+
rule_id: "format_string",
|
|
27138
|
+
cwe: "CWE-134",
|
|
27139
|
+
severity: "high",
|
|
27140
|
+
level: "error",
|
|
27141
|
+
message: "Format-string injection: Python `<tainted>.format(...)` lets " + "the attacker control the format template. Field-access shapes " + "such as `{0.__class__.__init__.__globals__}` can leak module " + "globals (e.g. secret keys). Use a literal template and pass " + "untrusted values as positional/keyword arguments only.",
|
|
27142
|
+
file,
|
|
27143
|
+
line: i2 + 1,
|
|
27144
|
+
snippet: line.trim()
|
|
27145
|
+
});
|
|
27146
|
+
}
|
|
27147
|
+
}
|
|
27148
|
+
}
|
|
27149
|
+
return findings;
|
|
27150
|
+
}
|
|
27151
|
+
function findJsUtilFormatFormatStringFindings(code, file) {
|
|
27152
|
+
const findings = [];
|
|
27153
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27154
|
+
return findings;
|
|
27155
|
+
if (!/\butil\s*\.\s*format\s*\(/.test(code))
|
|
27156
|
+
return findings;
|
|
27157
|
+
if (!/\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/.test(code)) {
|
|
27158
|
+
return findings;
|
|
27159
|
+
}
|
|
27160
|
+
const lines = code.split(`
|
|
27161
|
+
`);
|
|
27162
|
+
const reqExtractRe = /\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/;
|
|
27163
|
+
const taintedVars = new Set;
|
|
27164
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27165
|
+
const before = taintedVars.size;
|
|
27166
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27167
|
+
const t = lines[i2].trim();
|
|
27168
|
+
if (t.startsWith("//"))
|
|
27169
|
+
continue;
|
|
27170
|
+
const a = t.match(/^(?:const|let|var)\s+(\w+)\s*=\s*(.+?);?$/);
|
|
27171
|
+
if (!a)
|
|
27172
|
+
continue;
|
|
27173
|
+
const lhs = a[1];
|
|
27174
|
+
const rhs = a[2];
|
|
27175
|
+
if (taintedVars.has(lhs))
|
|
27176
|
+
continue;
|
|
27177
|
+
if (reqExtractRe.test(rhs)) {
|
|
27178
|
+
taintedVars.add(lhs);
|
|
27179
|
+
continue;
|
|
27180
|
+
}
|
|
27181
|
+
for (const v of taintedVars) {
|
|
27182
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27183
|
+
taintedVars.add(lhs);
|
|
27184
|
+
break;
|
|
27185
|
+
}
|
|
27186
|
+
}
|
|
27187
|
+
}
|
|
27188
|
+
if (taintedVars.size === before)
|
|
27189
|
+
break;
|
|
27190
|
+
}
|
|
27191
|
+
if (taintedVars.size === 0)
|
|
27192
|
+
return findings;
|
|
27193
|
+
const callRe = /\butil\s*\.\s*format\s*\(\s*([\w.]+)\s*[,)]/;
|
|
27194
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27195
|
+
const line = lines[i2];
|
|
27196
|
+
const m = line.match(callRe);
|
|
27197
|
+
if (!m)
|
|
27198
|
+
continue;
|
|
27199
|
+
const arg = m[1];
|
|
27200
|
+
const root = arg.split(".")[0];
|
|
27201
|
+
if (!taintedVars.has(root) && !reqExtractRe.test(arg))
|
|
27202
|
+
continue;
|
|
27203
|
+
findings.push({
|
|
27204
|
+
id: `format_string-${file}-${i2 + 1}-js-util-format`,
|
|
27205
|
+
pass: "language-sources",
|
|
27206
|
+
category: "security",
|
|
27207
|
+
rule_id: "format_string",
|
|
27208
|
+
cwe: "CWE-134",
|
|
27209
|
+
severity: "medium",
|
|
27210
|
+
level: "error",
|
|
27211
|
+
message: "Format-string injection: Node `util.format(<tainted>, ...)` uses " + "an HTTP-request-controlled format string. The attacker can " + "manipulate `%s`/`%j`/`%O` specifiers to alter the rendered " + "output. Pass user input as a subsequent argument with a literal " + "format string.",
|
|
27212
|
+
file,
|
|
27213
|
+
line: i2 + 1,
|
|
27214
|
+
snippet: line.trim()
|
|
27215
|
+
});
|
|
27216
|
+
}
|
|
27217
|
+
return findings;
|
|
27218
|
+
}
|
|
27219
|
+
function findPythonHeaderCrlfInjectionFindings(code, file) {
|
|
27220
|
+
const findings = [];
|
|
27221
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27222
|
+
return findings;
|
|
27223
|
+
if (!/\b\w+\s*\.\s*headers\b/.test(code))
|
|
27224
|
+
return findings;
|
|
27225
|
+
if (!/\b(?:request\.(?:args|form|values|json|files|cookies|headers|data)|flask\.request)\b/.test(code)) {
|
|
27226
|
+
return findings;
|
|
27227
|
+
}
|
|
27228
|
+
const lines = code.split(`
|
|
27229
|
+
`);
|
|
27230
|
+
const reqExtractRe = /\b(?:request\.(?:args|form|values|json|files|cookies|headers|data)\b|flask\.request\b)/;
|
|
27231
|
+
const taintedVars = new Set;
|
|
27232
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27233
|
+
const before = taintedVars.size;
|
|
27234
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27235
|
+
const t = lines[i2].trim();
|
|
27236
|
+
if (t.startsWith("#"))
|
|
27237
|
+
continue;
|
|
27238
|
+
const a = t.match(/^(\w+)\s*=\s*(.+?)$/);
|
|
27239
|
+
if (!a)
|
|
27240
|
+
continue;
|
|
27241
|
+
const lhs = a[1];
|
|
27242
|
+
const rhs = a[2];
|
|
27243
|
+
if (taintedVars.has(lhs))
|
|
27244
|
+
continue;
|
|
27245
|
+
if (reqExtractRe.test(rhs)) {
|
|
27246
|
+
taintedVars.add(lhs);
|
|
27247
|
+
continue;
|
|
27248
|
+
}
|
|
27249
|
+
for (const v of taintedVars) {
|
|
27250
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27251
|
+
taintedVars.add(lhs);
|
|
27252
|
+
break;
|
|
27253
|
+
}
|
|
27254
|
+
}
|
|
27255
|
+
}
|
|
27256
|
+
if (taintedVars.size === before)
|
|
27257
|
+
break;
|
|
27258
|
+
}
|
|
27259
|
+
if (taintedVars.size === 0)
|
|
27260
|
+
return findings;
|
|
27261
|
+
const subscriptRe = /\b\w+\s*\.\s*headers\s*\[\s*['"][^'"]+['"]\s*\]\s*=\s*(.+)$/;
|
|
27262
|
+
const methodRe = /\b\w+\s*\.\s*headers\s*\.\s*(?:add|set|setdefault|append)\s*\(\s*['"][^'"]+['"]\s*,\s*(.+?)\)/;
|
|
27263
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27264
|
+
const line = lines[i2];
|
|
27265
|
+
const t = line.trim();
|
|
27266
|
+
if (t.startsWith("#"))
|
|
27267
|
+
continue;
|
|
27268
|
+
let expr = null;
|
|
27269
|
+
const sm = line.match(subscriptRe);
|
|
27270
|
+
if (sm)
|
|
27271
|
+
expr = sm[1];
|
|
27272
|
+
else {
|
|
27273
|
+
const mm = line.match(methodRe);
|
|
27274
|
+
if (mm)
|
|
27275
|
+
expr = mm[1];
|
|
27276
|
+
}
|
|
27277
|
+
if (!expr)
|
|
27278
|
+
continue;
|
|
27279
|
+
let tainted = reqExtractRe.test(expr);
|
|
27280
|
+
if (!tainted) {
|
|
27281
|
+
for (const v of taintedVars) {
|
|
27282
|
+
if (new RegExp(`\\b${v}\\b`).test(expr)) {
|
|
27283
|
+
tainted = true;
|
|
27284
|
+
break;
|
|
27285
|
+
}
|
|
27286
|
+
}
|
|
27287
|
+
}
|
|
27288
|
+
if (!tainted)
|
|
27289
|
+
continue;
|
|
27290
|
+
findings.push({
|
|
27291
|
+
id: `crlf-${file}-${i2 + 1}-py-headers`,
|
|
27292
|
+
pass: "language-sources",
|
|
27293
|
+
category: "security",
|
|
27294
|
+
rule_id: "crlf",
|
|
27295
|
+
cwe: "CWE-113",
|
|
27296
|
+
severity: "medium",
|
|
27297
|
+
level: "error",
|
|
27298
|
+
message: "CRLF / header injection: Python Flask/Werkzeug " + "`response.headers[...] = <tainted>` lets an attacker inject a " + "`\\r\\n` sequence and forge additional headers or split the " + "response body. Validate the value (reject control characters) or " + "use a fixed allowlist.",
|
|
27299
|
+
file,
|
|
27300
|
+
line: i2 + 1,
|
|
27301
|
+
snippet: line.trim()
|
|
27302
|
+
});
|
|
27303
|
+
}
|
|
27304
|
+
return findings;
|
|
27305
|
+
}
|
|
27306
|
+
function findJsLdapInjectionFindings(code, file) {
|
|
27307
|
+
const findings = [];
|
|
27308
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27309
|
+
return findings;
|
|
27310
|
+
if (!/\.\s*search\s*\(/.test(code))
|
|
27311
|
+
return findings;
|
|
27312
|
+
if (!/\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/.test(code)) {
|
|
27313
|
+
return findings;
|
|
27314
|
+
}
|
|
27315
|
+
if (!/\b(?:ldapjs|ldapts|require\(['"]ldapjs['"]\)|from\s+['"]ldapts['"])\b/.test(code)) {
|
|
27316
|
+
return findings;
|
|
27317
|
+
}
|
|
27318
|
+
const lines = code.split(`
|
|
27319
|
+
`);
|
|
27320
|
+
const reqExtractRe = /\breq\s*\.\s*(?:query|body|params|headers|cookies)\b/;
|
|
27321
|
+
const taintedVars = new Set;
|
|
27322
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27323
|
+
const before = taintedVars.size;
|
|
27324
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27325
|
+
const t = lines[i2].trim();
|
|
27326
|
+
if (t.startsWith("//"))
|
|
27327
|
+
continue;
|
|
27328
|
+
const m = t.match(/^(?:const|let|var)\s+(\w+)(?:\s*:\s*[^=]+)?\s*=\s*(.+?);?$/);
|
|
27329
|
+
if (!m)
|
|
27330
|
+
continue;
|
|
27331
|
+
const lhs = m[1];
|
|
27332
|
+
const rhs = m[2];
|
|
27333
|
+
if (taintedVars.has(lhs))
|
|
27334
|
+
continue;
|
|
27335
|
+
if (reqExtractRe.test(rhs)) {
|
|
27336
|
+
taintedVars.add(lhs);
|
|
27337
|
+
continue;
|
|
27338
|
+
}
|
|
27339
|
+
for (const v of taintedVars) {
|
|
27340
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27341
|
+
taintedVars.add(lhs);
|
|
27342
|
+
break;
|
|
27343
|
+
}
|
|
27344
|
+
}
|
|
27345
|
+
}
|
|
27346
|
+
if (taintedVars.size === before)
|
|
27347
|
+
break;
|
|
27348
|
+
}
|
|
27349
|
+
if (taintedVars.size === 0)
|
|
27350
|
+
return findings;
|
|
27351
|
+
const filterPropRe = /\bfilter\s*:\s*([^,}\n]+)/;
|
|
27352
|
+
const filterShorthandRe = /(?:^|[\{,])\s*filter\s*[,}]/;
|
|
27353
|
+
const seen = new Set;
|
|
27354
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27355
|
+
const line = lines[i2];
|
|
27356
|
+
const fm = line.match(filterPropRe);
|
|
27357
|
+
let expr = null;
|
|
27358
|
+
if (fm) {
|
|
27359
|
+
expr = fm[1].trim();
|
|
27360
|
+
} else if (filterShorthandRe.test(line)) {
|
|
27361
|
+
expr = "filter";
|
|
27362
|
+
} else {
|
|
27363
|
+
continue;
|
|
27364
|
+
}
|
|
27365
|
+
let tainted = false;
|
|
27366
|
+
for (const v of taintedVars) {
|
|
27367
|
+
if (new RegExp(`\\b${v}\\b`).test(expr)) {
|
|
27368
|
+
tainted = true;
|
|
27369
|
+
break;
|
|
27370
|
+
}
|
|
27371
|
+
}
|
|
27372
|
+
if (!tainted)
|
|
27373
|
+
continue;
|
|
27374
|
+
if (seen.has(i2 + 1))
|
|
27375
|
+
continue;
|
|
27376
|
+
seen.add(i2 + 1);
|
|
27377
|
+
findings.push({
|
|
27378
|
+
id: `ldap_injection-${file}-${i2 + 1}-js-ldap-filter`,
|
|
27379
|
+
pass: "language-sources",
|
|
27380
|
+
category: "security",
|
|
27381
|
+
rule_id: "ldap_injection",
|
|
27382
|
+
cwe: "CWE-90",
|
|
27383
|
+
severity: "critical",
|
|
27384
|
+
level: "error",
|
|
27385
|
+
message: "LDAP injection: ldapjs/ldapts `client.search(..., { filter: " + "<tainted>, ... })` lets the attacker break out of the filter " + "expression (e.g. `*)(uid=*`) and enumerate the directory. " + "Escape the user input with a tight allowlist (`[A-Za-z0-9_-]+`) " + "or use a structured filter builder.",
|
|
27386
|
+
file,
|
|
27387
|
+
line: i2 + 1,
|
|
27388
|
+
snippet: line.trim()
|
|
27389
|
+
});
|
|
27390
|
+
}
|
|
27391
|
+
return findings;
|
|
27392
|
+
}
|
|
27393
|
+
function findGoLdapInjectionFindings(code, file) {
|
|
27394
|
+
const findings = [];
|
|
27395
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27396
|
+
return findings;
|
|
27397
|
+
if (!/\bldap\s*\.\s*NewSearchRequest\s*\(/.test(code))
|
|
27398
|
+
return findings;
|
|
27399
|
+
if (!/\br\s*\.\s*(?:URL\s*\.\s*Query\s*\(\s*\)|Form|PostForm|FormValue|PostFormValue|Header)/.test(code)) {
|
|
27400
|
+
return findings;
|
|
27401
|
+
}
|
|
27402
|
+
const lines = code.split(`
|
|
27403
|
+
`);
|
|
27404
|
+
const reqExtractRe = /\br\s*\.\s*(?:URL\s*\.\s*Query\s*\(\s*\)\s*\.\s*Get|Form\s*\.\s*Get|PostForm\s*\.\s*Get|FormValue|PostFormValue|Header\s*\.\s*Get)\s*\(/;
|
|
27405
|
+
const taintedVars = new Set;
|
|
27406
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27407
|
+
const before = taintedVars.size;
|
|
27408
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27409
|
+
const t = lines[i2].trim();
|
|
27410
|
+
if (t.startsWith("//"))
|
|
27411
|
+
continue;
|
|
27412
|
+
const m = t.match(/^(\w+)\s*(?::=|=)\s*(.+?)$/);
|
|
27413
|
+
if (!m)
|
|
27414
|
+
continue;
|
|
27415
|
+
const lhs = m[1];
|
|
27416
|
+
const rhs = m[2];
|
|
27417
|
+
if (taintedVars.has(lhs))
|
|
27418
|
+
continue;
|
|
27419
|
+
if (reqExtractRe.test(rhs)) {
|
|
27420
|
+
taintedVars.add(lhs);
|
|
27421
|
+
continue;
|
|
27422
|
+
}
|
|
27423
|
+
for (const v of taintedVars) {
|
|
27424
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27425
|
+
taintedVars.add(lhs);
|
|
27426
|
+
break;
|
|
27427
|
+
}
|
|
27428
|
+
}
|
|
27429
|
+
}
|
|
27430
|
+
if (taintedVars.size === before)
|
|
27431
|
+
break;
|
|
27432
|
+
}
|
|
27433
|
+
if (taintedVars.size === 0)
|
|
27434
|
+
return findings;
|
|
27435
|
+
const seen = new Set;
|
|
27436
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27437
|
+
const openIdx = lines[i2].indexOf("NewSearchRequest(");
|
|
27438
|
+
if (openIdx === -1)
|
|
27439
|
+
continue;
|
|
27440
|
+
let depth = 0;
|
|
27441
|
+
let buf = "";
|
|
27442
|
+
let endLine = i2;
|
|
27443
|
+
let started = false;
|
|
27444
|
+
for (let j = i2;j < Math.min(i2 + 25, lines.length); j++) {
|
|
27445
|
+
for (const ch of lines[j]) {
|
|
27446
|
+
if (ch === "(") {
|
|
27447
|
+
depth++;
|
|
27448
|
+
started = true;
|
|
27449
|
+
} else if (ch === ")") {
|
|
27450
|
+
depth--;
|
|
27451
|
+
}
|
|
27452
|
+
buf += ch;
|
|
27453
|
+
if (started && depth === 0) {
|
|
27454
|
+
endLine = j;
|
|
27455
|
+
break;
|
|
27456
|
+
}
|
|
27457
|
+
}
|
|
27458
|
+
if (started && depth === 0)
|
|
27459
|
+
break;
|
|
27460
|
+
buf += `
|
|
27461
|
+
`;
|
|
27462
|
+
}
|
|
27463
|
+
const argsStart = buf.indexOf("(");
|
|
27464
|
+
if (argsStart === -1)
|
|
27465
|
+
continue;
|
|
27466
|
+
const args2 = buf.substring(argsStart + 1, buf.length - 1);
|
|
27467
|
+
const parts2 = [];
|
|
27468
|
+
{
|
|
27469
|
+
let d = 0;
|
|
27470
|
+
let b = "";
|
|
27471
|
+
let inStr = null;
|
|
27472
|
+
for (let k = 0;k < args2.length; k++) {
|
|
27473
|
+
const ch = args2[k];
|
|
27474
|
+
if (inStr) {
|
|
27475
|
+
if (ch === "\\") {
|
|
27476
|
+
b += ch + (args2[k + 1] ?? "");
|
|
27477
|
+
k++;
|
|
27478
|
+
continue;
|
|
27479
|
+
}
|
|
27480
|
+
if (ch === inStr)
|
|
27481
|
+
inStr = null;
|
|
27482
|
+
b += ch;
|
|
27483
|
+
continue;
|
|
27484
|
+
}
|
|
27485
|
+
if (ch === '"' || ch === "`") {
|
|
27486
|
+
inStr = ch;
|
|
27487
|
+
b += ch;
|
|
27488
|
+
continue;
|
|
27489
|
+
}
|
|
27490
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
27491
|
+
d++;
|
|
27492
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
27493
|
+
d--;
|
|
27494
|
+
if (ch === "," && d === 0) {
|
|
27495
|
+
parts2.push(b);
|
|
27496
|
+
b = "";
|
|
27497
|
+
continue;
|
|
27498
|
+
}
|
|
27499
|
+
b += ch;
|
|
27500
|
+
}
|
|
27501
|
+
if (b.trim().length > 0)
|
|
27502
|
+
parts2.push(b);
|
|
27503
|
+
}
|
|
27504
|
+
if (parts2.length < 7)
|
|
27505
|
+
continue;
|
|
27506
|
+
const filterExpr = parts2[6].trim();
|
|
27507
|
+
let tainted = false;
|
|
27508
|
+
for (const v of taintedVars) {
|
|
27509
|
+
if (new RegExp(`\\b${v}\\b`).test(filterExpr)) {
|
|
27510
|
+
tainted = true;
|
|
27511
|
+
break;
|
|
27512
|
+
}
|
|
27513
|
+
}
|
|
27514
|
+
if (!tainted)
|
|
27515
|
+
continue;
|
|
27516
|
+
if (seen.has(i2 + 1))
|
|
27517
|
+
continue;
|
|
27518
|
+
seen.add(i2 + 1);
|
|
27519
|
+
findings.push({
|
|
27520
|
+
id: `ldap_injection-${file}-${i2 + 1}-go-newsearchrequest`,
|
|
27521
|
+
pass: "language-sources",
|
|
27522
|
+
category: "security",
|
|
27523
|
+
rule_id: "ldap_injection",
|
|
27524
|
+
cwe: "CWE-90",
|
|
27525
|
+
severity: "critical",
|
|
27526
|
+
level: "error",
|
|
27527
|
+
message: "LDAP injection: go-ldap `ldap.NewSearchRequest(..., <tainted " + "filter>, ...)` lets the attacker break out of the filter " + "expression and enumerate the directory. Escape the user input " + "with `ldap.EscapeFilter(...)` or use a structured filter " + "builder.",
|
|
27528
|
+
file,
|
|
27529
|
+
line: i2 + 1,
|
|
27530
|
+
snippet: (lines[i2] + (endLine > i2 ? " …" : "")).trim()
|
|
27531
|
+
});
|
|
27532
|
+
}
|
|
27533
|
+
return findings;
|
|
27534
|
+
}
|
|
27535
|
+
function findRustLdapInjectionFindings(code, file) {
|
|
27536
|
+
const findings = [];
|
|
27537
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27538
|
+
return findings;
|
|
27539
|
+
if (!/\.\s*search\s*\(/.test(code))
|
|
27540
|
+
return findings;
|
|
27541
|
+
if (!/\b(?:ldap3|LdapConn|Ldap)\b/.test(code))
|
|
27542
|
+
return findings;
|
|
27543
|
+
const lines = code.split(`
|
|
27544
|
+
`);
|
|
27545
|
+
const extractorTypeRe = /:\s*(?:String|Bytes|bytes::Bytes|axum::body::Bytes|web::Query\b|web::Path\b|web::Form\b|web::Json\b|HttpRequest\b|actix_web::HttpRequest\b)/;
|
|
27546
|
+
const fns = [];
|
|
27547
|
+
let cur = null;
|
|
27548
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27549
|
+
const t = lines[i2];
|
|
27550
|
+
if (/^\s*(?:pub\s+)?(?:async\s+)?fn\s+\w+\s*\(/.test(t)) {
|
|
27551
|
+
if (cur) {
|
|
27552
|
+
cur.end = i2 - 1;
|
|
27553
|
+
fns.push(cur);
|
|
27554
|
+
}
|
|
27555
|
+
cur = { start: i2, end: lines.length - 1, tainted: new Set };
|
|
27556
|
+
let headerJoined = "";
|
|
27557
|
+
for (let j = i2;j < Math.min(i2 + 12, lines.length); j++) {
|
|
27558
|
+
headerJoined += lines[j];
|
|
27559
|
+
if (/\{\s*$/.test(lines[j]))
|
|
27560
|
+
break;
|
|
27561
|
+
}
|
|
27562
|
+
const open = headerJoined.indexOf("(");
|
|
27563
|
+
const close = headerJoined.lastIndexOf(")");
|
|
27564
|
+
if (open !== -1 && close > open) {
|
|
27565
|
+
const params = headerJoined.substring(open + 1, close);
|
|
27566
|
+
let depth = 0;
|
|
27567
|
+
let buf = "";
|
|
27568
|
+
const parts2 = [];
|
|
27569
|
+
for (const ch of params) {
|
|
27570
|
+
if (ch === "<" || ch === "(")
|
|
27571
|
+
depth++;
|
|
27572
|
+
else if (ch === ">" || ch === ")")
|
|
27573
|
+
depth--;
|
|
27574
|
+
if (ch === "," && depth === 0) {
|
|
27575
|
+
parts2.push(buf);
|
|
27576
|
+
buf = "";
|
|
27577
|
+
continue;
|
|
27578
|
+
}
|
|
27579
|
+
buf += ch;
|
|
27580
|
+
}
|
|
27581
|
+
if (buf.trim().length > 0)
|
|
27582
|
+
parts2.push(buf);
|
|
27583
|
+
for (const p of parts2) {
|
|
27584
|
+
const pm = p.match(/(?:mut\s+)?(\w+)\s*:/);
|
|
27585
|
+
if (!pm)
|
|
27586
|
+
continue;
|
|
27587
|
+
if (extractorTypeRe.test(p))
|
|
27588
|
+
cur.tainted.add(pm[1]);
|
|
27589
|
+
}
|
|
27590
|
+
}
|
|
27591
|
+
}
|
|
27592
|
+
}
|
|
27593
|
+
if (cur)
|
|
27594
|
+
fns.push(cur);
|
|
27595
|
+
for (const fn of fns) {
|
|
27596
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27597
|
+
const before = fn.tainted.size;
|
|
27598
|
+
for (let i2 = fn.start;i2 <= fn.end; i2++) {
|
|
27599
|
+
const t = lines[i2].trim();
|
|
27600
|
+
const m = t.match(/^let\s+(?:mut\s+)?(\w+)\s*(?::\s*[^=]+)?=\s*(.+?);?$/);
|
|
27601
|
+
if (!m)
|
|
27602
|
+
continue;
|
|
27603
|
+
const lhs = m[1];
|
|
27604
|
+
const rhs = m[2];
|
|
27605
|
+
if (fn.tainted.has(lhs))
|
|
27606
|
+
continue;
|
|
27607
|
+
for (const v of fn.tainted) {
|
|
27608
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27609
|
+
fn.tainted.add(lhs);
|
|
27610
|
+
break;
|
|
27611
|
+
}
|
|
27612
|
+
}
|
|
27613
|
+
}
|
|
27614
|
+
if (fn.tainted.size === before)
|
|
27615
|
+
break;
|
|
27616
|
+
}
|
|
27617
|
+
}
|
|
27618
|
+
const seen = new Set;
|
|
27619
|
+
for (const fn of fns) {
|
|
27620
|
+
if (fn.tainted.size === 0)
|
|
27621
|
+
continue;
|
|
27622
|
+
for (let i2 = fn.start;i2 <= fn.end; i2++) {
|
|
27623
|
+
const searchIdx = lines[i2].indexOf(".search(");
|
|
27624
|
+
if (searchIdx === -1)
|
|
27625
|
+
continue;
|
|
27626
|
+
let depth = 0;
|
|
27627
|
+
let buf = "";
|
|
27628
|
+
let started = false;
|
|
27629
|
+
for (let j = i2;j < Math.min(i2 + 15, lines.length); j++) {
|
|
27630
|
+
const startCol = j === i2 ? searchIdx : 0;
|
|
27631
|
+
for (let k = startCol;k < lines[j].length; k++) {
|
|
27632
|
+
const ch = lines[j][k];
|
|
27633
|
+
if (ch === "(") {
|
|
27634
|
+
depth++;
|
|
27635
|
+
started = true;
|
|
27636
|
+
} else if (ch === ")") {
|
|
27637
|
+
depth--;
|
|
27638
|
+
}
|
|
27639
|
+
buf += ch;
|
|
27640
|
+
if (started && depth === 0)
|
|
27641
|
+
break;
|
|
27642
|
+
}
|
|
27643
|
+
if (started && depth === 0)
|
|
27644
|
+
break;
|
|
27645
|
+
buf += `
|
|
27646
|
+
`;
|
|
27647
|
+
}
|
|
27648
|
+
const argsStart = buf.indexOf("(");
|
|
27649
|
+
if (argsStart === -1)
|
|
27650
|
+
continue;
|
|
27651
|
+
const args2 = buf.substring(argsStart + 1, buf.length - 1);
|
|
27652
|
+
const parts2 = [];
|
|
27653
|
+
{
|
|
27654
|
+
let d = 0;
|
|
27655
|
+
let b = "";
|
|
27656
|
+
let inStr = null;
|
|
27657
|
+
for (let k = 0;k < args2.length; k++) {
|
|
27658
|
+
const ch = args2[k];
|
|
27659
|
+
if (inStr) {
|
|
27660
|
+
if (ch === "\\") {
|
|
27661
|
+
b += ch + (args2[k + 1] ?? "");
|
|
27662
|
+
k++;
|
|
27663
|
+
continue;
|
|
27664
|
+
}
|
|
27665
|
+
if (ch === inStr)
|
|
27666
|
+
inStr = null;
|
|
27667
|
+
b += ch;
|
|
27668
|
+
continue;
|
|
27669
|
+
}
|
|
27670
|
+
if (ch === '"') {
|
|
27671
|
+
inStr = ch;
|
|
27672
|
+
b += ch;
|
|
27673
|
+
continue;
|
|
27674
|
+
}
|
|
27675
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
27676
|
+
d++;
|
|
27677
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
27678
|
+
d--;
|
|
27679
|
+
if (ch === "," && d === 0) {
|
|
27680
|
+
parts2.push(b);
|
|
27681
|
+
b = "";
|
|
27682
|
+
continue;
|
|
27683
|
+
}
|
|
27684
|
+
b += ch;
|
|
27685
|
+
}
|
|
27686
|
+
if (b.trim().length > 0)
|
|
27687
|
+
parts2.push(b);
|
|
27688
|
+
}
|
|
27689
|
+
if (parts2.length < 3)
|
|
27690
|
+
continue;
|
|
27691
|
+
const filterExpr = parts2[2].trim();
|
|
27692
|
+
let tainted = false;
|
|
27693
|
+
for (const v of fn.tainted) {
|
|
27694
|
+
if (new RegExp(`\\b${v}\\b`).test(filterExpr)) {
|
|
27695
|
+
tainted = true;
|
|
27696
|
+
break;
|
|
27697
|
+
}
|
|
27698
|
+
}
|
|
27699
|
+
if (!tainted)
|
|
27700
|
+
continue;
|
|
27701
|
+
if (seen.has(i2 + 1))
|
|
27702
|
+
continue;
|
|
27703
|
+
seen.add(i2 + 1);
|
|
27704
|
+
findings.push({
|
|
27705
|
+
id: `ldap_injection-${file}-${i2 + 1}-rust-ldap3-search`,
|
|
27706
|
+
pass: "language-sources",
|
|
27707
|
+
category: "security",
|
|
27708
|
+
rule_id: "ldap_injection",
|
|
27709
|
+
cwe: "CWE-90",
|
|
27710
|
+
severity: "critical",
|
|
27711
|
+
level: "error",
|
|
27712
|
+
message: "LDAP injection: ldap3 `LdapConn::search(..., &<tainted " + "filter>, ...)` lets the attacker break out of the filter " + "expression and enumerate the directory. Escape the user input " + "or use a structured filter builder.",
|
|
27713
|
+
file,
|
|
27714
|
+
line: i2 + 1,
|
|
27715
|
+
snippet: lines[i2].trim()
|
|
27716
|
+
});
|
|
27717
|
+
}
|
|
27718
|
+
}
|
|
27719
|
+
return findings;
|
|
27720
|
+
}
|
|
27721
|
+
function findRustLogInjectionFindings(code, file) {
|
|
27722
|
+
const findings = [];
|
|
27723
|
+
if (typeof code !== "string" || code.length === 0)
|
|
27724
|
+
return findings;
|
|
27725
|
+
if (!/\b(?:info|warn|error|debug|trace)\s*!\s*\(/.test(code))
|
|
27726
|
+
return findings;
|
|
27727
|
+
if (!/\b(?:log|tracing)\b/.test(code))
|
|
27728
|
+
return findings;
|
|
27729
|
+
const lines = code.split(`
|
|
27730
|
+
`);
|
|
27731
|
+
const extractorTypeRe = /:\s*(?:String|Bytes|bytes::Bytes|axum::body::Bytes|web::Query\b|web::Path\b|web::Form\b|web::Json\b|HttpRequest\b|actix_web::HttpRequest\b)/;
|
|
27732
|
+
const fns = [];
|
|
27733
|
+
let cur = null;
|
|
27734
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
27735
|
+
const t = lines[i2];
|
|
27736
|
+
if (/^\s*(?:pub\s+)?(?:async\s+)?fn\s+\w+\s*\(/.test(t)) {
|
|
27737
|
+
if (cur) {
|
|
27738
|
+
cur.end = i2 - 1;
|
|
27739
|
+
fns.push(cur);
|
|
27740
|
+
}
|
|
27741
|
+
cur = { start: i2, end: lines.length - 1, tainted: new Set };
|
|
27742
|
+
let headerJoined = "";
|
|
27743
|
+
for (let j = i2;j < Math.min(i2 + 12, lines.length); j++) {
|
|
27744
|
+
headerJoined += lines[j];
|
|
27745
|
+
if (/\{\s*$/.test(lines[j]))
|
|
27746
|
+
break;
|
|
27747
|
+
}
|
|
27748
|
+
const open = headerJoined.indexOf("(");
|
|
27749
|
+
const close = headerJoined.lastIndexOf(")");
|
|
27750
|
+
if (open !== -1 && close > open) {
|
|
27751
|
+
const params = headerJoined.substring(open + 1, close);
|
|
27752
|
+
let depth = 0;
|
|
27753
|
+
let buf = "";
|
|
27754
|
+
const parts2 = [];
|
|
27755
|
+
for (const ch of params) {
|
|
27756
|
+
if (ch === "<" || ch === "(")
|
|
27757
|
+
depth++;
|
|
27758
|
+
else if (ch === ">" || ch === ")")
|
|
27759
|
+
depth--;
|
|
27760
|
+
if (ch === "," && depth === 0) {
|
|
27761
|
+
parts2.push(buf);
|
|
27762
|
+
buf = "";
|
|
27763
|
+
continue;
|
|
27764
|
+
}
|
|
27765
|
+
buf += ch;
|
|
27766
|
+
}
|
|
27767
|
+
if (buf.trim().length > 0)
|
|
27768
|
+
parts2.push(buf);
|
|
27769
|
+
for (const p of parts2) {
|
|
27770
|
+
const pm = p.match(/(?:mut\s+)?(\w+)\s*:/);
|
|
27771
|
+
if (!pm)
|
|
27772
|
+
continue;
|
|
27773
|
+
if (extractorTypeRe.test(p))
|
|
27774
|
+
cur.tainted.add(pm[1]);
|
|
27775
|
+
}
|
|
27776
|
+
}
|
|
27777
|
+
}
|
|
27778
|
+
}
|
|
27779
|
+
if (cur)
|
|
27780
|
+
fns.push(cur);
|
|
27781
|
+
for (const fn of fns) {
|
|
27782
|
+
for (let pass = 0;pass < 3; pass++) {
|
|
27783
|
+
const before = fn.tainted.size;
|
|
27784
|
+
for (let i2 = fn.start;i2 <= fn.end; i2++) {
|
|
27785
|
+
const t = lines[i2].trim();
|
|
27786
|
+
const m = t.match(/^let\s+(?:mut\s+)?(\w+)\s*(?::\s*[^=]+)?=\s*(.+?);?$/);
|
|
27787
|
+
if (!m)
|
|
27788
|
+
continue;
|
|
27789
|
+
const lhs = m[1];
|
|
27790
|
+
const rhs = m[2];
|
|
27791
|
+
if (fn.tainted.has(lhs))
|
|
27792
|
+
continue;
|
|
27793
|
+
for (const v of fn.tainted) {
|
|
27794
|
+
if (new RegExp(`\\b${v}\\b`).test(rhs)) {
|
|
27795
|
+
fn.tainted.add(lhs);
|
|
27796
|
+
break;
|
|
27797
|
+
}
|
|
27798
|
+
}
|
|
27799
|
+
}
|
|
27800
|
+
if (fn.tainted.size === before)
|
|
27801
|
+
break;
|
|
27802
|
+
}
|
|
27803
|
+
}
|
|
27804
|
+
const macroRe = /\b(info|warn|error|debug|trace)\s*!\s*\(\s*([^;]+?)\)\s*;?\s*$/;
|
|
27805
|
+
const seen = new Set;
|
|
27806
|
+
for (const fn of fns) {
|
|
27807
|
+
if (fn.tainted.size === 0)
|
|
27808
|
+
continue;
|
|
27809
|
+
for (let i2 = fn.start;i2 <= fn.end; i2++) {
|
|
27810
|
+
const line = lines[i2];
|
|
27811
|
+
const m = line.match(macroRe);
|
|
27812
|
+
if (!m)
|
|
27813
|
+
continue;
|
|
27814
|
+
const macroName = m[1];
|
|
27815
|
+
const argSpan = m[2];
|
|
27816
|
+
const parts2 = [];
|
|
27817
|
+
{
|
|
27818
|
+
let d = 0;
|
|
27819
|
+
let b = "";
|
|
27820
|
+
let inStr = null;
|
|
27821
|
+
for (let k = 0;k < argSpan.length; k++) {
|
|
27822
|
+
const ch = argSpan[k];
|
|
27823
|
+
if (inStr) {
|
|
27824
|
+
if (ch === "\\") {
|
|
27825
|
+
b += ch + (argSpan[k + 1] ?? "");
|
|
27826
|
+
k++;
|
|
27827
|
+
continue;
|
|
27828
|
+
}
|
|
27829
|
+
if (ch === inStr)
|
|
27830
|
+
inStr = null;
|
|
27831
|
+
b += ch;
|
|
27832
|
+
continue;
|
|
27833
|
+
}
|
|
27834
|
+
if (ch === '"') {
|
|
27835
|
+
inStr = ch;
|
|
27836
|
+
b += ch;
|
|
27837
|
+
continue;
|
|
27838
|
+
}
|
|
27839
|
+
if (ch === "(" || ch === "[" || ch === "{")
|
|
27840
|
+
d++;
|
|
27841
|
+
else if (ch === ")" || ch === "]" || ch === "}")
|
|
27842
|
+
d--;
|
|
27843
|
+
if (ch === "," && d === 0) {
|
|
27844
|
+
parts2.push(b);
|
|
27845
|
+
b = "";
|
|
27846
|
+
continue;
|
|
27847
|
+
}
|
|
27848
|
+
b += ch;
|
|
27849
|
+
}
|
|
27850
|
+
if (b.trim().length > 0)
|
|
27851
|
+
parts2.push(b);
|
|
27852
|
+
}
|
|
27853
|
+
if (parts2.length < 2)
|
|
27854
|
+
continue;
|
|
27855
|
+
let tainted = false;
|
|
27856
|
+
for (let p = 1;p < parts2.length; p++) {
|
|
27857
|
+
for (const v of fn.tainted) {
|
|
27858
|
+
if (new RegExp(`\\b${v}\\b`).test(parts2[p])) {
|
|
27859
|
+
tainted = true;
|
|
27860
|
+
break;
|
|
27861
|
+
}
|
|
27862
|
+
}
|
|
27863
|
+
if (tainted)
|
|
27864
|
+
break;
|
|
27865
|
+
}
|
|
27866
|
+
if (!tainted)
|
|
27867
|
+
continue;
|
|
27868
|
+
const key = `${i2 + 1}:${macroName}`;
|
|
27869
|
+
if (seen.has(key))
|
|
27870
|
+
continue;
|
|
27871
|
+
seen.add(key);
|
|
27872
|
+
findings.push({
|
|
27873
|
+
id: `log_injection-${file}-${i2 + 1}-rust-${macroName}`,
|
|
27874
|
+
pass: "language-sources",
|
|
27875
|
+
category: "security",
|
|
27876
|
+
rule_id: "log_injection",
|
|
27877
|
+
cwe: "CWE-117",
|
|
27878
|
+
severity: "medium",
|
|
27879
|
+
level: "warning",
|
|
27880
|
+
message: `Log injection: Rust \`${macroName}!(...)\` interpolates a ` + "tainted value into the log line. Unsanitized CRLF lets an " + "attacker forge log entries or split records. Strip control " + "characters or use a structured logging API.",
|
|
27881
|
+
file,
|
|
27882
|
+
line: i2 + 1,
|
|
27883
|
+
snippet: line.trim()
|
|
27884
|
+
});
|
|
27885
|
+
}
|
|
27886
|
+
}
|
|
27887
|
+
return findings;
|
|
27888
|
+
}
|
|
27039
27889
|
|
|
27040
27890
|
// ../circle-ir/dist/analysis/passes/sink-filter-pass.js
|
|
27041
27891
|
var JS_XSS_SANITIZERS = [
|
|
@@ -40163,7 +41013,7 @@ var colors = {
|
|
|
40163
41013
|
};
|
|
40164
41014
|
|
|
40165
41015
|
// src/version.ts
|
|
40166
|
-
var version = "3.
|
|
41016
|
+
var version = "3.137.0";
|
|
40167
41017
|
|
|
40168
41018
|
// src/formatters.ts
|
|
40169
41019
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.137.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^3.
|
|
69
|
+
"circle-ir": "^3.137.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|