correctover-scan 3.1.1 → 3.2.1
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/core/skillpay.js +36 -2
- package/index.js +18 -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/core/skillpay.js
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* SkillPay (Alipay) acquisition bridge for users in mainland China.
|
|
3
3
|
*
|
|
4
4
|
* One public shelf URL covers every purchasable product; single-product deep
|
|
5
|
-
* links, if later provided, take precedence automatically.
|
|
6
|
-
*
|
|
5
|
+
* links, if later provided, take precedence automatically.
|
|
6
|
+
*
|
|
7
|
+
* Prices ARE printed by checkoutBlock() — explicitly authorized 2026-09-27 to
|
|
8
|
+
* close the scan→payment gap. Keep them in sync with the live Alipay shelf.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
'use strict';
|
|
@@ -18,6 +20,12 @@ const PRODUCTS = {
|
|
|
18
20
|
audit: { name: 'CCS收据批量审计', url: '' },
|
|
19
21
|
};
|
|
20
22
|
|
|
23
|
+
// Shelf prices (CNY). Owner-supplied 2026-09-27; keep identical to the shelf.
|
|
24
|
+
const OFFERS = {
|
|
25
|
+
cert: { label: 'CCS 运行时认证 · 正式合规凭证', price: '¥699/年' },
|
|
26
|
+
checkup: { label: 'MCP 安全体检 · 持续监控 + 月报', price: '¥49.9/月' },
|
|
27
|
+
};
|
|
28
|
+
|
|
21
29
|
function isChinaUser() {
|
|
22
30
|
if (process.env.CORRECTOVER_REGION === 'CN') return true;
|
|
23
31
|
if (process.env.CORRECTOVER_REGION === 'GLOBAL') return false;
|
|
@@ -36,6 +44,30 @@ function shelfUrlFor(key) {
|
|
|
36
44
|
return (p && p.url) || STORE_URL;
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Scan→payment checkout block. Personalized by the number of risks found so the
|
|
49
|
+
* user moves from "you're at risk" straight to a payable next step.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} riskCount fail-level findings in this scan
|
|
52
|
+
* @returns {string} multi-line block, or '' for non-China users
|
|
53
|
+
*/
|
|
54
|
+
function checkoutBlock(riskCount) {
|
|
55
|
+
if (!isChinaUser()) return '';
|
|
56
|
+
const n = Number.isFinite(riskCount) ? riskCount : 0;
|
|
57
|
+
const url = shelfUrlFor('scan');
|
|
58
|
+
const out = [];
|
|
59
|
+
out.push('🇨🇳 支付宝 SkillPay · 检出风险后直接处理');
|
|
60
|
+
if (n > 0) {
|
|
61
|
+
out.push(` 本次有 ${n} 项风险提示。别让它停在「你完蛋了」——`);
|
|
62
|
+
} else {
|
|
63
|
+
out.push(' 本次未发现风险。趁现在拿到正式凭证——');
|
|
64
|
+
}
|
|
65
|
+
out.push(` · ${OFFERS.cert.label} ${OFFERS.cert.price}`);
|
|
66
|
+
out.push(` · ${OFFERS.checkup.label} ${OFFERS.checkup.price}`);
|
|
67
|
+
out.push(` 打开即扫码付款:${url}`);
|
|
68
|
+
return out.join('\n');
|
|
69
|
+
}
|
|
70
|
+
|
|
39
71
|
/**
|
|
40
72
|
* Full China acquisition block. Returns '' for non-China users so behaviour is
|
|
41
73
|
* identical to pre-bridge releases unless a CN user is detected.
|
|
@@ -66,7 +98,9 @@ function ctaLine(productKey) {
|
|
|
66
98
|
module.exports = {
|
|
67
99
|
STORE_URL,
|
|
68
100
|
PRODUCTS,
|
|
101
|
+
OFFERS,
|
|
69
102
|
isChinaUser,
|
|
103
|
+
checkoutBlock,
|
|
70
104
|
block,
|
|
71
105
|
ctaLine,
|
|
72
106
|
};
|
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.
|
|
18
|
+
const VERSION = '3.2.0';
|
|
19
19
|
const PRODUCT = 'correctover-scan';
|
|
20
20
|
const JS_EXT = new Set(['.js', '.mjs', '.cjs', '.ts']);
|
|
21
21
|
|
|
@@ -133,7 +133,7 @@ function formatResults(results, stats, filename) {
|
|
|
133
133
|
lines.push('');
|
|
134
134
|
lines.push(`${c.dim}Free automated scan covers surface-level checks. A manual Correctover audit goes deeper: 116 semantic intent rules, 5-day turnaround, findings grounded in real MCP ecosystem CVEs. First customers: if we find no critical-severity issue, you pay nothing. \u2192 ${c.cyan}https://dshcorrectover.github.io/agent-audit/${c.reset}`);
|
|
135
135
|
|
|
136
|
-
const cnBlock = skillpay.
|
|
136
|
+
const cnBlock = skillpay.checkoutBlock(stats.fail + stats.warn);
|
|
137
137
|
if (cnBlock) {
|
|
138
138
|
lines.push('');
|
|
139
139
|
lines.push(cnBlock);
|
|
@@ -212,7 +212,7 @@ function formatBundleResults(bundle, baseLabel) {
|
|
|
212
212
|
lines.push('');
|
|
213
213
|
lines.push(`${c.dim}Web: https://correctover.com/scan/ | GitHub: https://github.com/DSHCorrectover${c.reset}`);
|
|
214
214
|
|
|
215
|
-
const cnBundleBlock = skillpay.
|
|
215
|
+
const cnBundleBlock = skillpay.checkoutBlock(activeRiskCount(bundle));
|
|
216
216
|
if (cnBundleBlock) {
|
|
217
217
|
lines.push('');
|
|
218
218
|
lines.push(cnBundleBlock);
|
|
@@ -220,6 +220,21 @@ function formatBundleResults(bundle, baseLabel) {
|
|
|
220
220
|
return lines.join('\n');
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
function activeRiskCount(bundle) {
|
|
224
|
+
// Every unsuppressed warn/fail finding is something the user should act on,
|
|
225
|
+
// not just confirmed fails (the bundle engine suppresses known-benign
|
|
226
|
+
// signals and can under-classify simple variable args as static).
|
|
227
|
+
let n = 0;
|
|
228
|
+
for (const fr of bundle.files) {
|
|
229
|
+
for (const r of fr.results) {
|
|
230
|
+
for (const f of r.findings) {
|
|
231
|
+
if (!f.suppressed && (f.severity === 'fail' || f.severity === 'warn')) n++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return n;
|
|
236
|
+
}
|
|
237
|
+
|
|
223
238
|
function formatBundleJSON(bundle, target) {
|
|
224
239
|
return JSON.stringify({
|
|
225
240
|
scanner: 'correctover-scan', version: VERSION, mode: 'bundle', target,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "correctover-scan",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
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": {
|