ai-saas-guard 0.37.0 → 0.38.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 CHANGED
@@ -170,7 +170,7 @@ For a concise comparison with Semgrep, zizmor, OpenSSF Scorecard, Snyk, and GitH
170
170
  | --- | --- | --- |
171
171
  | Local CLI | Private code, first local launch review, founder or reviewer workflow | Published on npm; local-first, read-only, no code upload, no LLM calls |
172
172
  | GitHub Action | CI review queue, SARIF upload, PR summary artifacts, controlled fail thresholds | Available through `zr9959/ai-saas-guard@v0` and fixed version tags |
173
- | Hosted GitHub App | Future hosted Check Run experience for selected repositories | Limited trial gate only; not the complete hosted SaaS, not a public hosted scanner |
173
+ | Hosted GitHub App | Future hosted Check Run experience for selected repositories | Limited trial gate with staging ingress, public install-info, compact Check Runs, and cleanup handling; not the complete hosted SaaS, not a public hosted scanner |
174
174
 
175
175
  ## Quick Start
176
176
 
@@ -233,13 +233,13 @@ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is availab
233
233
  | Area | Status |
234
234
  | --- | --- |
235
235
  | Public GitHub repository | Available |
236
- | npm CLI | `ai-saas-guard@0.37.0` |
237
- | GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.37.0` |
236
+ | npm CLI | `ai-saas-guard@0.38.0` |
237
+ | GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.38.0` |
238
238
  | Outputs | Launch decision queue, short summary, terminal, JSON, SARIF, and PR-focused markdown |
239
239
  | Project config | `.ai-saas-guard.json` rule toggles, severity overrides, suppressions, and fail thresholds |
240
240
  | Privacy model | Local-first, read-only scan commands, no LLM calls, no code upload |
241
- | Versioned Action tags | `v0.37.0`, `v0` |
242
- | Current release | `0.37.0` makes the GitHub Action path easier to copy into PR workflows, improves Check Run wording around the launch-risk middle layer, and keeps README first-screen guidance focused on AI-heavy PR review |
241
+ | Versioned Action tags | `v0.38.0`, `v0` |
242
+ | Current release | `0.38.0` closes more of the hosted GitHub App staging loop with public install guidance, selected-repository Check Run wording, signed installation cleanup, and updated hosted docs/tests |
243
243
  | npm publishing | Trusted Publisher/OIDC, no long-lived publish token |
244
244
  | Repository trust hardening | Strict branch protection, Dependabot, CodeQL, fast-check fuzzing, signed release provenance assets, private vulnerability reporting, secret scanning, and push protection |
245
245
  | Cloudflare hosted ingress | Deployed at `https://ai-saas-guard-hosted.zr9959.workers.dev`; signed GitHub App webhook delivery and compact Check Run smoke now pass in staging |
@@ -363,7 +363,7 @@ The hosted staging harness is documented in [docs/hosted-staging-harness.md](doc
363
363
 
364
364
  Deployed worker staging evidence is documented in [docs/hosted-deployed-worker-staging.md](docs/hosted-deployed-worker-staging.md). It exports `createHostedDeployedWorkerStagingEvidenceAutomation`, `createHostedDeployedWorkerStagingEvidenceBundle`, and `evaluateHostedDeployedWorkerStagingReleaseGate` from `ai-saas-guard/hosted/deployed-staging`. It validates safe log samples, then turns public HTTPS health, signed webhook replay, deployed worker cleanup, and external CI/scan/rollback evidence into the hosted release gate for a Node/container read-only checkout worker candidate. It does not deploy cloud resources or claim production hosted exposure.
365
365
 
366
- The first live hosted ingress is deployed on Cloudflare Workers at `https://ai-saas-guard-hosted.zr9959.workers.dev` and documented in [hosted/cloudflare-worker/README.md](hosted/cloudflare-worker/README.md). It exposes `/healthz`, `/github/app/manifest-callback`, and signed `/github/webhook` intake backed by Cloudflare KV. A private staging GitHub App, `ai-saas-guard-hosted`, is installed on `zr9959/ai-saas-guard` with selected-repository access and the first-slice permission contract. The Worker verifies signatures, stores compact pull request identity records, exchanges a scoped installation token, fetches PR file metadata from GitHub, classifies PR-risk hotspots, and publishes a bounded Check Run summary. Current deployed evidence is tracked in [docs/hosted-operations-evidence.md](docs/hosted-operations-evidence.md): health, signed webhook delivery, compact KV records, cleanup, and Check Run publication pass for the staging smoke. The Cloudflare Worker still does not run a full source checkout scan worker or store raw webhook payloads, PR title/body text, raw diffs, source, secrets, checkout paths, or installation tokens.
366
+ The first live hosted ingress is deployed on Cloudflare Workers at `https://ai-saas-guard-hosted.zr9959.workers.dev` and documented in [hosted/cloudflare-worker/README.md](hosted/cloudflare-worker/README.md). It exposes `/healthz`, `/github/app/install-info`, `/github/app/manifest-callback`, and signed `/github/webhook` intake backed by Cloudflare KV. A private staging GitHub App, `ai-saas-guard-hosted`, is installed on `zr9959/ai-saas-guard` with selected-repository access and the first-slice permission contract. The Worker verifies signatures, stores compact pull request identity records, exchanges a scoped installation token, fetches PR file metadata from GitHub, classifies PR-risk hotspots, and publishes a bounded selected-repository hosted check with a review queue and manual proof prompt. Signed installation deletion and repository removal events delete matching compact records. Current deployed evidence is tracked in [docs/hosted-operations-evidence.md](docs/hosted-operations-evidence.md): health, signed webhook delivery, compact KV records, cleanup, and Check Run publication pass in staging. The Cloudflare Worker still does not run a full source checkout scan worker or store raw webhook payloads, PR title/body text, raw diffs, source, secrets, checkout paths, or installation tokens.
367
367
 
368
368
  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. The pure gate evaluator exported from `ai-saas-guard/hosted/contracts` blocks hosted exposure unless every P0 evidence item is fresh, a container digest is recorded, and release notes avoid pentest, certification, and full-audit claims.
369
369
 
@@ -417,7 +417,7 @@ Use `suppressions` for narrower false-positive handling when one rule is noisy o
417
417
 
418
418
  ## GitHub Action
419
419
 
420
- The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.37.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
420
+ The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.38.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
421
421
 
422
422
  ```yaml
423
423
  name: ai-saas-guard
@@ -165,7 +165,7 @@ Next steps
165
165
  | --- | --- | --- |
166
166
  | 本地 CLI | 私有代码、本机首次上线 review、founder 或 reviewer 自查 | 已发布到 npm;本地优先、只读、不上传代码、不调用 LLM |
167
167
  | GitHub Action | CI 里的 review queue、SARIF、PR summary artifact、可控 fail threshold | 可通过 `zr9959/ai-saas-guard@v0` 或固定版本标签使用 |
168
- | Hosted GitHub App | 未来面向 selected repositories 的 hosted Check Run 体验 | 目前只是 limited trial gate,不是完整 hosted SaaS,也不是公开 hosted scanner |
168
+ | Hosted GitHub App | 未来面向 selected repositories 的 hosted Check Run 体验 | 当前是 staging ingress,已有公开 install-info、compact Check Run 和 cleanup handling;不是完整 hosted SaaS,也不是公开 hosted scanner |
169
169
 
170
170
  ## 快速开始
171
171
 
@@ -211,21 +211,21 @@ node dist/cli.js scan --root /path/to/your-saas
211
211
 
212
212
  这个仓库是公开 GitHub 仓库。
213
213
 
214
- CLI 已发布到 npm:`ai-saas-guard@0.37.0`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.37.0`。
214
+ CLI 已发布到 npm:`ai-saas-guard@0.38.0`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.38.0`。
215
215
 
216
216
  | 模块 | 状态 |
217
217
  | --- | --- |
218
218
  | 公开 GitHub 仓库 | 已可用 |
219
- | npm CLI | `ai-saas-guard@0.37.0` |
220
- | GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.37.0` |
219
+ | npm CLI | `ai-saas-guard@0.38.0` |
220
+ | GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.38.0` |
221
221
  | 输出格式 | 上线决策队列、短 summary、Terminal、JSON、SARIF 和 PR markdown |
222
222
  | 项目配置 | `.ai-saas-guard.json` 支持规则开关、severity 覆盖、suppressions 和 fail threshold |
223
223
  | 隐私模型 | 本地优先、只读扫描、不调用 LLM、不上传代码 |
224
- | 当前版本 | `0.37.0` GitHub Action PR workflow 更容易复制使用,优化 hosted Check Run launch-risk middle layer 文案,并让 README 首屏更直接指向 AI 大 PR review 场景 |
225
- | Action 标签 | `v0.37.0`、`v0` |
224
+ | 当前版本 | `0.38.0` 补齐更多 hosted GitHub App staging 闭环:公开 install-info、selected-repository Check Run 文案、签名 installation cleanup,以及对应 hosted 文档和测试 |
225
+ | Action 标签 | `v0.38.0`、`v0` |
226
226
  | npm 发布 | GitHub Actions Trusted Publisher/OIDC,无需长期 npm token |
227
227
  | 仓库可信度加固 | 严格 branch protection、Dependabot、CodeQL、fast-check fuzzing、signed release provenance assets、private vulnerability reporting、secret scanning 和 push protection |
228
- | Cloudflare hosted ingress | 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`;签名 GitHub App webhook deliverycompact Check Run staging smoke 已通过 |
228
+ | Cloudflare hosted ingress | 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`;提供 `/github/app/install-info`,签名 GitHub App webhook deliverycompact Check Run 和 installation cleanup staging smoke 已通过 |
229
229
  | Hosted GitHub App staging | 私有 App `ai-saas-guard-hosted`(`3834787`)已安装到 `zr9959/ai-saas-guard`;hosted operations evidence 见 [docs/hosted-operations-evidence.md](hosted-operations-evidence.md) |
230
230
  | OpenSSF Best Practices | 已获得 passing badge,项目 `12955`;`.bestpractices.json` 继续作为保守证据记录 |
231
231
  | 上一版路线 | v0.36.0 计划见 [v0.36-roadmap.md](v0.36-roadmap.md) |
@@ -379,7 +379,7 @@ GitHub Marketplace wrapper 决策见 [docs/github-marketplace-wrapper-decision.m
379
379
 
380
380
  ## Hosted GitHub App 设计
381
381
 
382
- 当前仓库已经包含未来 Hosted GitHub App 的设计文档、纯契约测试、第一个真实 Cloudflare hosted ingress,以及 Node/container read-only checkout scan runner。私有 staging GitHub App `ai-saas-guard-hosted` 已安装到 `zr9959/ai-saas-guard`,Cloudflare 已配置所需的云端凭据绑定。Worker 代码已经能接收签名 webhook、写入 KV 队列、换取 scoped installation token、读取 GitHub PR file metadata、做 compact PR-risk classification,并发布有长度上限的 Check Run summary;当前端到端 GitHub App webhook delivery smoke 已通过,证据记录在 [docs/hosted-operations-evidence.md](hosted-operations-evidence.md)。Cloudflare ingress 本身仍不是完整 source checkout scan worker。
382
+ 当前仓库已经包含未来 Hosted GitHub App 的设计文档、纯契约测试、第一个真实 Cloudflare hosted ingress,以及 Node/container read-only checkout scan runner。私有 staging GitHub App `ai-saas-guard-hosted` 已安装到 `zr9959/ai-saas-guard`,Cloudflare 已配置所需的云端凭据绑定。Worker 代码已经能接收签名 webhook、写入 KV 队列、换取 scoped installation token、读取 GitHub PR file metadata、做 compact PR-risk classification,并发布有长度上限的 selected-repository Check Run summary;`/github/app/install-info` 会返回公开安全的安装说明、权限、事件、隐私边界和卸载说明。签名 installation deletion 和 repository removal 事件会删除匹配的 compact records。当前端到端 GitHub App webhook delivery smoke 已通过,证据记录在 [docs/hosted-operations-evidence.md](hosted-operations-evidence.md)。Cloudflare ingress 本身仍不是完整 source checkout scan worker。
383
383
 
384
384
  相关文档:
385
385
 
@@ -412,7 +412,7 @@ GitHub Marketplace wrapper 决策见 [docs/github-marketplace-wrapper-decision.m
412
412
  - Hosted staging deployment planner:`ai-saas-guard/hosted/staging` 导出 `planHostedProviderBinding`、`planHostedStagingDeployment` 和 `planHostedGitHubAppPromotion`,把真实 provider 引用、Node/container deployment plan、hosted operational release-gate evidence 和 GitHub App deployment planning 组合起来;缺少 queue、store、worker sandbox、Check Run publisher、logs、metrics、rollback 或 incident-response 引用时,会阻止 staging exposure 和 production promotion;它本身仍然不会调用云平台、创建 GitHub App 或暴露公开 hosted 服务
413
413
  - Hosted staging harness:`ai-saas-guard/hosted/staging-harness` 导出 `createFileBackedHostedStagingHarness`、`createHostedStagingHarnessEvidence`、`createHostedStagingReleaseEvidenceBundle`、`evaluateHostedStagingReleaseEvidenceBundle` 和 `validateHostedLogBoundary`,可以在本地用 file-backed queue、compact report、Check Run request 和 worker sandbox 跑通签名 webhook replay、worker tick 和 cleanup 校验,把 success/failure cleanup probes 与 log-boundary samples 转成 release-gate evidence,并直接执行 hosted release gate 判断;它只是 staging 演练工具,不会调用云平台、创建 GitHub App、写真实 Check Run 或暴露公开 hosted 服务
414
414
  - Deployed worker staging evidence:`ai-saas-guard/hosted/deployed-staging` 导出 `createHostedDeployedWorkerStagingEvidenceAutomation`、`createHostedDeployedWorkerStagingEvidenceBundle` 和 `evaluateHostedDeployedWorkerStagingReleaseGate`,先验证 safe log samples,再把 public HTTPS health、signed webhook replay、deployed worker cleanup 以及外部 CI/scan/rollback evidence 转成 hosted release gate evidence;它不会部署云资源,也不会宣称 production hosted exposure
415
- - Cloudflare hosted ingress:`hosted/cloudflare-worker` 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`,提供 `/healthz`、`/github/app/manifest-callback` 和签名 `/github/webhook` intake;Worker 已具备 compact pull request identity、file/category risk signal Check Run metadata 路径;staging GitHub App ID 为 `3834787`,installation ID 为 `135085075`;真实 GitHub App webhook delivery 和 Check Run smoke 已通过;完整 source checkout worker deployment、monitoring、rollback 和 incident-response evidence 仍需要通过 hosted operational release gate
415
+ - Cloudflare hosted ingress:`hosted/cloudflare-worker` 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`,提供 `/healthz`、`/github/app/install-info`、`/github/app/manifest-callback` 和签名 `/github/webhook` intake;Worker 已具备 compact pull request identity、file/category risk signal、selected-repository Check Run metadata 和签名 installation cleanup 路径;staging GitHub App ID 为 `3834787`,installation ID 为 `135085075`;真实 GitHub App webhook delivery 和 Check Run smoke 已通过;完整 source checkout worker deployment、monitoring、rollback 和 incident-response evidence 仍需要通过 hosted operational release gate
416
416
  - Hosted GitHub App limited trial gate:`ai-saas-guard/hosted/contracts` 导出 `createHostedGitHubAppTrialGate`,确保 trial 只作用于 selected repositories,并要求 Check Run publication、compact report、worker cleanup 和 safe log-boundary evidence;它不宣称完整 hosted SaaS 已可用
417
417
  - Case-study fixture:`examples/case-study-ai-saas` 和 [case-study-ai-saas.md](case-study-ai-saas.md) 展示一个更接近真实 AI SaaS 的 auth、billing、Supabase、Next/Vercel 和 GitHub Actions 风险组合
418
418
  - Resource budget:`createLocalScanResourceBudget` 暴露大仓库本地扫描的保守预算:文件数、单文件字节、总字节、忽略 build/dependency 目录、不上传代码、不调用 LLM
@@ -89,4 +89,6 @@ The repository can now produce and validate the deployment plan, and a private s
89
89
 
90
90
  This is now a first-slice staging Worker deployment, not a complete hosted scanner. The Worker code verifies signatures, queues compact pull request identity records, exchanges scoped installation tokens, fetches PR file metadata, classifies PR-risk hotspots, and publishes bounded Check Runs. Current operations evidence is tracked in [hosted-operations-evidence.md](hosted-operations-evidence.md); health, signed webhook delivery, compact KV records, cleanup, and Check Run publication pass in staging. It still does not run full source checkout scan workers inside the Cloudflare Worker, store raw diffs, store source code, or expose a production hosted service.
91
91
 
92
+ The Worker also exposes public-safe installation guidance at `https://ai-saas-guard-hosted.zr9959.workers.dev/github/app/install-info`. That response documents the install URL, selected-repository boundary, first-slice permissions, subscribed events, privacy model, and uninstall cleanup wording without returning private keys, webhook secrets, installation tokens, source, diffs, or customer payloads. Signed installation deletion and repository removal events delete matching compact records from KV.
93
+
92
94
  The next deployment stage should wire the hosted service runtime, production adapters, [Node/container app skeleton](hosted-node-container-app.md), and [staging deployment planner](hosted-staging-deployment.md) to a real platform queue, compact report store, GitHub installation authentication, worker isolation layer, Checks API publisher, logs, metrics, rollback, and incident-response evidence.
@@ -6,10 +6,14 @@ Passing these checks does not make the project a pentest, certification, or full
6
6
 
7
7
  ## Current Evidence
8
8
 
9
- Recorded on 2026-05-24 from the deployed Cloudflare Worker and a temporary no-file-change GitHub PR smoke.
9
+ Recorded on 2026-05-25 from the deployed Cloudflare Worker plus the earlier temporary no-file-change GitHub PR smoke.
10
10
 
11
11
  | Check | Evidence | Result |
12
12
  | --- | --- | --- |
13
+ | Cloudflare Worker health, v0.38.0 | `GET https://ai-saas-guard-hosted.zr9959.workers.dev/healthz` returned `ok: true`, routes including `/github/app/install-info`, `checkRunPublisher: "configured"`, `scannerVersion: "0.38.0"`, and all privacy flags set to false for raw payloads, PR text, source, diffs, secrets, customer payloads, checkout paths, and installation tokens | Passed |
14
+ | Public install guidance, v0.38.0 | `GET https://ai-saas-guard-hosted.zr9959.workers.dev/github/app/install-info` returned the `ai-saas-guard-hosted` install URL, selected-repository boundary wording, first-slice permissions `checks: write`, `contents: read`, `metadata: read`, `pull_requests: read`, subscribed events `pull_request`, `installation`, and `installation_repositories`, uninstall cleanup wording, `scannerVersion: "0.38.0"`, and no private keys, webhook secrets, installation tokens, source, diffs, or customer payloads | Passed |
15
+ | Deployed Worker version, v0.38.0 | `wrangler deploy` uploaded 36.35 KiB / gzip 9.30 KiB and deployed version `5999ccce-c64d-4f3f-96c9-b46cff5a2aed` at `2026-05-25T10:51:30Z` verification time | Passed |
16
+ | Staging KV cleanup, v0.38.0 | `wrangler kv bulk delete` removed 104 old `delivery:` and `scan:` staging records, then `wrangler kv key list --namespace-id fa5344fbd7944de6a776bf8731d58460 --remote` returned `[]` | Passed |
13
17
  | Cloudflare Worker health | `GET https://ai-saas-guard-hosted.zr9959.workers.dev/healthz` returned `ok: true`, `checkRunPublisher: "configured"`, `scannerVersion: "0.28.0"`, and all privacy flags set to false for raw payloads, PR text, source, diffs, secrets, customer payloads, checkout paths, and installation tokens | Passed |
14
18
  | Deployed Worker version | `wrangler deployments list` showed current version `531d2286-86c6-4327-bfd0-67cad8693c10`, deployed at `2026-05-24T09:01:25.706Z` | Passed |
15
19
  | KV cleanup | `wrangler kv key list --namespace-id fa5344fbd7944de6a776bf8731d58460 --remote` returned `[]` after smoke cleanup | Passed |
@@ -5,11 +5,11 @@
5
5
  ## Current State
6
6
 
7
7
  - Package name: `ai-saas-guard`
8
- - Current published version: `0.37.0`
8
+ - Current published version: `0.38.0`
9
9
  - Next source candidate: none
10
10
  - npm registry state: published at <https://www.npmjs.com/package/ai-saas-guard>
11
11
  - First npm-published version: `0.1.1`
12
- - GitHub Release: `v0.37.0`
12
+ - GitHub Release: `v0.38.0`
13
13
  - Publish workflow: `.github/workflows/npm-publish.yml`
14
14
  - Trusted Publisher: GitHub Actions, `zr9959/ai-saas-guard`, workflow `npm-publish.yml`, allowed action `npm publish`
15
15
  - Long-lived npm publish token: not required
@@ -18,7 +18,7 @@
18
18
 
19
19
  Use GitHub Actions with npm Trusted Publisher/OIDC:
20
20
 
21
- 1. Create and review a release tag such as `v0.37.0`.
21
+ 1. Create and review a release tag such as `v0.38.0`.
22
22
  2. Publish from the GitHub Release or run the `Publish npm` workflow manually with `ref` set to that tag.
23
23
  3. Keep `permissions.id-token: write` in the workflow so npm can exchange the GitHub Actions OIDC identity for a short-lived publish credential.
24
24
  4. Run `npm publish --access public` from the workflow. Trusted publishing automatically generates provenance for this public package from this public repository.
@@ -66,7 +66,7 @@ Implemented surfaces:
66
66
  - hosted Node/container app skeleton document and helpers for safe health and webhook HTTP ingress, one-job worker ticks, in-memory provider adapters, provider reference validation, and the chosen `node_container` roles `webhook-ingress` and `scan-worker`
67
67
  - hosted staging deployment planner document and helpers for provider binding, staging release-gate evidence, Node/container deployment composition, and production GitHub App promotion gating
68
68
  - hosted staging harness document and helpers for local signed webhook replay, file-backed queue/report/Check Run artifacts, worker sandbox cleanup verification, and release-gate evidence fixtures without cloud calls
69
- - live Cloudflare hosted ingress at `https://ai-saas-guard-hosted.zr9959.workers.dev` with `/healthz`, `/github/app/manifest-callback`, signed `/github/webhook` intake, Cloudflare KV storage, private staging GitHub App `ai-saas-guard-hosted` (`3834787`) installed on `zr9959/ai-saas-guard`, Worker code for scoped installation-token exchange, PR file metadata fetching, compact PR-risk classification, and bounded Check Run publishing, plus hosted operations evidence in `docs/hosted-operations-evidence.md`
69
+ - live Cloudflare hosted ingress at `https://ai-saas-guard-hosted.zr9959.workers.dev` with `/healthz`, `/github/app/install-info`, `/github/app/manifest-callback`, signed `/github/webhook` intake, Cloudflare KV storage, private staging GitHub App `ai-saas-guard-hosted` (`3834787`) installed on `zr9959/ai-saas-guard`, Worker code for public-safe install guidance, scoped installation-token exchange, PR file metadata fetching, compact PR-risk classification, bounded selected-repository Check Run publishing, and signed installation deletion/repository removal cleanup, plus hosted operations evidence in `docs/hosted-operations-evidence.md`
70
70
  - resource caps for repository text collection, including per-file, total-file, and total-byte scan budgets to reduce worst-case memory use
71
71
  - hosted pre-implementation contracts document, hosted compact report fixture, and pure helpers for pull request webhook intake planning, durable scan queue upsert planning, worker read-only scan planning, Check Run publication planning, queue-safe pull request event parsing from trusted GitHub event fields, bounded check-run summary rendering, idempotent queue cleanup planning, worker checkout cleanup planning, retention/deletion cleanup planning, and operational release gate evaluation
72
72
  - implementation-ready hosted GitHub App permission contract for required permissions, optional PR comment permissions, selected repository installation, and out-of-scope broad permissions
@@ -5,11 +5,14 @@ This directory contains the first live hosted ingress for `ai-saas-guard`.
5
5
  It is intentionally narrow:
6
6
 
7
7
  - `GET /healthz` returns public-safe service health.
8
+ - `GET /github/app/install-info` returns public-safe installation guidance, first-slice permissions, subscribed events, privacy boundaries, and uninstall wording.
8
9
  - `GET /github/app/manifest-callback` acknowledges the GitHub App manifest redirect without storing the one-time code.
9
10
  - `POST /github/webhook` verifies GitHub `sha256` webhook signatures before JSON parsing or storage.
10
11
  - Requests over 1 MiB are rejected before JSON parsing or KV writes.
11
12
  - Signed `pull_request` events are reduced to trusted GitHub identity fields and stored in Cloudflare KV.
12
13
  - When GitHub App bindings are configured, the Worker exchanges a scoped installation token, fetches PR file metadata from GitHub, runs compact PR-risk classification, and publishes a bounded Check Run summary.
14
+ - The Check Run summary names the selected-repository hosted check, the Review queue, and a Manual proof prompt so reviewers know which trust-boundary files to inspect before merge.
15
+ - Signed `installation` deletion events and `installation_repositories` `repositories_removed` events delete matching compact `scan:<installation>:...` records from KV when KV list/delete bindings are available.
13
16
  - Duplicate GitHub delivery IDs are accepted idempotently.
14
17
  - Responses and KV records do not include raw webhook payloads, PR title/body text, source code, diffs, secrets, customer payloads, checkout paths, or installation tokens.
15
18
 
@@ -20,7 +23,7 @@ This Worker is a real hosted ingress with first-slice Check Run publishing code,
20
23
  - `HOSTED_EVENTS`: Cloudflare KV namespace for compact delivery and queued scan records.
21
24
  - `WEBHOOK_SECRET`: Worker secret matching the GitHub App webhook secret.
22
25
  - `GITHUB_APP_PRIVATE_KEY`: Worker secret for the staging GitHub App private key, used only in memory to sign short-lived GitHub App JWTs.
23
- - `SCANNER_VERSION`: public version string, currently `0.28.0`.
26
+ - `SCANNER_VERSION`: public version string, currently `0.38.0`.
24
27
  - `GITHUB_APP_ID`, `GITHUB_APP_SLUG`, `GITHUB_APP_INSTALLATION_ID`: public staging identifiers for the private GitHub App installation.
25
28
 
26
29
  ## Deployment
@@ -39,6 +42,7 @@ After creating the KV namespace, replace the placeholder namespace ID in `wrangl
39
42
  Current public staging endpoint:
40
43
 
41
44
  - Worker URL: `https://ai-saas-guard-hosted.zr9959.workers.dev`
45
+ - Install-info URL: `https://ai-saas-guard-hosted.zr9959.workers.dev/github/app/install-info`
42
46
  - KV namespace binding: `HOSTED_EVENTS`
43
47
  - KV namespace ID: `fa5344fbd7944de6a776bf8731d58460`
44
48
  - GitHub App slug: `ai-saas-guard-hosted`
@@ -47,6 +51,39 @@ Current public staging endpoint:
47
51
  - Installed repository: `zr9959/ai-saas-guard`
48
52
  - Mode: signed webhook ingress, compact queueing, PR file metadata classification, and bounded Check Run publishing
49
53
 
54
+ ## Public Install Guidance
55
+
56
+ `GET /github/app/install-info` is designed for the first screen a repository admin sees before installation. It returns only public-safe fields:
57
+
58
+ - install URL for `ai-saas-guard-hosted`
59
+ - first-slice permissions: `checks: write`, `contents: read`, `pull_requests: read`, and `metadata: read`
60
+ - subscribed events: `pull_request`, `installation`, and `installation_repositories`
61
+ - selected-repository boundary and explicit wording that the hosted check is not an AI reviewer, pentest, full audit, or certification
62
+ - uninstall/data deletion wording for compact records
63
+
64
+ Do not add raw app private keys, webhook secrets, installation tokens, source, diffs, customer payloads, or checkout paths to this response.
65
+
66
+ ## Check Run Shape
67
+
68
+ The Check Run is intentionally compact. It should answer:
69
+
70
+ - What changed at a launch-risk boundary?
71
+ - Which files are in the Review queue?
72
+ - What Manual proof should block merge until it passes?
73
+ - What selected-repository permissions did the hosted check use?
74
+
75
+ The Check Run must not include patch text, source snippets, PR title/body text, secrets, installation tokens, customer data, or private checkout paths.
76
+
77
+ ## Uninstall And Repository Removal
78
+
79
+ The Worker handles signed GitHub cleanup events, including installation deletion:
80
+
81
+ - `installation` with action `deleted` deletes compact scan records for the installation.
82
+ - `installation_repositories` with action `removed` deletes compact scan records for removed repository IDs.
83
+ - repeated cleanup is safe because deleting an already-removed compact record is a no-op.
84
+
85
+ Delivery audit records may remain for the normal KV TTL. They must not contain source, diffs, secrets, customer payloads, PR-authored text, checkout paths, or installation tokens.
86
+
50
87
  ## Release Boundary
51
88
 
52
89
  Do not expose this as the full product. The hosted operational release gate still requires deployed evidence for Check Run publication, worker cleanup, monitoring, rollback, incident response, dependency and deployment artifact scanning, and GitHub App installation behavior.
@@ -17,6 +17,13 @@ const EVENT_TTL_SECONDS = 60 * 60 * 24 * 30;
17
17
  export const MAX_WEBHOOK_PAYLOAD_BYTES = 1024 * 1024;
18
18
  const MAX_PR_FILES_PAGES = 3;
19
19
  const MAX_PATCH_CHARS_PER_FILE = 20_000;
20
+ const HOSTED_APP_PERMISSIONS = {
21
+ checks: "write",
22
+ contents: "read",
23
+ metadata: "read",
24
+ pull_requests: "read"
25
+ };
26
+ const HOSTED_APP_EVENTS = ["pull_request", "installation", "installation_repositories"];
20
27
 
21
28
  const CATEGORY_WEIGHTS = {
22
29
  "auth/session": 30,
@@ -38,6 +45,10 @@ export default {
38
45
  return jsonResponse(200, createHostedWorkerHealth(env));
39
46
  }
40
47
 
48
+ if (request.method === "GET" && url.pathname === "/github/app/install-info") {
49
+ return jsonResponse(200, createHostedInstallInfo(env));
50
+ }
51
+
41
52
  if (request.method === "GET" && url.pathname === "/github/app/manifest-callback") {
42
53
  return jsonResponse(200, createGitHubAppManifestCallback(url));
43
54
  }
@@ -129,6 +140,38 @@ export default {
129
140
  });
130
141
  }
131
142
 
143
+ if (eventName === "installation" || eventName === "installation_repositories") {
144
+ let installationPayload;
145
+ try {
146
+ installationPayload = JSON.parse(payload);
147
+ } catch {
148
+ return jsonResponse(400, {
149
+ accepted: false,
150
+ stage: "payload",
151
+ reason: "invalid_json",
152
+ deliveryId,
153
+ privacy: HOSTED_WORKER_PRIVACY
154
+ });
155
+ }
156
+
157
+ const cleanup = await handleInstallationCleanupEvent({
158
+ kv: env.HOSTED_EVENTS,
159
+ deliveryKey,
160
+ deliveryId,
161
+ eventName,
162
+ payload: installationPayload
163
+ });
164
+ return jsonResponse(202, {
165
+ accepted: true,
166
+ stage: cleanup.cleaned ? "cleanup" : "ignored",
167
+ reason: cleanup.reason,
168
+ deliveryId,
169
+ deletedRecords: cleanup.deletedRecords,
170
+ shouldCreateCheckRun: false,
171
+ privacy: HOSTED_WORKER_PRIVACY
172
+ });
173
+ }
174
+
132
175
  if (eventName !== "pull_request") {
133
176
  await storeJson(env.HOSTED_EVENTS, deliveryKey, {
134
177
  deliveryId,
@@ -275,7 +318,7 @@ export function createHostedWorkerHealth(env = {}) {
275
318
  ok: true,
276
319
  service: "ai-saas-guard-hosted",
277
320
  mode: "webhook-ingress",
278
- routes: ["/healthz", "/github/app/manifest-callback", "/github/webhook"],
321
+ routes: ["/healthz", "/github/app/install-info", "/github/app/manifest-callback", "/github/webhook"],
279
322
  storage: "cloudflare_kv",
280
323
  checkRunPublisher: hasGitHubCheckRunConfig(env) ? "configured" : "not_configured",
281
324
  scannerVersion: env.SCANNER_VERSION || "unknown",
@@ -283,6 +326,24 @@ export function createHostedWorkerHealth(env = {}) {
283
326
  };
284
327
  }
285
328
 
329
+ export function createHostedInstallInfo(env = {}) {
330
+ const slug = stringValue(env.GITHUB_APP_SLUG) ?? "ai-saas-guard-hosted";
331
+
332
+ return {
333
+ ok: true,
334
+ service: "ai-saas-guard-hosted",
335
+ installUrl: `https://github.com/apps/${encodeURIComponent(slug)}/installations/new`,
336
+ permissions: HOSTED_APP_PERMISSIONS,
337
+ events: HOSTED_APP_EVENTS,
338
+ boundary:
339
+ "Install on selected repositories only. The hosted check turns PR trust-boundary changes into a review queue; it is not an AI reviewer, pentest, full audit, or certification.",
340
+ uninstall:
341
+ "Uninstall or repository removal deletes compact records for that installation or repository when GitHub sends the signed event. Local CLI use does not depend on hosted installation.",
342
+ scannerVersion: env.SCANNER_VERSION || "unknown",
343
+ privacy: HOSTED_WORKER_PRIVACY
344
+ };
345
+ }
346
+
286
347
  export function createGitHubAppManifestCallback(url) {
287
348
  return {
288
349
  ok: true,
@@ -387,6 +448,97 @@ async function storeJson(kv, key, value) {
387
448
  await kv.put(key, JSON.stringify(value), { expirationTtl: EVENT_TTL_SECONDS });
388
449
  }
389
450
 
451
+ async function handleInstallationCleanupEvent({ kv, deliveryKey, deliveryId, eventName, payload }) {
452
+ const cleanup = resolveInstallationCleanup(payload, eventName);
453
+ await storeJson(kv, deliveryKey, {
454
+ deliveryId,
455
+ eventName,
456
+ accepted: true,
457
+ reason: cleanup.reason,
458
+ installationId: cleanup.installationId,
459
+ repositoryIds: cleanup.repositoryIds,
460
+ receivedAt: new Date().toISOString()
461
+ });
462
+
463
+ if (!cleanup.cleaned || cleanup.installationId === undefined) {
464
+ return { ...cleanup, deletedRecords: 0 };
465
+ }
466
+
467
+ const deletedRecords = await deleteCompactRecordsForInstallation({
468
+ kv,
469
+ installationId: cleanup.installationId,
470
+ repositoryIds: cleanup.repositoryIds
471
+ });
472
+ return { ...cleanup, deletedRecords };
473
+ }
474
+
475
+ function resolveInstallationCleanup(payload, eventName) {
476
+ const installationId = integerValue(payload?.installation?.id);
477
+ if (installationId === undefined) {
478
+ return {
479
+ cleaned: false,
480
+ reason: "missing_installation_id",
481
+ installationId,
482
+ repositoryIds: []
483
+ };
484
+ }
485
+
486
+ if (eventName === "installation" && payload?.action === "deleted") {
487
+ return {
488
+ cleaned: true,
489
+ reason: "installation_deleted",
490
+ installationId,
491
+ repositoryIds: []
492
+ };
493
+ }
494
+
495
+ if (eventName === "installation_repositories" && payload?.action === "removed") {
496
+ const repositoryIds = Array.isArray(payload?.repositories_removed)
497
+ ? payload.repositories_removed.map((repo) => integerValue(repo?.id)).filter((id) => id !== undefined)
498
+ : [];
499
+ return {
500
+ cleaned: repositoryIds.length > 0,
501
+ reason: repositoryIds.length > 0 ? "repositories_removed" : "no_removed_repositories",
502
+ installationId,
503
+ repositoryIds
504
+ };
505
+ }
506
+
507
+ return {
508
+ cleaned: false,
509
+ reason: "installation_event_ignored",
510
+ installationId,
511
+ repositoryIds: []
512
+ };
513
+ }
514
+
515
+ async function deleteCompactRecordsForInstallation({ kv, installationId, repositoryIds }) {
516
+ if (typeof kv?.list !== "function" || typeof kv?.delete !== "function") {
517
+ return 0;
518
+ }
519
+
520
+ let deleted = 0;
521
+ const prefixes =
522
+ repositoryIds.length > 0
523
+ ? repositoryIds.map((repositoryId) => `scan:${installationId}:${repositoryId}:`)
524
+ : [`scan:${installationId}:`];
525
+
526
+ for (const prefix of prefixes) {
527
+ let cursor;
528
+ do {
529
+ const page = await kv.list({ prefix, cursor });
530
+ for (const key of page.keys ?? []) {
531
+ if (typeof key?.name !== "string") continue;
532
+ await kv.delete(key.name);
533
+ deleted += 1;
534
+ }
535
+ cursor = page.list_complete === false ? page.cursor : undefined;
536
+ } while (cursor);
537
+ }
538
+
539
+ return deleted;
540
+ }
541
+
390
542
  async function runHostedPrRiskCheck({ env, identity, scanKey, scannerVersion }) {
391
543
  try {
392
544
  await storeJson(env.HOSTED_EVENTS, scanKey, {
@@ -672,6 +824,8 @@ function renderCheckRunSummary({ identity, report, scannerVersion }) {
672
824
  `Launch-risk gate: ai-saas-guard found ${report.summary.total} PR risk signal(s) for ${identity.repositoryFullName}#${identity.pullRequestNumber}. Review first: inspect the listed trust-boundary files before merge.`,
673
825
  `Scanner version: ${scannerVersion}.`,
674
826
  "",
827
+ "Selected-repository hosted check: this App uses checks:write, contents:read, pull_requests:read, and metadata:read for the installed repository only.",
828
+ "",
675
829
  "This is not an AI reviewer, pentest, certification, or full security audit. Review the listed files before merge.",
676
830
  "",
677
831
  "Launch decision queue:",
@@ -683,10 +837,11 @@ function renderCheckRunSummary({ identity, report, scannerVersion }) {
683
837
  ];
684
838
 
685
839
  if (report.topRiskyFiles.length > 0) {
686
- lines.push("", "Top files:");
840
+ lines.push("", "Review queue:");
687
841
  for (const file of report.topRiskyFiles.slice(0, 5)) {
688
842
  lines.push(`- ${file.path}: ${file.categories.join(", ")} (${file.added}+/${file.removed}-)`);
689
843
  }
844
+ lines.push("", "Manual proof:", "- Review the listed files locally and prove auth, billing, data, deploy, or test changes fail closed before merge.");
690
845
  }
691
846
 
692
847
  if (report.truncated) {
@@ -7,7 +7,7 @@
7
7
  "enabled": true
8
8
  },
9
9
  "vars": {
10
- "SCANNER_VERSION": "0.28.0",
10
+ "SCANNER_VERSION": "0.38.0",
11
11
  "GITHUB_APP_ID": "3834787",
12
12
  "GITHUB_APP_SLUG": "ai-saas-guard-hosted",
13
13
  "GITHUB_APP_INSTALLATION_ID": "135085075"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-saas-guard",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "description": "Local-first CLI that catches launch blockers in AI-built Next.js/Supabase/Stripe SaaS apps.",
5
5
  "readmeFilename": "README.md",
6
6
  "type": "module",