---
slug: overview
title: 插件开发总览与快速开始
titleEn: Plugin Development Overview
summary: 一页看懂 TestSecScan 的四种插件形态、两套 WASM SDK 的适用边界、签名与上架流程，以及该从哪篇文档开始。
summaryEn: A single page covering the four plugin forms, which WASM SDK applies where, the signing and publishing flow, and where to start.
category: 总览
order: 10
enabled: true
updatedAt: "2026-10-05"
---
# 插件开发总览与快速开始

> 本文面向**第三方开发者**：你只需要 TestSecScan 的官方发行包 + 本文档，就能从零写出一个可被控制器、扫描节点或 AI 渗透代理调用的插件。

[[toc]]

## 一、四种插件形态，先选对赛道

TestSecScan 的"插件"并不是一个东西，而是四套互相独立、各有 SDK 与签名体系的扩展点。**选错赛道的典型症状是：代码能编译，但宿主永远不会调用你。**

| 形态 | 扩展点 | 宿主 | 开发语言 | SDK / 文档 |
|---|---|---|---|---|
| 控制器插件 | 订阅并处理控制器转发的 TCP 指令 | 控制器 | Go / Rust / C / C++ (WASI) | [控制器插件开发](/docs/controller-plugin) |
| WASM POC 模板 | 处理单个流量包，产出漏洞发现 | 扫描节点 | Go / Rust / C / C++ (WASI) | [WASM POC 模板开发](/docs/scan-poc-wasm) |
| 应用插件 | 钩住扫描节点业务点（任务/流量/MITM） | 扫描节点 | Go / Rust / C / C++ (WASI, c-shared) | [应用插件开发](/docs/scan-app-plugin) |
| AIAgent 外部工具 | 作为大模型可调用的一个"工具" | AI 渗透代理 | 任意可执行程序 / WASI wasm | [AiAgent 外部工具开发](/docs/aiagent-tool) |

另有 **Go 热加载 POC**（见 [Go 热加载 POC 开发](/docs/go-poc-hotload)）：它不是插件，而是**漏洞模板的一种形态**，用纯 Go 源码编写、由扫描节点**在进程内解释执行**（目标机无需安装 Go 工具链），适合"需要一个图灵完备脚本、但不值得走 WASM 构建链"的场景。

## 二、两套 WASM SDK 的边界（最容易踩的坑）

扫描节点为两类 WASM 模块注入了**两套完全不重叠的宿主函数**：

| | WASM POC 模板（`scan_*`） | 应用插件（`app_*`） |
|---|---|---|
| 宿主命名空间 | `scan_http` / `scan_log` / `scan_report` / `scan_call` / `scan_t` / `scan_config` / `scan_set_timeout` | `app_log` / `app_send_tcp*` / `app_call` / `app_spawn_tool` / … |
| 入口 | `_start`（`func main`），一次处理一个流量包 | `应用_` 前缀的**导出函数**，按钩子调用 |
| 构建方式 | 普通 wasip1 命令模块 | **必须 `-buildmode=c-shared`** |
| 生效场景 | **仅 POC 模板列表**（漏洞模板）中 `PocType=wasm` 的条目 | 插件商店安装的扫描节点应用插件 |
| SDK 文件（在下载的 SDK 包里） | `scan.go` | `app.go` |

**关键限制：`scan_*` 这组宿主函数只在"POC 模板执行"的调用路径上注入。**也就是说，把 `scan.HTTP(...)` 写进应用插件、或把 `app.SpawnTool(...)` 写进 WASM POC，都会因为宿主没有导出对应函数而**实例化失败**。判断方法很简单：

- 你的产物在 **POC 模版列表**里作为一条漏洞被扫描 → 用 `scan.*`；
- 你的产物在 **插件商店 / 应用插件目录**里作为常驻插件跑 → 用 `app.*`。

同理，控制器插件用 `ctl_*`，AiAgent 外部工具用 stdin/stdout JSON——四套互不通用。

## 三、最短上手路径

```text
① 选形态（上表）
② 复制对应 SDK 到你的工程（每篇文档都给了 go.mod 写法）
③ 本地编译成 .wasm / 可执行文件
④ 本地签名（控制器密钥 Ed25519）
⑤ 放进宿主加载目录，或提交到插件商店 / POC 商店
⑥ 在 GUI 里看到它、触发它、看日志
```

每一篇分册文档都包含：**目录结构 → 编译命令 → 全部公开函数逐个说明（参数/返回/场景/示例）→ 注意事项 → 完整可运行示例 → 调试方法**。

### 每篇分册怎么读（统一结构）

五篇分册都按同一套结构写，按下面的顺序读不会卡住：

| 章节 | 你会得到什么 |
|---|---|
| 一、它是什么 / 什么时候该写它 | 选型判断，避免写错赛道 |
| **入口在哪：宿主怎么加载并调用你** | 文件放哪 → 怎么签名/校验 → 宿主何时编译/实例化 → **入口函数何时被调用** → 数据怎么进（stdin / 主机函数）、结果怎么出 → 怎么重载 |
| 逐个函数 / 钩子 / 字段精讲 | **每个公开符号一节**：作用 → 完整签名 → **参数表（传什么、从哪来、示例值）** → **返回（字段表 + 怎么取值 + 失败时的表现）** → **可复制代码片段** → 注意事项 |
| 内置函数 / 宿主函数全表 | 底层 `env` 宿主函数与内置工具函数的参数、返回值、ID 对照 |
| 完整示例 | 从最简到进阶，可直接照抄改 |
| **调试手册** | 日志在哪看 → 5 分钟最小验证流程 → 「症状 → 原因 → 解决」表格 |
| 注意事项清单 | 每条都写清"写错会怎样" |

> [!TIP]
> 遇到"这个函数返回什么、我怎么拿到返回值"时，直接跳到该函数的 `####` 小节看**参数表 + 返回 + 代码片段**三件套；遇到"我的插件根本没被调用"时，先看**入口在哪**那一章（九成是没导出/没签名/没声明能力）。

## 四、下载：文档与 SDK 包

每篇文档页**右上角**都有下载按钮，也可以直接用下面的固定地址（随版本更新，始终指向当前生效版本）：

| 要下载的东西 | 地址 |
|---|---|
| 本页 Markdown（单篇） | 文档页右上角「下载 Markdown」，或 `/api/v1/site/devdocs/download/<slug>` |
| 全部文档（打包 .zip，含 README 索引） | `/api/v1/site/devdocs/archive.zip` |
| 控制器插件 SDK（`ctl`：Go/C/C++/Rust） | `/api/v1/site/devdocs/sdk/controller.zip` |
| WASM POC 模板 SDK（`scan`：Go/C/C++/Rust） | `/api/v1/site/devdocs/sdk/wasm-scan.zip` |
| 应用插件 SDK（`app`：Go/C/C++/Rust） | `/api/v1/site/devdocs/sdk/wasm-app.zip` |
| Go 热加载 POC 参考包（`scanapi.go` + 类型定义） | `/api/v1/site/devdocs/sdk/gopoc.zip` |
| 全部 SDK 一次打包 | `/api/v1/site/devdocs/sdk/all.zip` |

每个 SDK 包内都有独立的 `README.md`，写清了**工程怎么接（`go.mod` 的 `replace` 怎么写）、编译命令、注意事项**；
解压后把整个目录拷进你的插件工程即可。

> [!NOTE]
> SDK 包内的源码是**快照**（与本平台各端工程里的 SDK 逐字节一致，由护栏测试锁定）。
> 官方会通过后台发布更新（下载按钮上标注当前版本，如 `v1.0.0`）；升级后重新下载即可拿到最新 SDK，
> 若你已把旧 SDK 拷进工程，请对比后替换。

## 五、签名与上架（安全硬门槛）

TestSecScan 的四类扩展**一律"无签名不加载"**，且验签使用**宿主持有的公钥**，绝不信任包内自带的公钥：

- 控制器 / 扫描节点 WASM：Ed25519 签名，`sig.json` 内 `signStatus` 必须为 `verified` 或 `official`；
- AiAgent 外部工具：对**整包摘要**签名（目录内除 `sig.json` 外全部文件按相对路径升序参与 SHA-256），加载与每次调用前**各验一次**（防 TOCTOU）；
- 签名私钥只有控制器持有；第三方开发者通过 GUI「一键签名」或官方构建服务完成签名。

> [!WARNING]
> 不要试图用自签密钥对绕过校验：宿主只认控制器下发的公钥，自签的包会被判为 `verify_failed`，模型/扫描引擎**看不到**它。这不是"看到了但拒绝执行"，而是根本不会进入注册表。

## 六、文档维护约定（面向后续 SDK 演进）

本套开发文档与 SDK 源码**同步演进**：

1. 新增/修改任何公开 SDK 函数后，必须同步更新对应分册，并在本页"两套 WASM SDK 的边界"等对照表中登记；
2. 仓库内置了 SDK 覆盖护栏测试（`buildservice/devdocs/coverage_test.go`），会解析各端 SDK 源码做两道断言：① 每个公开符号都出现在文档正文里；② 每个公开符号都有**以它命名的 `####` 小节且小节内含代码片段**（即"每个函数都要有怎么调用、怎么取返回值的例子"）。**漏写或只写一句话说明都会导致测试失败**，从而强制同步；
3. 文档正文可在后台「官网设置 → 开发文档」在线修改，修改以覆盖层落盘，内置版本随版本升级更新，可一键"恢复默认"。

<!-- en -->
# Plugin Development Overview and Quick Start

> This article is for **third-party developers**: all you need is the official TestSecScan distribution package plus this documentation to write, from scratch, a plugin that the controller, a scan node, or the AI penetration agent can invoke.

[[toc]]

## 1. Four Plugin Forms: Choose the Right Track First

A TestSecScan "plugin" is not a single thing but four independent extension points, each with its own SDK and signing system. **The classic symptom of choosing the wrong track is: your code compiles, but the host will never call it.**

| Form | Extension point | Host | Development language | SDK / Docs |
|---|---|---|---|---|
| Controller plugin | Subscribes to and handles TCP commands forwarded by the controller | Controller | Go / Rust / C / C++ (WASI) | [Controller Plugin Development](/docs/controller-plugin) |
| WASM POC template | Handles one traffic packet and produces vulnerability findings | Scan node | Go / Rust / C / C++ (WASI) | [WASM POC Template Development](/docs/scan-poc-wasm) |
| Application plugin | Hooks into scan node business points (task / traffic / MITM) | Scan node | Go / Rust / C / C++ (WASI, c-shared) | [Application Plugin Development](/docs/scan-app-plugin) |
| AIAgent external tool | Acts as a "tool" the large model can call | AI penetration agent | Any executable program / WASI wasm | [AiAgent External Tool Development](/docs/aiagent-tool) |

There is also the **Go hot-load POC** (see [Go Hot-Load POC Development](/docs/go-poc-hotload)): it is not a plugin but **one form of vulnerability template**. It is written in plain Go source and **interpreted in-process** by the scan node (the target machine needs no Go toolchain), which suits scenarios where "you need a Turing-complete script, but a full WASM build chain is not worth it".

## 2. The Boundary Between the Two WASM SDKs (the Easiest Trap to Fall Into)

A scan node injects **two completely non-overlapping sets of host functions** for the two kinds of WASM modules:

| | WASM POC template (`scan_*`) | Application plugin (`app_*`) |
|---|---|---|
| Host namespace | `scan_http` / `scan_log` / `scan_report` / `scan_call` / `scan_t` / `scan_config` / `scan_set_timeout` | `app_log` / `app_send_tcp*` / `app_call` / `app_spawn_tool` / … |
| Entry point | `_start` (`func main`), one traffic packet per invocation | **Exported functions** prefixed with `应用_`, called per hook |
| Build mode | An ordinary wasip1 command module | **Requires `-buildmode=c-shared`** |
| Where it takes effect | **Only entries with `PocType=wasm`** in the POC template list (vulnerability templates) | Scan node application plugins installed from the plugin store |
| SDK file (inside the downloaded SDK package) | `scan.go` | `app.go` |

**Key limitation: the `scan_*` host functions are injected only on the "POC template execution" call path.** In other words, putting `scan.HTTP(...)` into an application plugin, or `app.SpawnTool(...)` into a WASM POC, will make instantiation **fail**, because the host does not export the corresponding functions. Telling the two apart is easy:

- Your artifact is scanned as a vulnerability from the **POC template list** → use `scan.*`;
- Your artifact runs as a resident plugin from the **plugin store / application plugin directory** → use `app.*`.

By the same token, controller plugins use `ctl_*` and AiAgent external tools use stdin/stdout JSON — the four sets are not interchangeable.
## 3. The Shortest Path to Getting Started

```text
① Choose the form (table above)
② Copy the matching SDK into your project (every article shows how to wire up go.mod)
③ Compile locally into a .wasm file / executable
④ Sign locally (controller key, Ed25519)
⑤ Put it into the host's load directory, or submit it to the plugin store / POC store
⑥ See it in the GUI, trigger it, read the logs
```

Every chapter article covers: **directory layout → build commands → every public function explained one by one (parameters / returns / scenarios / examples) → caveats → complete runnable examples → debugging methods**.

### How to Read Each Chapter Article (Uniform Structure)

All five chapter articles follow the same structure; reading them in the order below will keep you unstuck:

| Section | What you get |
|---|---|
| 1. What it is / when you should write it | Choosing the right form; avoiding the wrong track |
| **Where the entry point is: how the host loads and calls you** | Where to put the file → how to sign / verify → when the host compiles / instantiates → **when the entry function is called** → how data goes in (stdin / host functions) and how results come out → how to reload |
| Function / hook / field walkthroughs | **One section per public symbol**: purpose → full signature → **parameter table (what to pass, where it comes from, example values)** → **returns (field table + how to read the value + behavior on failure)** → **copy-ready code snippet** → caveats |
| Built-in / host function reference | Parameters, return values and ID cross-reference for the low-level `env` host functions and built-in tool functions |
| Complete examples | From minimal to advanced, ready to copy and adapt |
| **Debugging handbook** | Where to find logs → a 5-minute minimal verification flow → a "symptom → cause → fix" table |
| Caveats checklist | Every entry spells out "what happens if you get it wrong" |

> [!TIP]
> When you wonder "what does this function return, and how do I get the return value", jump straight to that function's `####` section and read the three-part set: **parameter table + returns + code snippet**. When "my plugin is never called at all", read the **Where the entry point is** chapter first (nine times out of ten, you forgot to export it / sign it / declare the capability).

## 4. Downloads: Documentation and SDK Packages

Every documentation page has download buttons in the **top-right corner**, and you can also use the fixed addresses below (they follow releases and always point to the version currently in effect):

| What to download | Address |
|---|---|
| This page as Markdown (single article) | "Download Markdown" in the page's top-right corner, or `/api/v1/site/devdocs/download/<slug>` |
| All documentation (packed .zip, with a README index) | `/api/v1/site/devdocs/archive.zip` |
| Controller plugin SDK (`ctl`: Go/C/C++/Rust) | `/api/v1/site/devdocs/sdk/controller.zip` |
| WASM POC template SDK (`scan`: Go/C/C++/Rust) | `/api/v1/site/devdocs/sdk/wasm-scan.zip` |
| Application plugin SDK (`app`: Go/C/C++/Rust) | `/api/v1/site/devdocs/sdk/wasm-app.zip` |
| Go hot-load POC reference package (`scanapi.go` + type definitions) | `/api/v1/site/devdocs/sdk/gopoc.zip` |
| All SDKs in a single package | `/api/v1/site/devdocs/sdk/all.zip` |

Every SDK package contains its own `README.md` explaining **how to wire it into your project (how to write the `replace` directive in `go.mod`), the build commands, and the caveats**; after unzipping, copy the whole directory into your plugin project.

> [!NOTE]
> The source code inside each SDK package is a **snapshot** (byte-for-byte identical to the SDKs in this platform's per-end projects, locked down by guardrail tests).
> Official updates are published through the backend (the download button shows the current version, e.g. `v1.0.0`); download again after an upgrade to get the latest SDK,
> and if you have already copied an older SDK into your project, compare and replace it.

## 5. Signing and Publishing (a Hard Security Gate)

All four kinds of TestSecScan extensions follow the same rule: **no signature, no loading** — and signature verification uses **the public key held by the host**, never a public key bundled inside the package:

- Controller / scan node WASM: Ed25519 signature; inside `sig.json`, `signStatus` must be `verified` or `official`;
- AiAgent external tool: the signature covers the **whole-package digest** (every file in the directory except `sig.json`, hashed with SHA-256 in ascending relative-path order), and verification runs **once at load time and once before every invocation** (TOCTOU protection);
- The signing private key is held only by the controller; third-party developers sign through one-click signing in the GUI or through the official build service.

> [!WARNING]
> Do not try to bypass verification with a self-signed key pair: the host only trusts the public key delivered by the controller, so a self-signed package is judged `verify_failed` and the model / scan engine **cannot see** it. This is not "it was seen but execution was refused" — it never enters the registry at all.

## 6. Documentation Maintenance Conventions (for Future SDK Evolution)

This documentation set and the SDK source code **evolve together**:

1. After adding or modifying any public SDK function, you must update the corresponding chapter article and register it in the comparison tables on this page, such as "The Boundary Between the Two WASM SDKs";
2. The repository ships an SDK coverage guardrail test that parses the SDK source of each end and makes two assertions: ① every public symbol appears in the documentation body; ② every public symbol has a `####` section named after it, and that section contains a code snippet (in other words, "every function needs an example of how to call it and how to read its return value"). **Omitting it, or writing only a one-line description, fails the test**, which forces the documentation to stay in sync;
3. The documentation body can be edited online in the backend under "Site Settings → Developer Docs"; edits are persisted as an override layer, while the built-in version is refreshed with each release and can be restored to the default with one click.
