From 91c53ab451fe7c76dde9194d215b26d45ba294ea Mon Sep 17 00:00:00 2001 From: Ed1s0nZ Date: Tue, 6 Oct 2026 03:03:45 +0800 Subject: [PATCH] fix: preserve nmap script arguments when selecting scan options --- docs/bug-sheet-review.md | 6 ++++ docs/bug-sheet-status.md | 2 +- internal/security/executor.go | 9 +++-- internal/security/nmap_argument_order_test.go | 34 +++++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 internal/security/nmap_argument_order_test.go diff --git a/docs/bug-sheet-review.md b/docs/bug-sheet-review.md index eaf36b6d..98b6395d 100644 --- a/docs/bug-sheet-review.md +++ b/docs/bug-sheet-review.md @@ -102,3 +102,9 @@ - Excel 第 53 行:原截图的服务错误明确为 401 Unauthorized/Incorrect API key。页面改为按认证、权限、限流、超时、网络错误给出对应检查建议,保留原错误供定位;未知错误不推断原因。状态 API 不再五分钟后隐藏失败,下一次索引由现有 resetLastError 清除;空知识列表也显示已有失败。实际状态渲染与错误保留回归通过。 - Excel 第 54 行:当前启动、动态初始化及 ApplyConfig 路径均注册知识检索工具;真实 RegisterKnowledgeTool + Agent.ToolsForRole 验证默认角色和显式允许角色均含搜索/风险列表定义,显式不允许角色不被绕过。截图只有模型回复,缺少当时启用配置、工具目录和角色列表,不能据此证明当前注册缺陷;本项标为当前未复现,保留历史证据不足的限制。 + +## 追加:nmap 参数拼接 + +- Excel 第 47 行:截图显示 NSE 将 `-sT` 当作脚本名,原因是 scan_type 根据“最后一个非标志参数”找插入位置,遇到 additional_args 的 --script 值时插入到了标志与值之间。nmap 的 scan_type 改为置于参数列表开头,保留各 flag/value 相邻关系;其他工具的既有插入规则保留。 +- `-sC` 与 --script 不是互斥错误;官方说明明确有 --script 时忽略 -sC,故不新增错误拒绝规则。来源:https://nmap.org/book/man-nse.html 。 +- 读取真实 tools/nmap.yaml 验证截图参数、nse_scripts 字段和尾部 --max-retries 值的完整顺序;既有空可选参数测试通过。测试不向目标发送扫描;本机没有 nmap,因此不声称完成实际扫描验证。 diff --git a/docs/bug-sheet-status.md b/docs/bug-sheet-status.md index 9cb994ae..d75ee7fe 100644 --- a/docs/bug-sheet-status.md +++ b/docs/bug-sheet-status.md @@ -49,7 +49,7 @@ | 44 | — | 输入 目标地址 时,前端正确解析 IP/端口/协议,但提交提示“域名格式无效”,无法创建。 | 已修复 URL 与预填 IP 重复解析,IPv4/IPv6 回归通过 | | 45 | — | 无工具节点的工作流成功完成后,被误判为缺少执行证据 | 已修复无工具工作流终结与审批拒绝原因,实际运行回归通过 | | 46 | — | Agent ID 可重复- 两个不同文件可使用同一个 ID。 | 已修复并有针对性验证 | -| 47 | — | nmap 参数缺少前置校验 | 待核对 | +| 47 | — | nmap 参数缺少前置校验 | 已修复 scan_type 拆开 --script 与脚本名的拼接错误;实际 YAML 参数回归通过 | | 48 | — | 对单个资产发起“只读连通性与服务指纹核验”后,Agent 已通过 nmap 和 HTTP 指纹工 | 待核对 | | 49 | — | C2 会话终端执行没有换行结尾的命令时,系统内部用于获取当前目录的 pwd 输出会与用户命令输出 | 已修复,Unix Shell 回归通过 | | 50 | — | 监听器编辑后点击保存没有反应 | 已修复弹窗事件冒泡阻断;实际委托保存和失败提示回归通过 | diff --git a/internal/security/executor.go b/internal/security/executor.go index 0cf0e75a..e956c9f6 100644 --- a/internal/security/executor.go +++ b/internal/security/executor.go @@ -576,9 +576,12 @@ func (e *Executor) buildCommandArgs(toolName string, toolConfig *config.ToolConf if hasScanType { scanTypeArgs := e.parseAdditionalArgs(scanTypeValue) if len(scanTypeArgs) > 0 { - // 对于 nmap,scan_type 应该替换默认的扫描类型参数 - // 由于我们已经跳过了默认的 args,现在需要将 scan_type 插入到合适位置 - // 找到 target 参数的位置(通常是最后一个位置参数) + if toolName == "nmap" { + // Keep each option next to its value (especially --script). + // Nmap accepts scan options before all other arguments. + return append(scanTypeArgs, cmdArgs...) + } + // Preserve the existing insertion rule for other tools using scan_type. insertPos := len(cmdArgs) for i := len(cmdArgs) - 1; i >= 0; i-- { // target 通常是最后一个非标志参数 diff --git a/internal/security/nmap_argument_order_test.go b/internal/security/nmap_argument_order_test.go new file mode 100644 index 00000000..5c8f8b8c --- /dev/null +++ b/internal/security/nmap_argument_order_test.go @@ -0,0 +1,34 @@ +package security + +import ( + "cyberstrike-ai/internal/config" + "gopkg.in/yaml.v3" + "os" + "reflect" + "testing" +) + +func TestNmapScanTypeDoesNotSplitScriptOptionAndValue(t *testing.T) { + data, err := os.ReadFile("../../tools/nmap.yaml") + if err != nil { + t.Fatal(err) + } + var tool config.ToolConfig + if err := yaml.Unmarshal(data, &tool); err != nil { + t.Fatal(err) + } + executor, _ := setupTestExecutor(t) + for _, tc := range []struct { + additional, script string + expected []string + }{ + {"-Pn --script http-headers,http-title,http-server-header", "", []string{"-sT", "-sV", "-sC", "-p", "18081", "192.0.2.1", "-Pn", "--script", "http-headers,http-title,http-server-header"}}, + {"-Pn --max-retries 3", "http-title", []string{"-sT", "-sV", "-sC", "-p", "18081", "--script", "http-title", "192.0.2.1", "-Pn", "--max-retries", "3"}}, + } { + args := map[string]interface{}{"target": "192.0.2.1", "ports": "18081", "scan_type": "-sT -sV -sC", "additional_args": tc.additional, "nse_scripts": tc.script} + actual := executor.buildCommandArgs("nmap", &tool, args) + if !reflect.DeepEqual(actual, tc.expected) { + t.Fatalf("argument ordering: got %v want %v", actual, tc.expected) + } + } +}