ai-saas-guard 0.26.2 → 0.27.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
@@ -45,6 +45,7 @@ AI can make a SaaS look finished while the real launch blockers sit in trust-bou
45
45
 
46
46
  One command returns a launch-readiness report with:
47
47
 
48
+ - a plain launch-gate verdict at the top of terminal and Markdown output
48
49
  - risky files sorted before cosmetic files
49
50
  - rule ID, severity, and file evidence
50
51
  - why the finding matters for an AI-built SaaS launch
@@ -72,13 +73,13 @@ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is availab
72
73
  | Area | Status |
73
74
  | --- | --- |
74
75
  | Public GitHub repository | Available |
75
- | npm CLI | `ai-saas-guard@0.26.2` |
76
- | GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.26.2` |
76
+ | npm CLI | `ai-saas-guard@0.27.0` |
77
+ | GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.27.0` |
77
78
  | Outputs | Terminal, JSON, SARIF, and PR-focused markdown |
78
79
  | Project config | `.ai-saas-guard.json` rule toggles, severity overrides, suppressions, and fail thresholds |
79
80
  | Privacy model | Local-first, read-only scan commands, no LLM calls, no code upload |
80
- | Versioned Action tags | `v0.26.2`, `v0` |
81
- | Current release | `0.26.2` README positioning, TypeScript 6, and dependency policy cleanup |
81
+ | Versioned Action tags | `v0.27.0`, `v0` |
82
+ | Current release | `0.27.0` launch-gate report summary for CLI and hosted Check Runs |
82
83
  | npm publishing | Trusted Publisher/OIDC, no long-lived publish token |
83
84
  | Repository trust hardening | Strict branch protection, Dependabot, CodeQL, fast-check fuzzing, signed release provenance assets, private vulnerability reporting, secret scanning, and push protection |
84
85
  | Cloudflare hosted ingress | Deployed at `https://ai-saas-guard-hosted.zr9959.workers.dev`; Worker health and Check Run publisher configuration are live, but end-to-end GitHub App webhook delivery is still blocked pending private App settings verification |
@@ -293,7 +294,7 @@ Use `suppressions` for narrower false-positive handling when one rule is noisy o
293
294
 
294
295
  ## GitHub Action
295
296
 
296
- The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.26.2` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
297
+ The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.27.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
297
298
 
298
299
  ```yaml
299
300
  name: ai-saas-guard
package/README.zh-CN.md CHANGED
@@ -44,6 +44,7 @@ AI 能很快把一个 SaaS 做到“看起来能用”。真正危险的是上
44
44
 
45
45
  一个命令会返回一份上线前 review 队列:
46
46
 
47
+ - terminal 和 Markdown 输出开头会先给出直观上线判断
47
48
  - 先看高风险文件,再看 UI 或普通重构
48
49
  - 每个 finding 都有 rule ID、severity 和文件证据
49
50
  - 说明它为什么会影响 AI 构建的 SaaS 上线
@@ -66,18 +67,18 @@ AI 能很快把一个 SaaS 做到“看起来能用”。真正危险的是上
66
67
 
67
68
  这个仓库是公开 GitHub 仓库。
68
69
 
69
- CLI 已发布到 npm:`ai-saas-guard@0.26.2`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.26.2`。
70
+ CLI 已发布到 npm:`ai-saas-guard@0.27.0`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.27.0`。
70
71
 
71
72
  | 模块 | 状态 |
72
73
  | --- | --- |
73
74
  | 公开 GitHub 仓库 | 已可用 |
74
- | npm CLI | `ai-saas-guard@0.26.2` |
75
- | GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.26.2` |
75
+ | npm CLI | `ai-saas-guard@0.27.0` |
76
+ | GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.27.0` |
76
77
  | 输出格式 | Terminal、JSON、SARIF 和 PR markdown |
77
78
  | 项目配置 | `.ai-saas-guard.json` 支持规则开关、severity 覆盖、suppressions 和 fail threshold |
78
79
  | 隐私模型 | 本地优先、只读扫描、不调用 LLM、不上传代码 |
79
- | 当前版本 | `0.26.2` README 定位、TypeScript 6 和依赖策略清理 |
80
- | Action 标签 | `v0.26.2`、`v0` |
80
+ | 当前版本 | `0.27.0` CLI hosted Check Run 的 launch-gate report summary |
81
+ | Action 标签 | `v0.27.0`、`v0` |
81
82
  | npm 发布 | GitHub Actions Trusted Publisher/OIDC,无需长期 npm token |
82
83
  | 仓库可信度加固 | 严格 branch protection、Dependabot、CodeQL、fast-check fuzzing、signed release provenance assets、private vulnerability reporting、secret scanning 和 push protection |
83
84
  | Cloudflare hosted ingress | 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`;Worker health 和 Check Run publisher 配置已在线,但端到端 GitHub App webhook delivery 仍需要验证私有 App 设置 |
@@ -479,13 +479,14 @@ export function createHostedCheckRunSummary(input) {
479
479
  const totalFindings = getHostedReportFindingTotal(report);
480
480
  const localCliCommand = `npx ai-saas-guard@${report.scannerVersion} pr-risk --root .`;
481
481
  const conclusion = resolveCheckRunConclusion(report, input.failOnSeverity);
482
+ const launchGate = hostedLaunchGateVerdict(report);
482
483
  return {
483
484
  name: CHECK_RUN_NAME,
484
485
  conclusion,
485
486
  output: {
486
487
  title: formatCheckRunTitle(totalFindings, conclusion, input.failOnSeverity),
487
- summary: "Review first: verify this launch-readiness signal before release; it is not a full security audit, pentest, or certification.",
488
- text: truncateMarkdown(formatCheckRunMarkdown(report, conclusion, localCliCommand), input.maxMarkdownChars)
488
+ summary: `Launch gate: ${launchGate}. Review first: verify this signal before release; it is not a full security audit, pentest, or certification.`,
489
+ text: truncateMarkdown(formatCheckRunMarkdown(report, conclusion, localCliCommand, launchGate), input.maxMarkdownChars)
489
490
  },
490
491
  annotations: report.evidence.slice(0, MAX_CHECK_RUN_ANNOTATIONS).map((finding) => {
491
492
  const line = finding.line ?? 1;
@@ -1056,6 +1057,22 @@ function getHostedReportFindingTotal(report) {
1056
1057
  const explicitTotal = typeof report.summaryCounts.total === "number" ? report.summaryCounts.total : 0;
1057
1058
  return Math.max(countedBySeverity, explicitTotal, report.evidence.length);
1058
1059
  }
1060
+ function hostedLaunchGateVerdict(report) {
1061
+ const summary = report.summaryCounts;
1062
+ if ((summary.critical ?? 0) > 0) {
1063
+ return "blocked";
1064
+ }
1065
+ if ((summary.high ?? 0) > 0) {
1066
+ return "review required";
1067
+ }
1068
+ if ((summary.medium ?? 0) > 0) {
1069
+ return "check before launch";
1070
+ }
1071
+ if ((summary.low ?? 0) > 0 || (summary.info ?? 0) > 0) {
1072
+ return "low-noise review";
1073
+ }
1074
+ return "clear from current heuristics";
1075
+ }
1059
1076
  function formatCheckRunTitle(totalFindings, conclusion, failOnSeverity) {
1060
1077
  if (totalFindings === 0) {
1061
1078
  return "AI SaaS Guard found no launch-readiness findings";
@@ -1065,7 +1082,7 @@ function formatCheckRunTitle(totalFindings, conclusion, failOnSeverity) {
1065
1082
  }
1066
1083
  return `AI SaaS Guard found ${totalFindings} finding${totalFindings === 1 ? "" : "s"} to review`;
1067
1084
  }
1068
- function formatCheckRunMarkdown(report, conclusion, localCliCommand) {
1085
+ function formatCheckRunMarkdown(report, conclusion, localCliCommand, launchGate) {
1069
1086
  const categories = getHostedCheckRunCategories(report);
1070
1087
  const filesToReview = getHostedCheckRunFiles(report);
1071
1088
  const findingLines = report.evidence.length === 0
@@ -1080,6 +1097,7 @@ function formatCheckRunMarkdown(report, conclusion, localCliCommand) {
1080
1097
  "",
1081
1098
  "Review first: verify findings locally before launch. This hosted check is not a full security audit, pentest, or certification.",
1082
1099
  "",
1100
+ `Launch gate: ${launchGate}`,
1083
1101
  `Conclusion: ${conclusion}`,
1084
1102
  `Local CLI: \`${localCliCommand}\``,
1085
1103
  `Retention: compact report ${report.retentionDays} days; raw source, raw diffs, secrets, and customer payloads are not retained.`,
@@ -0,0 +1,4 @@
1
+ import type { BaseReport, Finding } from "../types.js";
2
+ export declare function launchGateVerdict(report: BaseReport): string;
3
+ export declare function reviewFirst(findings: Finding[], limit?: number): string[];
4
+ export declare function manualProofSteps(findings: Finding[], limit?: number): string[];
@@ -0,0 +1,36 @@
1
+ export function launchGateVerdict(report) {
2
+ if (report.summary.critical > 0) {
3
+ return "blocked: critical launch-readiness findings need review before inviting users";
4
+ }
5
+ if (report.summary.high > 0) {
6
+ return "review required: high-risk launch paths need manual verification before launch";
7
+ }
8
+ if (report.summary.medium > 0) {
9
+ return "check before launch: medium-risk findings should be verified by an owner";
10
+ }
11
+ if (report.summary.low > 0 || report.summary.info > 0) {
12
+ return "low-noise review: confirm these hints against the launch checklist";
13
+ }
14
+ return "clear from current heuristics: no findings from this command, not a certification";
15
+ }
16
+ export function reviewFirst(findings, limit = 3) {
17
+ return findings.slice(0, limit).map((finding) => {
18
+ const firstEvidence = finding.evidence[0];
19
+ const location = firstEvidence?.line ? `${firstEvidence.file}:${firstEvidence.line}` : firstEvidence?.file;
20
+ return `${finding.severity.toUpperCase()} ${finding.ruleId}${location ? ` at ${location}` : ""} - ${finding.title}`;
21
+ });
22
+ }
23
+ export function manualProofSteps(findings, limit = 3) {
24
+ const steps = [];
25
+ const seen = new Set();
26
+ for (const finding of findings) {
27
+ const step = finding.suggestedVerification.trim();
28
+ if (!step || seen.has(step))
29
+ continue;
30
+ seen.add(step);
31
+ steps.push(step);
32
+ if (steps.length >= limit)
33
+ break;
34
+ }
35
+ return steps;
36
+ }
@@ -1,3 +1,4 @@
1
+ import { launchGateVerdict, manualProofSteps, reviewFirst } from "./launchGate.js";
1
2
  export function formatMarkdownReport(report) {
2
3
  if (report.command === "pr-risk")
3
4
  return `${formatPrRiskMarkdown(report)}\n`;
@@ -8,6 +9,7 @@ function formatPrRiskMarkdown(report) {
8
9
  lines.push("## ai-saas-guard PR risk summary");
9
10
  lines.push("");
10
11
  lines.push(summaryLine(report));
12
+ lines.push(`**Launch gate:** ${escapeMarkdownInline(launchGateVerdict(report))}`);
11
13
  if (report.categories.length > 0) {
12
14
  lines.push("");
13
15
  lines.push(`**Risk categories:** ${report.categories.map((category) => `\`${category}\``).join(", ")}`);
@@ -42,6 +44,8 @@ function formatGenericMarkdown(report) {
42
44
  lines.push(`## ai-saas-guard ${report.command}`);
43
45
  lines.push("");
44
46
  lines.push(summaryLine(report));
47
+ lines.push(`**Launch gate:** ${escapeMarkdownInline(launchGateVerdict(report))}`);
48
+ appendLaunchQueue(lines, report.findings);
45
49
  lines.push("");
46
50
  lines.push("### Findings");
47
51
  appendFindings(lines, report.findings);
@@ -56,6 +60,16 @@ function appendList(lines, items) {
56
60
  lines.push(`- ${item}`);
57
61
  }
58
62
  }
63
+ function appendLaunchQueue(lines, findings) {
64
+ if (findings.length === 0)
65
+ return;
66
+ lines.push("");
67
+ lines.push("### Review First");
68
+ appendList(lines, reviewFirst(findings).map(escapeMarkdownInline));
69
+ lines.push("");
70
+ lines.push("### Manual Proof To Run Next");
71
+ appendList(lines, manualProofSteps(findings).map(escapeMarkdownInline));
72
+ }
59
73
  function appendFindings(lines, findings) {
60
74
  if (findings.length === 0) {
61
75
  lines.push("");
@@ -1,14 +1,26 @@
1
+ import { launchGateVerdict, manualProofSteps, reviewFirst } from "./launchGate.js";
1
2
  export function formatTerminalReport(report) {
2
3
  const lines = [];
3
4
  lines.push(`ai-saas-guard ${report.command}`);
4
5
  lines.push(`Root: ${report.rootDir}`);
5
6
  lines.push(`Findings: ${report.summary.total} total | critical ${report.summary.critical} | high ${report.summary.high} | medium ${report.summary.medium} | low ${report.summary.low} | info ${report.summary.info}`);
7
+ lines.push(`Launch gate: ${launchGateVerdict(report)}`);
6
8
  if (report.findings.length === 0) {
7
9
  lines.push("");
8
10
  lines.push("No heuristic launch-readiness risks found by this command.");
9
11
  appendCommandExtras(lines, report);
10
12
  return lines.join("\n");
11
13
  }
14
+ lines.push("");
15
+ lines.push("Review first:");
16
+ for (const item of reviewFirst(report.findings)) {
17
+ lines.push(`- ${item}`);
18
+ }
19
+ lines.push("");
20
+ lines.push("Manual proof to run next:");
21
+ for (const step of manualProofSteps(report.findings)) {
22
+ lines.push(`- ${step}`);
23
+ }
12
24
  for (const [index, item] of report.findings.entries()) {
13
25
  lines.push("");
14
26
  lines.push(`${index + 1}. [${item.severity.toUpperCase()}] ${item.title}`);
@@ -2,7 +2,7 @@
2
2
 
3
3
  `ai-saas-guard` ships as a composite GitHub Action for pull request and code scanning workflows.
4
4
 
5
- Use `zr9959/ai-saas-guard@v0` for the latest compatible pre-1.0 Action. Use a specific tag such as `v0.26.2` or a reviewed commit SHA when reproducibility is more important than automatic minor updates.
5
+ Use `zr9959/ai-saas-guard@v0` for the latest compatible pre-1.0 Action. Use a specific tag such as `v0.27.0` or a reviewed commit SHA when reproducibility is more important than automatic minor updates.
6
6
 
7
7
  ## PR Summary
8
8
 
@@ -5,11 +5,11 @@
5
5
  ## Current State
6
6
 
7
7
  - Package name: `ai-saas-guard`
8
- - Current published version: `0.26.2`
8
+ - Current published version: `0.27.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.26.2`
12
+ - GitHub Release: `v0.27.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.26.2`.
21
+ 1. Create and review a release tag such as `v0.27.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.
@@ -160,7 +160,7 @@ OpenSSF Best Practices:
160
160
  Publishing:
161
161
 
162
162
  - npm package: `ai-saas-guard`
163
- - Current published release line: `v0.26.2`
163
+ - Current published release line: `v0.27.0`
164
164
  - Next source candidate: none
165
165
  - Publish workflow: `.github/workflows/npm-publish.yml`
166
166
  - Trusted Publisher: GitHub Actions for `zr9959/ai-saas-guard`, workflow `npm-publish.yml`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-saas-guard",
3
- "version": "0.26.2",
3
+ "version": "0.27.0",
4
4
  "description": "Repo-local launch-readiness scanner for AI-built SaaS apps.",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/zr9959/ai-saas-guard#readme",