Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ concurrency:
cancel-in-progress: false

# ---------------------------------------------------------------------------
# One frontend bundle + standard/full/runner build matrix -> verified bundle
# One frontend bundle + standard/full/runner CI build matrix -> verified bundle
# ---------------------------------------------------------------------------

jobs:
Expand Down Expand Up @@ -319,6 +319,9 @@ jobs:
for f in dist/build/*; do
[ -f "$f" ] || continue
base=$(basename "$f")
# Runner is built and smoke-tested in CI for Cairn, but is not a
# GitHub Release asset.
[[ "$base" == runner_* ]] && continue
archive_name="${base%.exe}"
binary="${base%%_*}"
inner_name="$binary"
Expand Down Expand Up @@ -367,8 +370,7 @@ jobs:
$expectedVersion = "aiscan $env:TAG"
$cases = @(
@{ Archive = 'aiscan_windows_amd64.zip'; Binary = 'aiscan.exe'; Full = $false },
@{ Archive = 'aiscan-full_windows_amd64.zip'; Binary = 'aiscan-full.exe'; Full = $true },
@{ Archive = 'runner_windows_amd64.zip'; Binary = 'runner.exe'; Full = $false; Runner = $true }
@{ Archive = 'aiscan-full_windows_amd64.zip'; Binary = 'aiscan-full.exe'; Full = $true }
)

foreach ($case in $cases) {
Expand All @@ -379,9 +381,8 @@ jobs:
if ($LASTEXITCODE -ne 0) {
throw "$($case.Binary) --version exited with $LASTEXITCODE"
}
$caseExpectedVersion = if ($case.Runner) { "runner $env:TAG" } else { $expectedVersion }
if ($version -ne $caseExpectedVersion) {
throw "$($case.Binary) reported '$version', expected '$caseExpectedVersion'"
if ($version -ne $expectedVersion) {
throw "$($case.Binary) reported '$version', expected '$expectedVersion'"
}
if ($case.Full) {
& $binary web --help | Out-Null
Expand Down
30 changes: 0 additions & 30 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,6 @@ builds:
gcflags:
- all=-trimpath={{.Env.GOPATH}}

- id: runner
main: ./cmd/runner
binary: runner
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
flags:
- -trimpath
ldflags:
- -s -w -X github.com/chainreactors/aiscan/core/config.Version={{.Version}}
asmflags:
- all=-trimpath={{.Env.GOPATH}}
gcflags:
- all=-trimpath={{.Env.GOPATH}}

- id: aiscan-full
main: ./cmd/aiscan
binary: "{{ .ProjectName }}-full"
Expand Down Expand Up @@ -109,15 +88,6 @@ archives:
- src: README.md
- src: docs/*

- id: runner
ids: [runner]
name_template: "runner_{{ .Os }}_{{ .Arch }}"
formats:
- zip
files:
- src: README.md
- src: docs/*

- id: aiscan-full
ids: [aiscan-full]
name_template: "{{ .ProjectName }}-full_{{ .Os }}_{{ .Arch }}"
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,12 @@ From [GitHub Releases](https://github.com/chainreactors/aiscan/releases/latest):
| --- | --- |
| **aiscan** | Standard — scan/agent/gogo/spray/zombie/neutron/proton/arsenal |
| **aiscan-full** | Full — adds Web, playwright, passive recon, and katana |
| **runner** | Single tag-free remote tool node |

| OS | Arch | Standard | Full | Runner |
| --- | --- | --- | --- | --- |
| Linux | amd64 / arm64 | `aiscan_linux_<arch>.zip` | `aiscan-full_linux_<arch>.zip` | `runner_linux_<arch>.zip` |
| macOS | Intel / Apple Silicon | `aiscan_darwin_<arch>.zip` | `aiscan-full_darwin_<arch>.zip` | `runner_darwin_<arch>.zip` |
| Windows | amd64 / arm64 | `aiscan_windows_<arch>.zip` | `aiscan-full_windows_amd64.zip` | `runner_windows_<arch>.zip` |
| OS | Arch | Standard | Full |
| --- | --- | --- | --- |
| Linux | amd64 / arm64 | `aiscan_linux_<arch>.zip` | `aiscan-full_linux_<arch>.zip` |
| macOS | Intel / Apple Silicon | `aiscan_darwin_<arch>.zip` | `aiscan-full_darwin_<arch>.zip` |
| Windows | amd64 / arm64 | `aiscan_windows_<arch>.zip` | `aiscan-full_windows_amd64.zip` |

```bash
# Linux
Expand Down
70 changes: 57 additions & 13 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,72 @@
# Changelog

## v1.0.0-rc2 — 流量与文件审计 + 单版本 Runner + 发布门禁
## v1.0.0-rc2 — 远程执行审计 + 单版本 Runner + 可验证发布

v1.0.0-rc2 聚焦远程 Runner 的原生运行时组装、可审计的流量与文件访问,以及发布链路的可重复验证。Runner 现在只有一个无 build tag 的实现和发行 profile,CI 与发布 wrapper 共用只读的 release-build workflow 完成构建、打包和 smoke test
v1.0.0-rc2 让远程 Runner 更容易部署、审计和维护:Runner 只有一个版本,可直接连接 AIScan Web;流量、文件访问和扫描结果都有明确边界;CI 会用正式发布矩阵验证每个候选版本

### New Features

- AOP 新增常驻流量捕获 namespace,proxy 共享统一 capture hub,并能按任务查询捕获结果。
- Runner 文件访问进入 task-scoped audit trail,并通过 AOP namespace 对控制面提供结构化记录。
- 官方 Release 新增 Linux、macOS、Windows amd64/arm64 的单一 `runner` 产物。
**单版本 Runner**

Runner 不再区分 full/non-full,也不需要 build tag。它与 AIScan 共用 `pkg/runner.App` 的原生运行时组装;CI 会编译并验证 Linux、macOS、Windows 的 amd64/arm64 版本,但 Runner 只供 Cairn 使用,不作为 GitHub Release 资产发布。

```bash
make runner
./bin/runner --server https://aiscan.example.com --token <token> --id office-linux
```

`make all` 也会自动构建 `bin/runner`。Windows 可使用本地构建出的 `runner.exe`;除命令行参数外,也可通过 `--config` 读取配置。`runner --version` 可用于确认部署版本。

**远程任务审计**

- proxy 使用常驻的统一 capture hub,可在不重启监听器的情况下切换 relay/capture;AOP traffic namespace 可查看状态并按任务查询 HTTP/HTTPS 流量,敏感请求头会在 Runner 侧脱敏。
- 开启 HTTPS 捕获时,工具会使用当前 Hub CA;关闭捕获后只保留流量转发,不再注入 CA。
- read、write、edit 和远程文件 RPC 会写入任务级文件审计;shell 命令通过工作目录快照记录文件变化,并标记记录来源。
- 审计不会阻塞任务,发生丢弃时会给出数量。shell 中未修改文件的读取无法由快照推断,因此不会被误报为已审计读取。

**Web 快速连接**

Web 会自动取得 Agent token,并根据远程执行节点的系统、架构和全球/中国下载源生成安装与连接命令;聊天输入框也会根据当前上下文给出操作入口。

### Improvements

- Runner 与 AIScan 共用 `pkg/runner.App` 的原生组装路径,删除重复 setup 与全局 hook 状态。
- `make runner` 直接、无 build tag 地构建 `./cmd/runner`;`make all` 自动包含 runner。
- 原生 record 工具改为显式 opt-in,默认 full 与官方 Release 不再隐式下载或链接 recorder SDK。
- macOS full 产物由 Linux 上的 Zig 与固定 SDK 交叉编译,工具链版本和校验和固定。
- CI 恢复 `go vet`,预编译 integration-tag 回归,并以只读 release-build 验证正式平台矩阵。
- Release notes 优先读取本文件中的对应版本章节,回退日志也会正确以上一个 prerelease 为基线。
**Runner 连接稳定性**

- 每个 Runner 进程携带独立实例标识,并通过存活 deadline 及时发现失效连接。
- WebSocket 会区分连接失败原因并显示 enrollment 拒绝信息;稳定连接后会重置重连退避。
- 连接、keep-alive 与 producer 生命周期收敛到 session,减少命令结束或重连时的遗留状态。

**扫描结果与运行时边界**

- scanner-native Artifact 在产生处执行体积预算;工具结束或取消后到达的尾随 Artifact 会被丢弃,避免大结果或晚到结果污染后续会话。
- AOP 工具文本在 UTF-8 边界清洗,截断内容不会产生无效字符。
- shell 命令默认保持前台执行;设置 `wait: N` 后,运行超过 N 秒的命令才会转入后台并通过 inbox 返回进度。默认 `timeout` 为 600 秒,显式设为 `0` 表示不限时。
- harness prompt 与 scan skill 分离;Katana 升级到 v1.7.0,并修正 gogo/scan 文档中无效的 `top100` / `top1000` 端口 preset。
- record 改为显式 opt-in;默认 full 和官方 Release 不再下载或链接 recorder SDK。
- Go module 可在独立 checkout 中解析,不再依赖相邻仓库的本地目录结构;新增 Agent 架构文档,运行时与协议边界更明确。

**CI 与发布**

- CI 执行 `go vet`、lint 和 integration-tag 编译,并验证冷缓存 protobuf 生成。
- 修复 headless 初始化竞态和 CDP deadline;Playwright 浏览器安装绕过易挂起的 apt mirror,并增加超时与重试。
- release build 与发布权限分离;PR 和 master 都使用正式矩阵构建、生成 checksum,并对 Windows 产物执行启动 smoke test。
- Release notes 会读取本文件中对应版本的章节,并以上一个 prerelease 作为回退日志基线。

### Bug Fixes

- 修复 scanner-regression 因 integration test 遗留未使用 import 而持续无法编译的问题。
- 合入 rc1 后的 Node WebSocket 稳定性、尾随 artifact 丢弃、UTF-8 边界清洗和 scanner artifact 体积预算修复。
- 子进程退出时会排空 PTY 尾部输出,修复 `tmux capture-pane -c` 偶发返回空结果。
- inbox 会在所有 producer 结束时正确唤醒;LoopScheduler 统一注册 producer,loop 生命周期绑定 session 而非单次命令 context。
- 修复 integration 回归中的静态分析错误,以及 cyber-ui 文件访问协议版本未固定导致的生成漂移。

### Release Matrix

| 产物 | Linux | macOS | Windows | 数量 |
| --- | --- | --- | --- | ---: |
| `aiscan` | amd64、arm64 | amd64、arm64 | amd64、arm64 | 6 |
| `aiscan-full` | amd64、arm64 | amd64、arm64 | amd64 | 5 |
| `checksums.txt` | — | — | — | 1 |

Release 只包含 `aiscan`、`aiscan-full` 和 checksum;Runner 始终只有一个版本,没有 `runner-full`,由 `make runner` 构建后交给 Cairn 使用。

## v1.0.0-rc1 — 原生录屏 + 浏览器自动化扩展 + 稳定接口候选

Expand Down
7 changes: 3 additions & 4 deletions docs/v1.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ v1.0.0 是 AIScan 的首个稳定接口基线。此前版本用于快速迭代
| --- | --- | --- |
| `aiscan` | Linux/macOS/Windows amd64、arm64 | standard:scan、agent、IOA 和纯 Go 工具集 |
| `aiscan-full` | Linux/macOS amd64、arm64;Windows amd64 | standard + Web、Playwright、passive、katana |
| `runner` | Linux/macOS/Windows amd64、arm64 | 单一、无 build tag 的远程工具节点 |

macOS standard/full 均由 Linux runner 交叉编译,不使用 macOS 原生 runner;Windows arm64 只发布 standard。原生 `record` 不包含在官方 full 产物中,SDK 和工具开发者可在 Linux/Windows 支持的平台上显式启用。
macOS standard/full 均由 Linux runner 交叉编译,不使用 macOS 原生 runner;Windows arm64 只发布 standard。原生 `record` 不包含在官方 full 产物中,SDK 和工具开发者可在 Linux/Windows 支持的平台上显式启用。Cairn Runner 由 `make runner` 构建并在 CI 验证,不作为 GitHub Release 资产发布。

## 从 pre-v1 迁移

Expand Down Expand Up @@ -59,8 +58,8 @@ PTY/terminal 路由位于 `pkg/terminal`。`core` 仅保留 AIScan 的领域配
- v1.x 内不会无提示删除公开 CLI 参数、配置字段或已发布的 protobuf 字段。
- 新增 protobuf 字段只使用新的 field number;已发布字段不会重编号或复用。
- 必须破坏接口的变更会在 changelog 和迁移文档中说明,并进入下一个 major 版本。
- standard/full 的能力和平台矩阵以本文件及 release workflow 为准。
- standard/full 的能力和平台矩阵以本文件及 release workflow 为准;Cairn Runner 不属于发布资产矩阵

## 发布门禁

正式发布前需通过:Go 单元/竞态/架构测试、`go vet`、`golangci-lint`、protobuf 与资源生成一致性、standard/full/runner 构建、Web 前端构建与 E2E、cyber-ui viewer 测试、Windows 产物启动验证。CI 与发布 wrapper 共用只读的 release-build workflow,执行同一套构建、打包和 smoke test;只有门禁通过后的发布 wrapper 能创建 Git tag 和 Release。依赖漏洞报告单独跟踪,不作为本次 v1.0.0 发布门禁。
正式发布前需通过:Go 单元/竞态/架构测试、`go vet`、`golangci-lint`、protobuf 与资源生成一致性、standard/full 构建、Cairn Runner CI 构建、Web 前端构建与 E2E、cyber-ui viewer 测试、Windows 产物启动验证。CI 与发布 wrapper 共用只读的 release-build workflow,执行同一套构建、打包和 smoke test;只有门禁通过后的发布 wrapper 能创建 Git tag 和 Release。依赖漏洞报告单独跟踪,不作为本次 v1.0.0 发布门禁。
38 changes: 7 additions & 31 deletions internal/repositorytest/architecture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,42 +107,18 @@ func TestRunnerIsSingleTagFreeImplementation(t *testing.T) {

releaseWorkflow := readRepositoryFile(t, root, filepath.Join(".github", "workflows", "release-build.yml"))
if count := strings.Count(releaseWorkflow, "main: ./cmd/runner"); count != 1 {
t.Fatalf("release workflow must contain exactly one runner build, got %d", count)
t.Fatalf("CI release matrix must contain exactly one runner build, got %d", count)
}
runnerStart := strings.Index(releaseWorkflow, " - id: runner\n")
if runnerStart < 0 {
t.Fatal("release workflow is missing the runner build")
if !strings.Contains(releaseWorkflow, "[[ \"$base\" == runner_* ]] && continue") {
t.Error("release packaging must exclude runner archives")
}
runnerBuild := releaseWorkflow[runnerStart:]
if next := strings.Index(runnerBuild[1:], "\n - id:"); next >= 0 {
runnerBuild = runnerBuild[:next+1]
}
for _, required := range []string{
"profile: runner",
"main: ./cmd/runner",
"binary: runner",
"tags: \"\"",
"linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64",
} {
if !strings.Contains(runnerBuild, required) {
t.Errorf("runner release build is missing %q", required)
}
if strings.Contains(releaseWorkflow, "runner_windows_amd64.zip") {
t.Error("Windows release smoke must not require a runner archive")
}

goreleaser := readRepositoryFile(t, root, ".goreleaser.yml")
if count := strings.Count(goreleaser, "main: ./cmd/runner"); count != 1 {
t.Fatalf("GoReleaser must contain exactly one runner build, got %d", count)
}
runnerStart = strings.Index(goreleaser, " - id: runner\n")
if runnerStart < 0 {
t.Fatal("GoReleaser is missing the runner build")
}
runnerBuild = goreleaser[runnerStart:]
if next := strings.Index(runnerBuild[1:], "\n - id:"); next >= 0 {
runnerBuild = runnerBuild[:next+1]
}
if strings.Contains(runnerBuild, "\n tags:") {
t.Error("GoReleaser runner build must not use build tags")
if strings.Contains(goreleaser, "main: ./cmd/runner") || strings.Contains(goreleaser, "ids: [runner]") {
t.Error("GoReleaser must not publish runner builds or archives")
}
}

Expand Down
Loading