---
name: develop-fingerprint
description: 当用户希望在平台侧编写、生成、完善、检查、保存、更新或删除指纹规则时使用。该 skill 用于生成符合 FingerprintComponent 约束的 HTTP/TCP 指纹 rule.requests 与 rule.script，并要求通过平台 FingerprintTools 查询已有指纹、读取详情、保存和删除，不直接读写 VFS 指纹文件。
---

# 开发平台侧指纹

使用这个 skill 辅助用户编写平台指纹。所有平台指纹管理动作都必须通过 Tools 完成，不要让 Agent 自己读写 `root/fingerprint`、VFS 文件或本地 JSON 文件。

可用工具由 `FingerprintTools` 提供：

- `listFingerprints(protocol?)`: 获取全部指纹摘要，或按 `http`、`tcp` 等协议筛选。
- `getFingerprintById(fingerprintId)`: 获取完整指纹对象，包含 `rule`。
- `saveFingerprint(userId, name, ruleJson, infoJson, protocol, tagsJson, version)`: 创建或覆盖保存指纹。
- `deleteFingerprint(userId, fingerprintId)`: 删除指定指纹。

如果需要保存或删除但缺少 `userId`，优先通过平台用户工具确认；仍无法确定时询问用户。不要猜测 `userId`。

## 工作流程

### 执行前：制定计划

查询、生成、自检、保存等多个依赖阶段同时存在时调用 `createPlan` 跟踪真实进度；
简单查看或单次解释直接执行，不额外输出固定计划模板。用户给了 fingerprintId 时，先用
`getFingerprintById` 读取当前内容。

---

1. 明确用户意图：
   - 只生成指纹草稿
   - 创建并保存新指纹
   - 修改已有指纹
   - 检查或解释已有指纹
   - 删除指纹
2. 查询现状：
   - 用户给了 `fingerprintId`：先调用 `getFingerprintById(fingerprintId)`。
   - 用户只给了产品名或协议：先调用 `listFingerprints(protocol?)`，检查是否已有相近指纹。
3. 生成或修改指纹：
   - 只生成 `name`、`protocol`、`tags`、`info`、`rule.requests`、`rule.script`。
   - 不手工生成或写入文件；`fingerprintId` 由保存工具按 `name + "_" + version` 生成。
4. 自检规则：
   - `rule.requests` 必须是非空数组。
   - `rule.script` 必须非空。
   - HTTP 指纹使用 HTTP 响应字段。
   - TCP 指纹使用 `protocol=tcp`，脚本读取 `resp[n].raw`。
   - 脚本对空值和请求失败保持容错。
5. 保存：
   - 用户明确要求“保存”“创建”“更新”时才调用 `saveFingerprint`。
   - 用户只要求“写一个”“给草稿”“看看怎么写”时不要保存。
   - 保存前如果同名同版本可能覆盖已有指纹，先说明覆盖风险，除非上下文已经明确是更新。
6. 输出：
   - 如果已调用 Tool，报告 Tool 动作和返回的 `fingerprintId`。
   - 如果未保存，明确说明这是草稿。

## 指纹对象规范

完整指纹对象推荐结构如下。保存 Tool 的 `ruleJson` 只传其中的 `rule` 对象，不传完整对象。

```json
{
  "name": "nginx",
  "protocol": "http",
  "tags": ["web", "server"],
  "info": {
    "version": "1.0",
    "description": "Detects Nginx by HTTP server header or default page markers."
  },
  "rule": {
    "requests": [
      {
        "method": "GET",
        "uri": "/",
        "timeout": 3000,
        "charset": "UTF-8",
        "maxBodyBytes": 1048576
      }
    ],
    "script": "var b = String(body || '').toLowerCase(); var h = String(headers || '').toLowerCase(); status == 200 && (h.indexOf('nginx') >= 0 || b.indexOf('welcome to nginx') >= 0)"
  }
}
```

必填字段：

- `name`: 指纹名称。
- `rule.requests`: 非空数组。
- `rule.script`: 非空 JavaScript 脚本。
- `info.version` 或 `version`: 保存时用于生成 `fingerprintId`。

推荐字段：

- `protocol`: `http` 或 `tcp`。
- `tags`: 短标签，例如 `web`、`server`、`database`、`middleware`、`cms`。
- `info.description`: 说明识别依据、适用版本和误报边界。

## saveFingerprint 参数

调用保存 Tool 时按以下方式拆分：

- `userId`: 创建/更新人。
- `name`: 指纹名称，例如 `nginx`。
- `ruleJson`: 只传 `rule` 对象的 JSON 字符串。
- `infoJson`: 传 JSON 对象字符串，至少包含 `version`。
- `protocol`: `http` 或 `tcp`。
- `tagsJson`: 传 JSON 数组字符串，例如 `["web","server"]`。
- `version`: 可直接传版本；如果为空，Tool 会从 `infoJson.version` 读取，仍为空则使用默认版本。

示例：

```text
saveFingerprint(
  userId="admin",
  name="nginx",
  ruleJson="{\"requests\":[{\"method\":\"GET\",\"uri\":\"/\",\"timeout\":3000}],\"script\":\"var b = String(body || '').toLowerCase(); var h = String(headers || '').toLowerCase(); status == 200 && (h.indexOf('nginx') >= 0 || b.indexOf('welcome to nginx') >= 0)\"}",
  infoJson="{\"version\":\"1.0\",\"description\":\"Detects Nginx by HTTP server header or default page markers.\"}",
  protocol="http",
  tagsJson="[\"web\",\"server\"]",
  version="1.0"
)
```

## HTTP 规则

HTTP request 支持：

```json
{
  "method": "GET",
  "uri": "/",
  "path": "/",
  "headers": {
    "User-Agent": "LeoAI-Fingerprint"
  },
  "body": "",
  "timeout": 3000,
  "charset": "UTF-8",
  "maxBodyBytes": 1048576
}
```

规则：

- `method` 默认 `GET`。
- `uri` 优先于 `path`；两者都为空时默认 `/`。
- `uri` 可以是绝对 URL；否则会和 target 的 `baseUrl` 拼接。
- 只有 `POST`、`PUT`、`PATCH` 且存在 `body` 时才发送请求体。
- 默认 `timeout=3000`、`charset=UTF-8`、`maxBodyBytes=1048576`。
- HTTP 不跟随重定向；HTTPS 信任所有证书。

HTTP 响应字段：

```javascript
resp[0].status
resp[0].body
resp[0].bodyLength
resp[0].truncated
resp[0].headers
```

只有一个响应时，组件会把响应字段提升为变量：

```javascript
status
body
bodyLength
truncated
headers
```

HTTP 脚本应组合多个证据，避免只靠弱关键字：

```javascript
var b = String(body || '').toLowerCase();
var h = String(headers || '').toLowerCase();
status == 200 && (h.indexOf('nginx') >= 0 || b.indexOf('welcome to nginx') >= 0)
```

## TCP 规则

TCP 指纹要求目标使用 `protocol=tcp`。request 支持：

```json
{
  "body": "PING\r\n",
  "timeout": 3000,
  "charset": "UTF-8",
  "maxBodyBytes": 1048576
}
```

TCP 响应字段：

```javascript
resp[0].raw
resp[0].bytes
resp[0].bodyLength
resp[0].truncated
```

TCP 脚本示例：

```javascript
var r = String(resp[0].raw || '');
r.indexOf('PONG') >= 0 || r.indexOf('NOAUTH') >= 0
```

## 脚本约束

`rule.script` 由 JVM JavaScript ScriptEngine 执行。返回以下值视为命中：

- Boolean `true`
- 非 0 数字
- 字符串 `"true"`，忽略大小写

编写脚本时：

- 使用 ES5 风格语法，避免依赖现代浏览器 API。
- 多请求规则使用 `resp[0]`、`resp[1]` 明确索引。
- 对 `body`、`raw`、`headers` 做字符串匹配前先用 `String(value || '')` 兜底。
- 某个 request 失败时，对应 `resp[i]` 是错误对象，包含 `requestIndex`、`errorType`、`error`。
- 不要编写会修改目标状态的探测请求，除非用户明确要求且场景合法。

## 输出格式

最终回复保持简洁，包含：

1. `计划`：目标服务、协议类型和执行步骤
2. `目标服务和协议`
3. `识别依据`
4. `生成的关键 rule 或完整 JSON 草稿`
5. `调用了哪些 Fingerprint Tools`
6. `是否已保存`
7. `保存后的 fingerprintId`（如已保存）
8. `下一步建议`：根据操作结果给出 1~2 条具体建议，例如：
   - 指纹已保存 → "建议在目标 Puppet 上执行指纹探测，验证识别效果"
   - 只生成了草案 → "草案已就绪，确认后可调用 saveFingerprint 保存"
   - 指纹规则较弱（单一关键词匹配）→ "建议增加多个证据组合（如 header + body），降低误报率"

如果 Tool 调用失败，说明失败的 Tool、错误信息和下一步修正建议。
