ai-saas-guard 0.26.2 → 0.27.1
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 +6 -5
- package/README.zh-CN.md +6 -5
- package/dist/hosted/contracts.js +21 -3
- package/dist/report/launchGate.d.ts +4 -0
- package/dist/report/launchGate.js +36 -0
- package/dist/report/markdown.js +14 -0
- package/dist/report/terminal.js +12 -0
- package/docs/github-action.md +1 -1
- package/docs/npm-publishing.md +3 -3
- package/docs/project-handoff.md +1 -1
- package/package.json +2 -1
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.
|
|
76
|
-
| GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.
|
|
76
|
+
| npm CLI | `ai-saas-guard@0.27.1` |
|
|
77
|
+
| GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.27.1` |
|
|
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.
|
|
81
|
-
| Current release | `0.
|
|
81
|
+
| Versioned Action tags | `v0.27.1`, `v0` |
|
|
82
|
+
| Current release | `0.27.1` npm README metadata fix; launch-gate report summary remains current |
|
|
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.
|
|
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.1` 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.
|
|
70
|
+
CLI 已发布到 npm:`ai-saas-guard@0.27.1`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.27.1`。
|
|
70
71
|
|
|
71
72
|
| 模块 | 状态 |
|
|
72
73
|
| --- | --- |
|
|
73
74
|
| 公开 GitHub 仓库 | 已可用 |
|
|
74
|
-
| npm CLI | `ai-saas-guard@0.
|
|
75
|
-
| GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.
|
|
75
|
+
| npm CLI | `ai-saas-guard@0.27.1` |
|
|
76
|
+
| GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.27.1` |
|
|
76
77
|
| 输出格式 | Terminal、JSON、SARIF 和 PR markdown |
|
|
77
78
|
| 项目配置 | `.ai-saas-guard.json` 支持规则开关、severity 覆盖、suppressions 和 fail threshold |
|
|
78
79
|
| 隐私模型 | 本地优先、只读扫描、不调用 LLM、不上传代码 |
|
|
79
|
-
| 当前版本 | `0.
|
|
80
|
-
| Action 标签 | `v0.
|
|
80
|
+
| 当前版本 | `0.27.1` npm README metadata fix;CLI 和 hosted Check Run 的 launch-gate report summary 仍是当前功能 |
|
|
81
|
+
| Action 标签 | `v0.27.1`、`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 设置 |
|
package/dist/hosted/contracts.js
CHANGED
|
@@ -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:
|
|
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
|
+
}
|
package/dist/report/markdown.js
CHANGED
|
@@ -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("");
|
package/dist/report/terminal.js
CHANGED
|
@@ -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}`);
|
package/docs/github-action.md
CHANGED
|
@@ -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.
|
|
5
|
+
Use `zr9959/ai-saas-guard@v0` for the latest compatible pre-1.0 Action. Use a specific tag such as `v0.27.1` or a reviewed commit SHA when reproducibility is more important than automatic minor updates.
|
|
6
6
|
|
|
7
7
|
## PR Summary
|
|
8
8
|
|
package/docs/npm-publishing.md
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
## Current State
|
|
6
6
|
|
|
7
7
|
- Package name: `ai-saas-guard`
|
|
8
|
-
- Current published version: `0.
|
|
8
|
+
- Current published version: `0.27.1`
|
|
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.
|
|
12
|
+
- GitHub Release: `v0.27.1`
|
|
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.
|
|
21
|
+
1. Create and review a release tag such as `v0.27.1`.
|
|
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.
|
package/docs/project-handoff.md
CHANGED
|
@@ -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.
|
|
163
|
+
- Current published release line: `v0.27.1`
|
|
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,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-saas-guard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "Repo-local launch-readiness scanner for AI-built SaaS apps.",
|
|
5
|
+
"readmeFilename": "README.md",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"homepage": "https://github.com/zr9959/ai-saas-guard#readme",
|
|
7
8
|
"repository": {
|