correctover-scan 3.2.0 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/bundle-scanner.js +16 -5
- package/index.js +6 -3
- package/package.json +1 -1
package/core/bundle-scanner.js
CHANGED
|
@@ -195,7 +195,11 @@ const METADATA_HOSTS = /metadata\.google(?:\.internal)?|169\.254\.|fd00:ec2/i;
|
|
|
195
195
|
|
|
196
196
|
// Context that proves a 169.254/private address is a cloud SDK credential
|
|
197
197
|
// provider (normal) rather than business-code SSRF (finding).
|
|
198
|
-
|
|
198
|
+
// Strong signals only: the metadata address must actually sit inside a cloud
|
|
199
|
+
// SDK credential-provider call. A bare AWS_/GCP env-var name nearby is NOT
|
|
200
|
+
// proof of SDK usage — it often co-occurs with a hand-rolled SSRF and used to
|
|
201
|
+
// wrongly suppress it. Keep this narrow to avoid false negatives.
|
|
202
|
+
const SDK_CTX = /(aws-sdk|@aws-sdk|amazon-cognito|aws4fetch|fromNodeProviderChain|fromIni|ECSCredentials|EC2MetadataClient|instanceMetadataService|metadataService\b|[A-Za-z_$][\w$]*[Mm]etadata\s*[=(]|metadata\s*(?:service|endpoint)\s*[:=]|credentialprovider|credential[ _-]?provider|credentials?\s*(?:chain|from)|container\s*credentials|sts\.|gcp-metadata|google-auth-library)/i;
|
|
199
203
|
// Context that proves the address sits inside an SSRF guard / blocklist.
|
|
200
204
|
// Actual guard constructs (a bare "ssrf" word in a comment is not a guard).
|
|
201
205
|
const GUARD_CTX = /(no_?proxy|blocklist|block_list|denylist|deny_list|allowlist|preflight|isPrivate|private[_-]?ip|loopback|metadata.{0,30}(block|deny|filter|redirect))/i;
|
|
@@ -391,7 +395,9 @@ function checkMetadataIntranet(file) {
|
|
|
391
395
|
seen.add(target + ':' + i);
|
|
392
396
|
const ctx = lineWindow(file.lines, i);
|
|
393
397
|
const isMetadata = /^169\.254\./.test(target) || /metadata\.google/i.test(target) || /fd00:ec2/i.test(target);
|
|
394
|
-
|
|
398
|
+
// Bare "gcp"/"google" words are too weak and suppress real SSRFs; require
|
|
399
|
+
// concrete SDK/host markers instead (SDK_CTX already covers the strong ones).
|
|
400
|
+
const inSdk = SDK_CTX.test(ctx) || /SECONDARY_HOST_ADDRESS|gcp-metadata|metadata\.google(?:\.internal)?/.test(ctx);
|
|
395
401
|
const inGuard = GUARD_CTX.test(ctx);
|
|
396
402
|
let sev, msg, suppressed = false;
|
|
397
403
|
if (inSdk) {
|
|
@@ -417,7 +423,7 @@ function checkMetadataIntranet(file) {
|
|
|
417
423
|
function checkCommandExec(file) {
|
|
418
424
|
const findings = [];
|
|
419
425
|
const shellTrue = /shell\s*:\s*(?:!0|true)/;
|
|
420
|
-
const execCall = /(
|
|
426
|
+
const execCall = /(?<![\w$])(?:exec|execSync)\s*\(/;
|
|
421
427
|
const gatedRe = /(permission[A-Z]|permission[ _-]?(?:check|mode|behavior|behaviour|prompt|decision|system|gate)|allowedTools|disallowedTools|shell-quote|shellQuote|\.quote\(|PreToolUse|PostToolUse|hookEventName|"Bash"|Bash tool|isCommandAllowed|canUseBash|checkPermissions|permissionRule|toolPermission)/i;
|
|
422
428
|
const buildRe = /(node-gyp|npm install|install script|spawnSync\(\s*["'`])(?:[a-z\-]+ )?[a-z\-]+(?:\s|["'`])/i;
|
|
423
429
|
// call site where the command arg is a fixed literal ("cmd ...")
|
|
@@ -456,9 +462,14 @@ function checkCommandExec(file) {
|
|
|
456
462
|
if (seen.has('exec:' + i)) return;
|
|
457
463
|
seen.add('exec:' + i);
|
|
458
464
|
const ctx = lineWindow(file.lines, i);
|
|
459
|
-
|
|
465
|
+
// Variable passed straight through as the command arg, e.g. exec(userInput)
|
|
466
|
+
// or execSync(cmd, opts) — no template/concat syntax but still fully
|
|
467
|
+
// attacker-controllable. String literals are excluded by the identifier
|
|
468
|
+
// pattern; method chains like obj.execute(x) are not matched (execCall).
|
|
469
|
+
const varArg = /(?:exec|execSync)\s*\(\s*[A-Za-z_$][\w$]*\s*[,\)]/.test(line);
|
|
470
|
+
const looksDynamic = dynamicCmd.test(line) || /\$\{/.test(ctx) || varArg;
|
|
460
471
|
findings.push(mkFinding(file, i + 1, looksDynamic ? 'warn' : 'info',
|
|
461
|
-
`child_process exec/execSync 调用(始终经 shell)${looksDynamic ? '
|
|
472
|
+
`child_process exec/execSync 调用(始终经 shell)${looksDynamic ? ',命令参数疑似动态/外部输入 — 检查命令注入面' : ',参数为静态/库内字符串'}`,
|
|
462
473
|
{ suppressed: !looksDynamic, snippet: snippet(line), checkId: 'command-exec' }));
|
|
463
474
|
}
|
|
464
475
|
});
|
package/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const { recordCall, getUpgradeMessage } = require('./core/license');
|
|
|
15
15
|
const skillpay = require('./core/skillpay');
|
|
16
16
|
const { formatHTMLReport, normalizeConfigFindings, normalizeBundleFindings, failHint } = require('./core/report');
|
|
17
17
|
|
|
18
|
-
const VERSION = '3.2.
|
|
18
|
+
const VERSION = '3.2.2';
|
|
19
19
|
const PRODUCT = 'correctover-scan';
|
|
20
20
|
const JS_EXT = new Set(['.js', '.mjs', '.cjs', '.ts']);
|
|
21
21
|
|
|
@@ -373,10 +373,13 @@ function runBundleMode(target, format, outFile) {
|
|
|
373
373
|
}
|
|
374
374
|
if (files.length === 0) {
|
|
375
375
|
diag(`${c.yellow}No JavaScript files (.js/.mjs/.cjs) found under ${target}.${c.reset}`);
|
|
376
|
-
|
|
376
|
+
diag(`${c.dim}Emitting an empty (zero-result) report${outFile ? ` to ${outFile}` : ''}.${c.reset}\n`);
|
|
377
|
+
} else {
|
|
378
|
+
diag(`${c.dim}Bundle mode: ${files.length} JS file(s) under ${target}${c.reset}\n`);
|
|
377
379
|
}
|
|
378
|
-
diag(`${c.dim}Bundle mode: ${files.length} JS file(s) under ${target}${c.reset}\n`);
|
|
379
380
|
|
|
381
|
+
// An empty file list yields a valid zero-finding bundle (score 100) so that
|
|
382
|
+
// SARIF/JSON consumers downstream never see a missing artifact.
|
|
380
383
|
const bundle = runBundleScan(files, baseDir);
|
|
381
384
|
const label = path.relative(process.cwd(), abs) || abs;
|
|
382
385
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "correctover-scan",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "Correctover Security Scanner — CCS audit for MCP configurations AND published JS bundles/npm packages. Detects hardcoded secrets, RCE, SSRF, credential hijacking against OWASP AISVS 1.0.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|