ai-saas-guard 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -12
- package/README.zh-CN.md +293 -0
- package/dist/hosted/contracts.d.ts +209 -0
- package/dist/hosted/contracts.js +414 -1
- package/docs/github-action.md +1 -1
- package/docs/github-app-design.md +14 -0
- package/docs/hosted-deployment-model.md +222 -0
- package/docs/hosted-first-service-slice.md +199 -0
- package/docs/hosted-operational-release-gate.md +226 -0
- package/docs/hosted-preimplementation-contracts.md +185 -0
- package/docs/hosted-pricing-packaging.md +195 -0
- package/docs/hosted-uninstall-data-deletion.md +166 -0
- package/docs/npm-publishing.md +3 -3
- package/docs/project-handoff.md +13 -11
- package/docs/release-quality-knowledge-base.md +1 -0
- package/examples/hosted-compact-report.json +38 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
<h1 align="center">ai-saas-guard</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>
|
|
4
|
+
<strong>You used AI to build your SaaS. Now you need to know what is risky before launch.</strong>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
|
|
8
|
+
ai-saas-guard points reviewers to the auth, billing, data access, secrets, MCP, and deploy changes that deserve human attention first. It runs locally, reads your repo only, and does not upload code.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
It is not a pentest. It is a practical review checklist for launch-risk hotspots.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
English | <a href="README.zh-CN.md">中文 README</a>
|
|
9
17
|
</p>
|
|
10
18
|
|
|
11
19
|
<p align="center">
|
|
@@ -18,13 +26,29 @@
|
|
|
18
26
|
|
|
19
27
|
---
|
|
20
28
|
|
|
21
|
-
##
|
|
29
|
+
## The Problem It Solves
|
|
30
|
+
|
|
31
|
+
AI can turn an idea into a working SaaS quickly. The harder question is whether the app is ready for real users.
|
|
32
|
+
|
|
33
|
+
The risky parts are often not the obvious UI bugs. They are the small changes that decide who can see data, who gets paid access, where secrets are exposed, and what an AI tool is allowed to do:
|
|
34
|
+
|
|
35
|
+
- Can one customer read another customer's data?
|
|
36
|
+
- Can a Stripe webhook grant access twice, miss a failed payment, or trust an unsigned request?
|
|
37
|
+
- Did a public environment variable expose a secret?
|
|
38
|
+
- Did an MCP tool get shell, database, or broad filesystem access?
|
|
39
|
+
- Did a pull request hide auth, billing, or deploy changes inside a large AI-generated diff?
|
|
22
40
|
|
|
23
|
-
`ai-saas-guard` is a
|
|
41
|
+
`ai-saas-guard` is a local-first, review-first preflight for that moment. It does not try to prove your app is secure. It is not a pentest, certification, or full audit. It gives founders, solo builders, small teams, and reviewers a short, evidence-backed list of what to check before launch or merge.
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
## What You Get
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
Run it against a repository or pull request and get findings with:
|
|
46
|
+
|
|
47
|
+
- the rule that matched
|
|
48
|
+
- severity and file evidence
|
|
49
|
+
- why the issue matters in a SaaS launch
|
|
50
|
+
- how to verify it manually
|
|
51
|
+
- a practical fix direction
|
|
28
52
|
|
|
29
53
|
It is built for common AI-SaaS stacks:
|
|
30
54
|
|
|
@@ -35,8 +59,6 @@ It is built for common AI-SaaS stacks:
|
|
|
35
59
|
- MCP server configuration
|
|
36
60
|
- AI-generated pull requests with large mixed diffs
|
|
37
61
|
|
|
38
|
-
It is intentionally evidence-first. Findings include a rule ID, severity, file evidence, why it matters, how to verify it, and a fix direction.
|
|
39
|
-
|
|
40
62
|
## Current Status
|
|
41
63
|
|
|
42
64
|
This repository is public on GitHub.
|
|
@@ -51,8 +73,8 @@ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is availab
|
|
|
51
73
|
| JSON and SARIF output | Available |
|
|
52
74
|
| Composite GitHub Action | Available |
|
|
53
75
|
| Project config | `.ai-saas-guard.json` rule toggles, severity overrides, and fail thresholds |
|
|
54
|
-
| Versioned Action tags | `v0.
|
|
55
|
-
| npm package | `ai-saas-guard@0.
|
|
76
|
+
| Versioned Action tags | `v0.11.0`, `v0` |
|
|
77
|
+
| npm package | `ai-saas-guard@0.11.0` |
|
|
56
78
|
| npm publishing | Trusted Publisher/OIDC, no long-lived publish token |
|
|
57
79
|
|
|
58
80
|
## Quick Start
|
|
@@ -182,9 +204,23 @@ Use [docs/stripe-webhook-replay.md](docs/stripe-webhook-replay.md) after `check-
|
|
|
182
204
|
|
|
183
205
|
See [docs/github-app-design.md](docs/github-app-design.md) for the proposed hosted GitHub App layer. The note covers least-privilege permissions, selected repositories, webhook verification, PR comments, check runs, privacy, data retention, prompt injection handling, and why the hosted app should not replace the local CLI.
|
|
184
206
|
|
|
207
|
+
The first hosted service slice is defined in [docs/hosted-first-service-slice.md](docs/hosted-first-service-slice.md). It is intentionally check-run-only: signed GitHub App webhook intake, trusted scan identity, idempotent scan queueing, read-only worker behavior, compact report storage, and no PR comments, dashboard, billing, or AI summaries.
|
|
208
|
+
|
|
209
|
+
The hosted deployment model is documented in [docs/hosted-deployment-model.md](docs/hosted-deployment-model.md). It chooses a containerized Node.js ingress and worker model with a managed durable queue, platform secret manager, structured redacted logs, installation/repository rate limits, and rollback/incident response paths.
|
|
210
|
+
|
|
211
|
+
The hosted operational release gate is documented in [docs/hosted-operational-release-gate.md](docs/hosted-operational-release-gate.md). It defines the hosted-specific CI, replay, queue, worker cleanup, privacy, monitoring, rollback, and incident-response evidence required before any hosted environment is exposed to users.
|
|
212
|
+
|
|
213
|
+
Hosted uninstall and data deletion behavior is documented in [docs/hosted-uninstall-data-deletion.md](docs/hosted-uninstall-data-deletion.md). It defines repository removal, full app uninstall, compact report deletion, queue cancellation, audit record retention, repeated cleanup, and user-facing deletion wording.
|
|
214
|
+
|
|
215
|
+
Hosted pricing and packaging boundaries are documented in [docs/hosted-pricing-packaging.md](docs/hosted-pricing-packaging.md). Core local scanning stays useful without an account; hosted plans may add workflow convenience, saved reports, team policy, and optional human review, but they do not gate local CLI scanning.
|
|
216
|
+
|
|
217
|
+
Hosted pre-implementation pure contracts are documented in [docs/hosted-preimplementation-contracts.md](docs/hosted-preimplementation-contracts.md). They now include a pull request webhook intake planner that verifies signatures before parsing or queueing, builds trusted scan identity, authorizes selected-repository scope, and defaults to check-run-only output. They also cover queue-safe webhook event parsing, bounded check-run summary rendering, idempotent queue cleanup planning, worker checkout cleanup planning, and other service-free helpers exported from `ai-saas-guard/hosted/contracts`.
|
|
218
|
+
|
|
219
|
+
A public hosted compact report schema fixture is available at [examples/hosted-compact-report.json](examples/hosted-compact-report.json). It is synthetic and public-safe: compact evidence only, no raw source, raw diffs, secrets, webhook payload bodies, customer payloads, private URLs, or worker checkout paths.
|
|
220
|
+
|
|
185
221
|
The proposed hosted app permission boundary is intentionally narrow: repository contents read, pull requests read, checks write, and metadata read for the first version. Optional PR comments require repository policy opt-in, and broad permissions such as administration, deployments, Actions write, and repository secrets are out of scope.
|
|
186
222
|
|
|
187
|
-
The repository also includes pure pre-implementation hosted contract helpers and tests for webhook verification, installation token scoping, queue idempotency, compact reports, and retention limits. These helpers do not implement or deploy a hosted service.
|
|
223
|
+
The repository also includes pure pre-implementation hosted contract helpers and tests for webhook intake order, webhook verification, installation token scoping, queue idempotency, compact reports, and retention limits. These helpers do not implement or deploy a hosted service.
|
|
188
224
|
|
|
189
225
|
Users should prefer the local CLI for private repositories, offline review, or no-account workflows where hosted code processing is not acceptable.
|
|
190
226
|
|
|
@@ -218,7 +254,7 @@ Use `suppressions` for narrower false-positive handling when one rule is noisy o
|
|
|
218
254
|
|
|
219
255
|
## GitHub Action
|
|
220
256
|
|
|
221
|
-
The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.
|
|
257
|
+
The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.11.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
|
|
222
258
|
|
|
223
259
|
```yaml
|
|
224
260
|
name: ai-saas-guard
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
<h1 align="center">ai-saas-guard</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>你用 AI 把 SaaS 做出来了。现在要知道上线前哪里最容易出事。</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
ai-saas-guard 会优先指出 auth、billing、data access、secrets、MCP 和 deploy 里最值得人工 review 的改动。它本地运行、只读仓库、不上传代码。
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
它不是渗透测试,而是一份面向上线风险点的实用 review 清单。
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="README.md">English README</a> | 中文
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 它解决什么问题
|
|
22
|
+
|
|
23
|
+
AI 能很快把一个 SaaS 从想法做成可运行的产品。真正难的是:它能不能放心给真实用户用。
|
|
24
|
+
|
|
25
|
+
上线前最危险的通常不是界面小 bug,而是那些会影响用户数据、付费权限、密钥暴露和 AI 工具权限的小改动:
|
|
26
|
+
|
|
27
|
+
- 一个用户会不会看到另一个客户的数据?
|
|
28
|
+
- Stripe webhook 会不会重复开通权限、漏处理付款失败,或者信任未签名请求?
|
|
29
|
+
- `NEXT_PUBLIC_*` 里是不是不小心暴露了 secret?
|
|
30
|
+
- MCP 工具是不是拿到了 shell、数据库或过宽的文件系统权限?
|
|
31
|
+
- AI 生成的大 PR 里,是不是把 auth、billing 或 deploy 改动藏在 UI 调整中?
|
|
32
|
+
|
|
33
|
+
`ai-saas-guard` 是面向这个时刻的本地优先、review-first 上线预检工具。它不会证明你的应用绝对安全,也不是渗透测试、认证或完整安全审计。它的目标是给 founder、独立开发者、小团队和 reviewer 一份短而有证据的清单,告诉你上线或合并 PR 前最该先看哪里。
|
|
34
|
+
|
|
35
|
+
## 你会得到什么
|
|
36
|
+
|
|
37
|
+
对仓库或 PR 运行后,它会给出:
|
|
38
|
+
|
|
39
|
+
- 命中的 rule
|
|
40
|
+
- severity 和文件证据
|
|
41
|
+
- 为什么这个问题会影响 SaaS 上线
|
|
42
|
+
- 如何人工验证
|
|
43
|
+
- 实际修复方向
|
|
44
|
+
|
|
45
|
+
它适合常见 AI 构建的 SaaS 技术栈:
|
|
46
|
+
|
|
47
|
+
- Next.js 和 Vercel
|
|
48
|
+
- Supabase RLS、storage policy、SQL migration
|
|
49
|
+
- Stripe checkout、subscription、webhook
|
|
50
|
+
- Prisma 或 SQL migration
|
|
51
|
+
- MCP server 配置
|
|
52
|
+
- AI 生成的大型混合 PR
|
|
53
|
+
|
|
54
|
+
## 当前状态
|
|
55
|
+
|
|
56
|
+
这个仓库是公开 GitHub 仓库。
|
|
57
|
+
|
|
58
|
+
CLI 已发布到 npm:`ai-saas-guard@0.11.0`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.11.0`。
|
|
59
|
+
|
|
60
|
+
| 模块 | 状态 |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| 公开 GitHub 仓库 | 已可用 |
|
|
63
|
+
| npm CLI | 已发布为 `ai-saas-guard` |
|
|
64
|
+
| 本地源码运行 | 已可用 |
|
|
65
|
+
| JSON 和 SARIF 输出 | 已可用 |
|
|
66
|
+
| Markdown PR summary | 已可用 |
|
|
67
|
+
| GitHub Action | 已可用 |
|
|
68
|
+
| 项目配置 | `.ai-saas-guard.json` 支持规则开关、severity 覆盖和 fail threshold |
|
|
69
|
+
| 当前版本 | `0.11.0` |
|
|
70
|
+
| Action 标签 | `v0.11.0`、`v0` |
|
|
71
|
+
| npm 发布 | GitHub Actions Trusted Publisher/OIDC,无需长期 npm token |
|
|
72
|
+
|
|
73
|
+
## 快速开始
|
|
74
|
+
|
|
75
|
+
无需全局安装,直接运行:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx ai-saas-guard@latest scan --root /path/to/your-saas
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
运行专项检查:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx ai-saas-guard@latest pr-risk --root /path/to/your-saas --base origin/main
|
|
85
|
+
npx ai-saas-guard@latest check-supabase --root /path/to/your-saas
|
|
86
|
+
npx ai-saas-guard@latest check-stripe --root /path/to/your-saas
|
|
87
|
+
npx ai-saas-guard@latest check-mcp --root /path/to/your-saas
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
机器可读输出:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx ai-saas-guard@latest scan --root /path/to/your-saas --json
|
|
94
|
+
npx ai-saas-guard@latest scan --root /path/to/your-saas --sarif > ai-saas-guard.sarif
|
|
95
|
+
npx ai-saas-guard@latest pr-risk --root /path/to/your-saas --base origin/main --markdown > ai-saas-guard-pr.md
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
本地开发:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git clone https://github.com/zr9959/ai-saas-guard.git
|
|
102
|
+
cd ai-saas-guard
|
|
103
|
+
npm ci
|
|
104
|
+
npm run build
|
|
105
|
+
node dist/cli.js scan --root /path/to/your-saas
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 主要命令
|
|
109
|
+
|
|
110
|
+
| 命令 | 用途 |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `scan` | 对 secrets、Stripe、Supabase、MCP、API routes、deploy config 做整体上线预检 |
|
|
113
|
+
| `pr-risk` | 分析当前 git diff 或指定 base branch diff,判断哪些文件和风险面应该先 review |
|
|
114
|
+
| `check-supabase` | 检查 migration 和 policy 文件里的 RLS、ownership、storage policy 风险 |
|
|
115
|
+
| `check-stripe` | 检查 webhook 签名、raw body、幂等、订阅生命周期和 entitlement 更新路径 |
|
|
116
|
+
| `check-mcp` | 检查 MCP 配置里的 secret、非 localhost 绑定、shell/db/filesystem 等副作用 |
|
|
117
|
+
|
|
118
|
+
## 它会检查什么
|
|
119
|
+
|
|
120
|
+
| 风险面 | 例子 |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| Secrets 和 env | 类似密钥的字符串、危险的 `NEXT_PUBLIC_*` 暴露 |
|
|
123
|
+
| Stripe | webhook 缺失、未验证签名、raw body 签名风险、缺幂等、缺失败/取消/退款/更新处理 |
|
|
124
|
+
| Supabase | 敏感表没启用 RLS、policy 过宽、缺少 ownership filter、`WITH CHECK` 过弱、storage object policy 过宽 |
|
|
125
|
+
| API routes | 有 auth 但缺少明显 ownership guard,敏感 mutation route 缺少 rate-limit 提示 |
|
|
126
|
+
| MCP | 明文 secret、非 localhost 绑定、过宽文件系统权限、shell 工具、raw SQL 工具 |
|
|
127
|
+
| Deploy config | Next static export 和 API route 冲突、Edge runtime 使用 Node-only API、关键 env 文档缺失 |
|
|
128
|
+
| PR risk | auth、billing、RLS、env、deploy、API、storage、测试删除、大型混合 diff |
|
|
129
|
+
|
|
130
|
+
完整规则请看 [docs/rules.md](docs/rules.md)。
|
|
131
|
+
|
|
132
|
+
## PR 风险分流
|
|
133
|
+
|
|
134
|
+
`scan` 可以扫整个仓库,但这个项目更锋利的入口是 PR review。
|
|
135
|
+
|
|
136
|
+
AI 生成的 PR 经常把很多东西混在一起:
|
|
137
|
+
|
|
138
|
+
- UI 调整
|
|
139
|
+
- auth/session 改动
|
|
140
|
+
- database migration
|
|
141
|
+
- Stripe checkout 或 webhook 改动
|
|
142
|
+
- Supabase policy
|
|
143
|
+
- Vercel 配置
|
|
144
|
+
- 测试被删除或削弱
|
|
145
|
+
|
|
146
|
+
`pr-risk` 会输出:
|
|
147
|
+
|
|
148
|
+
- 最应该先 review 的文件
|
|
149
|
+
- PR 触碰到的敏感类别
|
|
150
|
+
- review-first checklist
|
|
151
|
+
- 建议拆分 PR 的方向
|
|
152
|
+
- 必要测试或人工验证步骤
|
|
153
|
+
- 当 base ref 或 shallow checkout 导致无法比较时,给出明确诊断
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
node dist/cli.js pr-risk --root /path/to/your-saas --base origin/main --json
|
|
157
|
+
node dist/cli.js pr-risk --root /path/to/your-saas --base origin/main --markdown
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## 目标用户
|
|
161
|
+
|
|
162
|
+
这个工具主要面向:
|
|
163
|
+
|
|
164
|
+
- 用 AI 编程工具快速做 SaaS MVP 的 founder 或独立开发者
|
|
165
|
+
- 没有专职安全工程师的小团队
|
|
166
|
+
- 需要 review 大型 AI-generated PR 的 reviewer
|
|
167
|
+
- 给客户交付 SaaS 的开发者或 agency
|
|
168
|
+
- 希望在 CI 里加一道轻量上线预检的小团队
|
|
169
|
+
|
|
170
|
+
它的目标不是吓人,也不是制造大量噪音,而是帮你把 review 时间用在最值得看的地方。
|
|
171
|
+
|
|
172
|
+
## GitHub Action
|
|
173
|
+
|
|
174
|
+
可以在 GitHub Actions 里直接使用:
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
name: ai-saas-guard
|
|
178
|
+
|
|
179
|
+
on:
|
|
180
|
+
pull_request:
|
|
181
|
+
|
|
182
|
+
permissions:
|
|
183
|
+
contents: read
|
|
184
|
+
|
|
185
|
+
jobs:
|
|
186
|
+
preflight:
|
|
187
|
+
runs-on: ubuntu-latest
|
|
188
|
+
steps:
|
|
189
|
+
- uses: actions/checkout@v6.0.2
|
|
190
|
+
with:
|
|
191
|
+
fetch-depth: 0
|
|
192
|
+
- uses: zr9959/ai-saas-guard@v0
|
|
193
|
+
with:
|
|
194
|
+
command: pr-risk
|
|
195
|
+
root: ${{ github.workspace }}
|
|
196
|
+
base: origin/main
|
|
197
|
+
fail-on: high
|
|
198
|
+
config: .ai-saas-guard.json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
更多 GitHub Action 示例请看 [docs/github-action.md](docs/github-action.md)。
|
|
202
|
+
|
|
203
|
+
## 项目配置
|
|
204
|
+
|
|
205
|
+
在仓库根目录添加 `.ai-saas-guard.json` 可以调整规则:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"failOn": "high",
|
|
210
|
+
"rules": {
|
|
211
|
+
"stripe.webhook.missing-signature": "off",
|
|
212
|
+
"stripe.webhook.missing-idempotency": "critical",
|
|
213
|
+
"deploy.env.example-missing": "info"
|
|
214
|
+
},
|
|
215
|
+
"suppressions": [
|
|
216
|
+
{
|
|
217
|
+
"ruleId": "stripe.webhook.missing-idempotency",
|
|
218
|
+
"paths": ["app/api/stripe/webhook/route.ts"],
|
|
219
|
+
"reason": "Temporary launch exception with duplicate-event coverage in integration tests."
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`rules` 可以关闭规则或覆盖 severity。`suppressions` 适合处理某个具体路径上的 false positive。`failOn` 用于设置 CI 失败阈值。
|
|
226
|
+
|
|
227
|
+
## 隐私模型
|
|
228
|
+
|
|
229
|
+
`ai-saas-guard` 设计上适合在私有本地仓库中运行。
|
|
230
|
+
|
|
231
|
+
- 本地运行
|
|
232
|
+
- 读取仓库文件和 git diff
|
|
233
|
+
- scan 命令无网络调用
|
|
234
|
+
- 不上传代码
|
|
235
|
+
- 不需要账号或登录
|
|
236
|
+
- 不修改被扫描仓库
|
|
237
|
+
- 对类似 secret 的 evidence 做 redaction
|
|
238
|
+
|
|
239
|
+
## Hosted GitHub App 设计
|
|
240
|
+
|
|
241
|
+
当前仓库已经包含未来 Hosted GitHub App 的设计文档和纯契约测试,但还没有部署真实 hosted 服务。
|
|
242
|
+
|
|
243
|
+
相关文档:
|
|
244
|
+
|
|
245
|
+
- [docs/github-app-design.md](docs/github-app-design.md)
|
|
246
|
+
- [docs/hosted-first-service-slice.md](docs/hosted-first-service-slice.md)
|
|
247
|
+
- [docs/hosted-deployment-model.md](docs/hosted-deployment-model.md)
|
|
248
|
+
- [docs/hosted-operational-release-gate.md](docs/hosted-operational-release-gate.md)
|
|
249
|
+
- [docs/hosted-uninstall-data-deletion.md](docs/hosted-uninstall-data-deletion.md)
|
|
250
|
+
- [docs/hosted-pricing-packaging.md](docs/hosted-pricing-packaging.md)
|
|
251
|
+
- [docs/hosted-preimplementation-contracts.md](docs/hosted-preimplementation-contracts.md)
|
|
252
|
+
|
|
253
|
+
已经实现的 hosted 预实现纯契约包括:
|
|
254
|
+
|
|
255
|
+
- pull request webhook intake planner:先验签,再解析 payload、生成可信 identity、校验 selected-repository scope,并默认只走 check-run-only 输出
|
|
256
|
+
- webhook event parser
|
|
257
|
+
- check-run summary renderer
|
|
258
|
+
- queue cleanup planner
|
|
259
|
+
- worker checkout cleanup planner
|
|
260
|
+
- hosted compact report fixture:[examples/hosted-compact-report.json](examples/hosted-compact-report.json)
|
|
261
|
+
|
|
262
|
+
这些 helper 不会启动服务、不会调用 GitHub API、不会请求 installation token、不会写 check run,也不会上传源码。
|
|
263
|
+
|
|
264
|
+
## 它不是什么
|
|
265
|
+
|
|
266
|
+
这个项目刻意避免过度安全承诺。
|
|
267
|
+
|
|
268
|
+
- 不是渗透测试
|
|
269
|
+
- 不是完整 SAST 平台
|
|
270
|
+
- 不能证明你的应用绝对安全
|
|
271
|
+
- 不能替代两账号权限测试
|
|
272
|
+
- 不执行 Stripe、Supabase、Vercel 或浏览器流程
|
|
273
|
+
- 不检查没有体现在本地文件里的生产设置
|
|
274
|
+
- 不替代 Semgrep、Gitleaks、TruffleHog、Bearer、CodeQL 或人工 review
|
|
275
|
+
|
|
276
|
+
正确使用方式是:把它当成上线前和 PR review 前的 preflight,帮助你决定应该先把人工注意力放在哪里。
|
|
277
|
+
|
|
278
|
+
## 开发
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm ci
|
|
282
|
+
npm test
|
|
283
|
+
npm run build
|
|
284
|
+
node dist/cli.js scan --root .
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
发布 CLI、GitHub Action、npm package 或任何公开仓库更新前,必须按照 [docs/release-quality-knowledge-base.md](docs/release-quality-knowledge-base.md) 的 release gate 执行。
|
|
288
|
+
|
|
289
|
+
以后更新英文 `README.md` 时,也要同步检查并更新本中文 `README.zh-CN.md`。
|
|
290
|
+
|
|
291
|
+
## 安全报告
|
|
292
|
+
|
|
293
|
+
报告漏洞前请阅读 [SECURITY.md](SECURITY.md)。不要在公开 issue 中发布真实 API key、客户数据、私有源码或生产 URL。
|
|
@@ -32,6 +32,57 @@ export interface HostedScanIdentity {
|
|
|
32
32
|
headSha: string;
|
|
33
33
|
scannerVersion: string;
|
|
34
34
|
}
|
|
35
|
+
export type PullRequestEventRejectReason = "unsupported_action" | "draft_pull_request" | "missing_required_field";
|
|
36
|
+
export interface HostedPullRequestEventInput {
|
|
37
|
+
payload: unknown;
|
|
38
|
+
scannerVersion: string;
|
|
39
|
+
allowDraft?: boolean;
|
|
40
|
+
supportedActions?: string[];
|
|
41
|
+
}
|
|
42
|
+
export interface HostedPullRequestEventDecision {
|
|
43
|
+
accepted: boolean;
|
|
44
|
+
shouldQueueScanJob: boolean;
|
|
45
|
+
reason?: PullRequestEventRejectReason;
|
|
46
|
+
action?: string;
|
|
47
|
+
identity?: HostedScanIdentity;
|
|
48
|
+
}
|
|
49
|
+
export type HostedPullRequestWebhookIntakeStage = "signature" | "payload" | "event" | "installation_scope" | "queue";
|
|
50
|
+
export type HostedPullRequestWebhookIntakeRejectReason = WebhookRejectReason | PullRequestEventRejectReason | InstallationScopeRejectReason | "invalid_json" | "missing_delivery_id";
|
|
51
|
+
export interface HostedPullRequestWebhookIntakeInput {
|
|
52
|
+
payload: string | Buffer;
|
|
53
|
+
signatureHeader?: string;
|
|
54
|
+
signingKey: string | Buffer;
|
|
55
|
+
deliveryId?: string;
|
|
56
|
+
seenDeliveryIds?: Set<string>;
|
|
57
|
+
scannerVersion: string;
|
|
58
|
+
selectedRepositoryIds: number[];
|
|
59
|
+
removedRepositoryIds?: number[];
|
|
60
|
+
queue: Map<string, HostedScanJobState>;
|
|
61
|
+
allowDraft?: boolean;
|
|
62
|
+
supportedActions?: string[];
|
|
63
|
+
manualRerun?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export interface HostedPullRequestWebhookIntakeDecision {
|
|
66
|
+
accepted: boolean;
|
|
67
|
+
stage: HostedPullRequestWebhookIntakeStage;
|
|
68
|
+
reason?: HostedPullRequestWebhookIntakeRejectReason;
|
|
69
|
+
action?: string;
|
|
70
|
+
deliveryId?: string;
|
|
71
|
+
identity?: HostedScanIdentity;
|
|
72
|
+
job?: HostedScanJobDecision;
|
|
73
|
+
shouldQueueScanJob: boolean;
|
|
74
|
+
shouldFetchRepository: boolean;
|
|
75
|
+
shouldCreateCheckRun: boolean;
|
|
76
|
+
shouldCreatePrComment: false;
|
|
77
|
+
privacy: {
|
|
78
|
+
includesRawWebhookPayload: false;
|
|
79
|
+
includesUntrustedPrText: false;
|
|
80
|
+
includesRawSource: false;
|
|
81
|
+
includesRawDiffs: false;
|
|
82
|
+
includesSecrets: false;
|
|
83
|
+
includesCustomerPayloads: false;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
35
86
|
export type InstallationScopeRejectReason = "installation_mismatch" | "repository_not_installed" | "repository_removed_from_installation";
|
|
36
87
|
export interface InstallationScopeInput {
|
|
37
88
|
identity: HostedScanIdentity;
|
|
@@ -62,6 +113,85 @@ export interface HostedScanJobDecision {
|
|
|
62
113
|
shouldCreateCheckRun: boolean;
|
|
63
114
|
shouldCreatePrComment: boolean;
|
|
64
115
|
}
|
|
116
|
+
export type HostedQueueJobStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
117
|
+
export type HostedQueueCleanupTrigger = "repository_removed" | "installation_deleted" | "repeated_cleanup";
|
|
118
|
+
export interface HostedQueueCleanupJobState {
|
|
119
|
+
key: string;
|
|
120
|
+
identity: HostedScanIdentity;
|
|
121
|
+
status: HostedQueueJobStatus;
|
|
122
|
+
attempt?: number;
|
|
123
|
+
deliveryIds?: string[];
|
|
124
|
+
}
|
|
125
|
+
export interface HostedQueueCleanupPlanInput {
|
|
126
|
+
trigger: HostedQueueCleanupTrigger;
|
|
127
|
+
installationId: number;
|
|
128
|
+
repositoryId?: number;
|
|
129
|
+
requestedAt: string;
|
|
130
|
+
jobs: HostedQueueCleanupJobState[];
|
|
131
|
+
}
|
|
132
|
+
export interface HostedQueueCleanupPlan {
|
|
133
|
+
trigger: HostedQueueCleanupTrigger;
|
|
134
|
+
scope: "repository" | "installation";
|
|
135
|
+
installationId: number;
|
|
136
|
+
repositoryId?: number;
|
|
137
|
+
requestedAt: string;
|
|
138
|
+
idempotencyKey: string;
|
|
139
|
+
idempotent: true;
|
|
140
|
+
matchedJobKeys: string[];
|
|
141
|
+
cancelQueuedJobKeys: string[];
|
|
142
|
+
requestRunningCancellationJobKeys: string[];
|
|
143
|
+
preserveTerminalJobKeys: string[];
|
|
144
|
+
keepUnmatchedJobKeys: string[];
|
|
145
|
+
cancelQueuedJobs: true;
|
|
146
|
+
requestRunningCancellation: true;
|
|
147
|
+
deleteRawSource: false;
|
|
148
|
+
deleteRawDiffs: false;
|
|
149
|
+
deleteSecrets: false;
|
|
150
|
+
deleteCustomerPayloads: false;
|
|
151
|
+
}
|
|
152
|
+
export type HostedWorkerCheckoutTerminalState = "success" | "failure" | "timeout" | "cancellation" | "cleanup_failure";
|
|
153
|
+
export type HostedWorkerCheckoutCleanupAction = "delete_checkout" | "record_cleanup_failure";
|
|
154
|
+
export interface HostedWorkerCheckoutCleanupInput {
|
|
155
|
+
identity: HostedScanIdentity;
|
|
156
|
+
jobKey: string;
|
|
157
|
+
terminalState: HostedWorkerCheckoutTerminalState;
|
|
158
|
+
finishedAt: string;
|
|
159
|
+
checkoutPath?: string;
|
|
160
|
+
cleanupError?: string;
|
|
161
|
+
rawSource?: string;
|
|
162
|
+
rawDiff?: string;
|
|
163
|
+
secretValues?: string[];
|
|
164
|
+
customerPayload?: unknown;
|
|
165
|
+
}
|
|
166
|
+
export interface HostedWorkerCheckoutCleanupPlan {
|
|
167
|
+
cleanupAction: HostedWorkerCheckoutCleanupAction;
|
|
168
|
+
shouldDeleteWorkerCheckout: boolean;
|
|
169
|
+
shouldRemoveCredentials: boolean;
|
|
170
|
+
shouldRemoveRawSource: boolean;
|
|
171
|
+
shouldRemoveRawDiffs: boolean;
|
|
172
|
+
shouldRemoveGeneratedArtifacts: boolean;
|
|
173
|
+
requiresOperatorReview: boolean;
|
|
174
|
+
preserveAuditRecord: true;
|
|
175
|
+
visibleUserMessage: string;
|
|
176
|
+
safeMetadata: {
|
|
177
|
+
jobKey: string;
|
|
178
|
+
installationId: number;
|
|
179
|
+
repositoryId: number;
|
|
180
|
+
repositoryFullName: string;
|
|
181
|
+
pullRequestNumber: number;
|
|
182
|
+
scannerVersion: string;
|
|
183
|
+
terminalState: HostedWorkerCheckoutTerminalState;
|
|
184
|
+
finishedAt: string;
|
|
185
|
+
};
|
|
186
|
+
privacy: {
|
|
187
|
+
returnsCheckoutPath: false;
|
|
188
|
+
returnsCleanupError: false;
|
|
189
|
+
returnsRawSource: false;
|
|
190
|
+
returnsRawDiffs: false;
|
|
191
|
+
returnsSecrets: false;
|
|
192
|
+
returnsCustomerPayloads: false;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
65
195
|
export interface CompactHostedFinding {
|
|
66
196
|
ruleId: string;
|
|
67
197
|
severity: string;
|
|
@@ -98,17 +228,96 @@ export interface CompactHostedReport {
|
|
|
98
228
|
modelTraining: "disabled";
|
|
99
229
|
workerCheckoutDeletion: "after_scan_completion";
|
|
100
230
|
}
|
|
231
|
+
export type HostedCheckRunConclusion = "success" | "neutral" | "failure";
|
|
232
|
+
export type HostedCheckRunSeverityThreshold = "critical" | "high" | "medium" | "low" | "info";
|
|
233
|
+
export type HostedCheckRunAnnotationLevel = "notice" | "warning" | "failure";
|
|
234
|
+
export interface HostedCheckRunSummaryInput {
|
|
235
|
+
report: CompactHostedReport;
|
|
236
|
+
failOnSeverity?: HostedCheckRunSeverityThreshold;
|
|
237
|
+
maxMarkdownChars?: number;
|
|
238
|
+
}
|
|
239
|
+
export interface HostedCheckRunAnnotation {
|
|
240
|
+
path: string;
|
|
241
|
+
startLine: number;
|
|
242
|
+
endLine: number;
|
|
243
|
+
annotationLevel: HostedCheckRunAnnotationLevel;
|
|
244
|
+
title: string;
|
|
245
|
+
message: string;
|
|
246
|
+
}
|
|
247
|
+
export interface HostedCheckRunSummary {
|
|
248
|
+
name: "AI SaaS Guard";
|
|
249
|
+
conclusion: HostedCheckRunConclusion;
|
|
250
|
+
output: {
|
|
251
|
+
title: string;
|
|
252
|
+
summary: string;
|
|
253
|
+
text: string;
|
|
254
|
+
};
|
|
255
|
+
annotations: HostedCheckRunAnnotation[];
|
|
256
|
+
localCliCommand: string;
|
|
257
|
+
privacy: {
|
|
258
|
+
includesRawSource: false;
|
|
259
|
+
includesRawDiffs: false;
|
|
260
|
+
includesSecrets: false;
|
|
261
|
+
includesCustomerPayloads: false;
|
|
262
|
+
modelTraining: "disabled";
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
export type HostedDeletionTrigger = "repository_removed" | "installation_deleted" | "repeated_cleanup";
|
|
266
|
+
export interface HostedDeletionPlanInput {
|
|
267
|
+
trigger: HostedDeletionTrigger;
|
|
268
|
+
installationId: number;
|
|
269
|
+
repositoryId?: number;
|
|
270
|
+
requestedAt: string;
|
|
271
|
+
auditRecordRetentionDays?: number;
|
|
272
|
+
}
|
|
273
|
+
export interface HostedDeletionPlan {
|
|
274
|
+
trigger: HostedDeletionTrigger;
|
|
275
|
+
scope: "repository" | "installation";
|
|
276
|
+
installationId: number;
|
|
277
|
+
repositoryId?: number;
|
|
278
|
+
requestedAt: string;
|
|
279
|
+
idempotencyKey: string;
|
|
280
|
+
idempotent: true;
|
|
281
|
+
deleteCompactReports: true;
|
|
282
|
+
cancelQueuedJobs: true;
|
|
283
|
+
deleteWorkerCheckouts: true;
|
|
284
|
+
deleteRawSource: false;
|
|
285
|
+
deleteRawDiffs: false;
|
|
286
|
+
deleteSecrets: false;
|
|
287
|
+
deleteCustomerPayloads: false;
|
|
288
|
+
deleteGitHubOwnedCheckRuns: false;
|
|
289
|
+
preserveAuditRecord: true;
|
|
290
|
+
auditRecordRetentionDays: number;
|
|
291
|
+
visibleUserMessage: string;
|
|
292
|
+
}
|
|
101
293
|
export declare const HOSTED_PRIVACY_DEFAULTS: {
|
|
102
294
|
readonly retentionDays: 30;
|
|
295
|
+
readonly auditRecordRetentionDays: 90;
|
|
103
296
|
readonly modelTraining: "disabled";
|
|
104
297
|
readonly deleteWorkerCheckout: "after_scan_completion";
|
|
105
298
|
};
|
|
106
299
|
export declare function verifyGitHubWebhook(input: GitHubWebhookInput): GitHubWebhookDecision;
|
|
300
|
+
export declare function planHostedPullRequestWebhookIntake(input: HostedPullRequestWebhookIntakeInput): HostedPullRequestWebhookIntakeDecision;
|
|
107
301
|
export declare function buildHostedScanIdentity(input: HostedScanIdentityInput): HostedScanIdentity;
|
|
302
|
+
export declare function parseHostedPullRequestEvent(input: HostedPullRequestEventInput): HostedPullRequestEventDecision;
|
|
108
303
|
export declare function authorizeInstallationTokenScope(input: InstallationScopeInput): InstallationScopeDecision;
|
|
109
304
|
export declare function getHostedScanIdempotencyKey(identity: HostedScanIdentity): string;
|
|
110
305
|
export declare function upsertHostedScanJob(queue: Map<string, HostedScanJobState>, input: HostedScanJobInput): HostedScanJobDecision;
|
|
306
|
+
export declare function getHostedQueueCleanupIdempotencyKey(input: {
|
|
307
|
+
trigger: HostedQueueCleanupTrigger;
|
|
308
|
+
installationId: number;
|
|
309
|
+
repositoryId?: number;
|
|
310
|
+
}): string;
|
|
311
|
+
export declare function createHostedQueueCleanupPlan(input: HostedQueueCleanupPlanInput): HostedQueueCleanupPlan;
|
|
312
|
+
export declare function createHostedWorkerCheckoutCleanupPlan(input: HostedWorkerCheckoutCleanupInput): HostedWorkerCheckoutCleanupPlan;
|
|
111
313
|
export declare function resolveHostedRetentionDays(input?: {
|
|
112
314
|
teamRequestedDays?: number;
|
|
113
315
|
}): number;
|
|
114
316
|
export declare function createCompactHostedReport(input: CompactHostedReportInput): CompactHostedReport;
|
|
317
|
+
export declare function createHostedCheckRunSummary(input: HostedCheckRunSummaryInput): HostedCheckRunSummary;
|
|
318
|
+
export declare function getHostedDeletionIdempotencyKey(input: {
|
|
319
|
+
trigger: HostedDeletionTrigger;
|
|
320
|
+
installationId: number;
|
|
321
|
+
repositoryId?: number;
|
|
322
|
+
}): string;
|
|
323
|
+
export declare function createHostedDeletionPlan(input: HostedDeletionPlanInput): HostedDeletionPlan;
|