ai-saas-guard 0.29.2 → 0.30.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 +18 -10
- package/dist/cli.js +8 -1
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +21 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/report/launchGate.d.ts +1 -0
- package/dist/report/launchGate.js +22 -0
- package/dist/report/markdown.js +36 -1
- package/dist/report/terminal.js +43 -1
- package/dist/types.d.ts +9 -1
- package/docs/README.zh-CN.md +18 -10
- package/docs/demo-quickstart.md +14 -0
- package/docs/npm-publishing.md +3 -3
- package/docs/sample-launch-report.md +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,12 @@ These are the failures that hurt after real users arrive:
|
|
|
46
46
|
|
|
47
47
|
## 60-Second Local Check
|
|
48
48
|
|
|
49
|
+
See the public demo output without cloning a repo:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx ai-saas-guard@latest demo
|
|
53
|
+
```
|
|
54
|
+
|
|
49
55
|
Run it against your app without installing anything globally:
|
|
50
56
|
|
|
51
57
|
```bash
|
|
@@ -65,13 +71,10 @@ You get rule IDs, severity, file evidence, why it matters, how to verify it manu
|
|
|
65
71
|
Want to see the report before scanning your own repo?
|
|
66
72
|
|
|
67
73
|
```bash
|
|
68
|
-
|
|
69
|
-
cd ai-saas-guard
|
|
70
|
-
npx ai-saas-guard@latest scan --root examples/demo-risky-saas
|
|
71
|
-
npx ai-saas-guard@latest scan --root examples/demo-safe-saas
|
|
74
|
+
npx ai-saas-guard@latest demo
|
|
72
75
|
```
|
|
73
76
|
|
|
74
|
-
The
|
|
77
|
+
The demo command uses packaged public fixtures: `examples/demo-risky-saas` currently returns 19 intentional findings across Stripe, Supabase, silent-success paths, Next/Vercel deploy hints, and GitHub Actions; `examples/demo-safe-saas` returns 0 findings for the same broad surfaces with safer static patterns. See [docs/demo-quickstart.md](docs/demo-quickstart.md) if you want to inspect the fixture files locally.
|
|
75
78
|
|
|
76
79
|
## See The Output
|
|
77
80
|
|
|
@@ -94,6 +97,10 @@ Verify: force the upstream billing call to fail and confirm the route returns an
|
|
|
94
97
|
MEDIUM deploy.next.missing-security-headers
|
|
95
98
|
File: app/api/billing/checkout/route.ts
|
|
96
99
|
Verify: inspect production response headers for auth, billing, and API pages.
|
|
100
|
+
|
|
101
|
+
Next steps
|
|
102
|
+
- Fix critical and high trust-boundary findings first.
|
|
103
|
+
- Run the manual proof steps in staging and confirm each risky path fails closed.
|
|
97
104
|
```
|
|
98
105
|
|
|
99
106
|
## What You Get
|
|
@@ -125,6 +132,7 @@ One command returns a launch-readiness report with:
|
|
|
125
132
|
Run the published CLI without installing it globally:
|
|
126
133
|
|
|
127
134
|
```bash
|
|
135
|
+
npx ai-saas-guard@latest demo
|
|
128
136
|
npx ai-saas-guard@latest scan --root /path/to/your-saas
|
|
129
137
|
```
|
|
130
138
|
|
|
@@ -179,13 +187,13 @@ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is availab
|
|
|
179
187
|
| Area | Status |
|
|
180
188
|
| --- | --- |
|
|
181
189
|
| Public GitHub repository | Available |
|
|
182
|
-
| npm CLI | `ai-saas-guard@0.
|
|
183
|
-
| GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.
|
|
190
|
+
| npm CLI | `ai-saas-guard@0.30.0` |
|
|
191
|
+
| GitHub Action | `zr9959/ai-saas-guard@v0` or fixed tag `v0.30.0` |
|
|
184
192
|
| Outputs | Terminal, JSON, SARIF, and PR-focused markdown |
|
|
185
193
|
| Project config | `.ai-saas-guard.json` rule toggles, severity overrides, suppressions, and fail thresholds |
|
|
186
194
|
| Privacy model | Local-first, read-only scan commands, no LLM calls, no code upload |
|
|
187
|
-
| Versioned Action tags | `v0.
|
|
188
|
-
| Current release | `0.
|
|
195
|
+
| Versioned Action tags | `v0.30.0`, `v0` |
|
|
196
|
+
| Current release | `0.30.0` adds `ai-saas-guard demo`, Next steps in human-readable reports, a more targeted quickstart feedback template, and refreshed first-run docs |
|
|
189
197
|
| npm publishing | Trusted Publisher/OIDC, no long-lived publish token |
|
|
190
198
|
| Repository trust hardening | Strict branch protection, Dependabot, CodeQL, fast-check fuzzing, signed release provenance assets, private vulnerability reporting, secret scanning, and push protection |
|
|
191
199
|
| 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 |
|
|
@@ -352,7 +360,7 @@ Use `suppressions` for narrower false-positive handling when one rule is noisy o
|
|
|
352
360
|
|
|
353
361
|
## GitHub Action
|
|
354
362
|
|
|
355
|
-
The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.
|
|
363
|
+
The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.30.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
|
|
356
364
|
|
|
357
365
|
```yaml
|
|
358
366
|
name: ai-saas-guard
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { applyGuardConfig, loadGuardConfig } from "./config.js";
|
|
4
|
-
import { checkActions, checkMcp, checkStripe, checkSupabase, classifyPrRisk, scanRepository } from "./index.js";
|
|
4
|
+
import { checkActions, checkMcp, checkStripe, checkSupabase, classifyPrRisk, runShowcase, scanRepository } from "./index.js";
|
|
5
5
|
import { formatJsonReport } from "./report/json.js";
|
|
6
6
|
import { formatMarkdownReport } from "./report/markdown.js";
|
|
7
7
|
import { formatSarifReport } from "./report/sarif.js";
|
|
@@ -12,6 +12,11 @@ async function main(argv) {
|
|
|
12
12
|
process.stdout.write(helpText());
|
|
13
13
|
return 0;
|
|
14
14
|
}
|
|
15
|
+
if (args.command === "demo") {
|
|
16
|
+
const report = await runShowcase();
|
|
17
|
+
process.stdout.write(formatReport(report, args.format));
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
15
20
|
const config = await loadGuardConfig(args.rootDir, args.configPath);
|
|
16
21
|
let report;
|
|
17
22
|
switch (args.command) {
|
|
@@ -165,6 +170,7 @@ Repo-local launch-readiness scanner for AI-built SaaS apps.
|
|
|
165
170
|
|
|
166
171
|
Usage:
|
|
167
172
|
ai-saas-guard scan [--root <repo>] [--config <file>] [--json|--sarif] [--fail-on <severity>]
|
|
173
|
+
ai-saas-guard demo [--json|--markdown]
|
|
168
174
|
ai-saas-guard check-supabase [--root <repo>] [--config <file>] [--doctor] [--json|--sarif] [--fail-on <severity>]
|
|
169
175
|
ai-saas-guard check-stripe [--root <repo>] [--config <file>] [--json|--sarif] [--fail-on <severity>]
|
|
170
176
|
ai-saas-guard check-mcp [--root <repo>] [--config <file>] [--policy-template] [--json|--sarif] [--fail-on <severity>]
|
|
@@ -175,6 +181,7 @@ Defaults:
|
|
|
175
181
|
- read-only
|
|
176
182
|
- no network calls
|
|
177
183
|
- no account or login required
|
|
184
|
+
- demo uses packaged public fixtures and ignores project config/fail thresholds
|
|
178
185
|
- terminal output by default, JSON with --json
|
|
179
186
|
- SARIF output for GitHub code scanning with --sarif
|
|
180
187
|
- PR-focused markdown summary with --markdown
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { scanRepository } from "./scan.js";
|
|
4
|
+
import { createReport } from "../report/findings.js";
|
|
5
|
+
import { nextSteps } from "../report/launchGate.js";
|
|
6
|
+
export async function runShowcase() {
|
|
7
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
8
|
+
const risky = await scanRepository({
|
|
9
|
+
rootDir: resolve(packageRoot, "examples", "demo-risky-saas")
|
|
10
|
+
});
|
|
11
|
+
const safe = await scanRepository({
|
|
12
|
+
rootDir: resolve(packageRoot, "examples", "demo-safe-saas")
|
|
13
|
+
});
|
|
14
|
+
return createReport("demo", packageRoot, risky.findings, {
|
|
15
|
+
demos: {
|
|
16
|
+
risky,
|
|
17
|
+
safe
|
|
18
|
+
},
|
|
19
|
+
nextSteps: nextSteps(risky.findings)
|
|
20
|
+
});
|
|
21
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { scanRepository } from "./commands/scan.js";
|
|
2
|
+
export { runShowcase } from "./commands/demo.js";
|
|
2
3
|
export { checkStripe } from "./commands/checkStripe.js";
|
|
3
4
|
export { checkSupabase } from "./commands/checkSupabase.js";
|
|
4
5
|
export { checkMcp } from "./commands/checkMcp.js";
|
|
@@ -7,7 +8,7 @@ export { classifyPrRisk } from "./commands/prRisk.js";
|
|
|
7
8
|
export { applyGuardConfig, defaultConfigFileName, loadGuardConfig } from "./config.js";
|
|
8
9
|
export { createScanContext } from "./context.js";
|
|
9
10
|
export { getRuleMetadata, RULE_CATALOG } from "./rules/catalog.js";
|
|
10
|
-
export type { BaseReport, CommandName, Evidence, Finding, ActionsReport, McpOptions, McpPolicyTemplate, McpReport, McpServerInventory, McpSideEffect, PrRiskFile, PrRiskReport, ScanOptions, StripeReport, SupabaseOptions, SupabaseDoctorReport, SupabaseReport } from "./types.js";
|
|
11
|
+
export type { BaseReport, CommandName, Evidence, Finding, ActionsReport, McpOptions, McpPolicyTemplate, McpReport, McpServerInventory, McpSideEffect, PrRiskFile, PrRiskReport, ScanOptions, ShowcaseReport, StripeReport, SupabaseOptions, SupabaseDoctorReport, SupabaseReport } from "./types.js";
|
|
11
12
|
export type { ScanContext, ScanInput } from "./context.js";
|
|
12
13
|
export type { FindingSuppression, GuardConfig, RuleConfigValue } from "./config.js";
|
|
13
14
|
export type { RuleMetadata, RuleStability } from "./rules/catalog.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { scanRepository } from "./commands/scan.js";
|
|
2
|
+
export { runShowcase } from "./commands/demo.js";
|
|
2
3
|
export { checkStripe } from "./commands/checkStripe.js";
|
|
3
4
|
export { checkSupabase } from "./commands/checkSupabase.js";
|
|
4
5
|
export { checkMcp } from "./commands/checkMcp.js";
|
|
@@ -2,3 +2,4 @@ import type { BaseReport, Finding } from "../types.js";
|
|
|
2
2
|
export declare function launchGateVerdict(report: BaseReport): string;
|
|
3
3
|
export declare function reviewFirst(findings: Finding[], limit?: number): string[];
|
|
4
4
|
export declare function manualProofSteps(findings: Finding[], limit?: number): string[];
|
|
5
|
+
export declare function nextSteps(findings: Finding[]): string[];
|
|
@@ -34,3 +34,25 @@ export function manualProofSteps(findings, limit = 3) {
|
|
|
34
34
|
}
|
|
35
35
|
return steps;
|
|
36
36
|
}
|
|
37
|
+
export function nextSteps(findings) {
|
|
38
|
+
if (findings.length === 0) {
|
|
39
|
+
return [
|
|
40
|
+
"Keep this report with the launch checklist, then run a two-account auth/data-access check and a deploy-preview smoke before inviting real users."
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
const steps = [];
|
|
44
|
+
const hasCriticalOrHigh = findings.some((finding) => finding.severity === "critical" || finding.severity === "high");
|
|
45
|
+
const hasMedium = findings.some((finding) => finding.severity === "medium");
|
|
46
|
+
const hasLowNoise = findings.some((finding) => finding.severity === "low" || finding.severity === "info");
|
|
47
|
+
if (hasCriticalOrHigh) {
|
|
48
|
+
steps.push("Fix critical and high trust-boundary findings first: auth/session, billing/webhook, tenant data, and silent-success paths.");
|
|
49
|
+
}
|
|
50
|
+
else if (hasMedium) {
|
|
51
|
+
steps.push("Verify medium-risk launch findings with the owning developer before production traffic.");
|
|
52
|
+
}
|
|
53
|
+
steps.push("Run the manual proof steps above in staging and confirm each risky path fails closed.");
|
|
54
|
+
if (hasLowNoise) {
|
|
55
|
+
steps.push("Treat low and info deploy/CI hygiene hints as cleanup after critical, high, and medium launch paths are understood.");
|
|
56
|
+
}
|
|
57
|
+
return steps;
|
|
58
|
+
}
|
package/dist/report/markdown.js
CHANGED
|
@@ -1,9 +1,36 @@
|
|
|
1
|
-
import { launchGateVerdict, manualProofSteps, reviewFirst } from "./launchGate.js";
|
|
1
|
+
import { launchGateVerdict, manualProofSteps, nextSteps, reviewFirst } from "./launchGate.js";
|
|
2
2
|
export function formatMarkdownReport(report) {
|
|
3
|
+
if (report.command === "demo")
|
|
4
|
+
return `${formatDemoMarkdown(report)}\n`;
|
|
3
5
|
if (report.command === "pr-risk")
|
|
4
6
|
return `${formatPrRiskMarkdown(report)}\n`;
|
|
5
7
|
return `${formatGenericMarkdown(report)}\n`;
|
|
6
8
|
}
|
|
9
|
+
function formatDemoMarkdown(report) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push("## ai-saas-guard demo");
|
|
12
|
+
lines.push("");
|
|
13
|
+
lines.push("Synthetic public demo for the local-first launch gate. This is not a pentest, full audit, or certification.");
|
|
14
|
+
lines.push("");
|
|
15
|
+
lines.push(`- Risky demo: ${escapeMarkdownInline(summaryText(report.demos.risky))}`);
|
|
16
|
+
lines.push(`- Safe demo: ${escapeMarkdownInline(summaryText(report.demos.safe))}`);
|
|
17
|
+
lines.push("");
|
|
18
|
+
lines.push("### Review First");
|
|
19
|
+
appendList(lines, reviewFirst(report.demos.risky.findings).map(escapeMarkdownInline));
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push("### Manual Proof To Run Next");
|
|
22
|
+
appendList(lines, manualProofSteps(report.demos.risky.findings).map(escapeMarkdownInline));
|
|
23
|
+
lines.push("");
|
|
24
|
+
lines.push("### Next Steps");
|
|
25
|
+
appendList(lines, report.nextSteps.map(escapeMarkdownInline));
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("Run against your app:");
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push("```bash");
|
|
30
|
+
lines.push("npx ai-saas-guard@latest scan --root /path/to/your-saas");
|
|
31
|
+
lines.push("```");
|
|
32
|
+
return lines.join("\n");
|
|
33
|
+
}
|
|
7
34
|
function formatPrRiskMarkdown(report) {
|
|
8
35
|
const lines = [];
|
|
9
36
|
lines.push("## ai-saas-guard PR risk summary");
|
|
@@ -69,6 +96,9 @@ function appendLaunchQueue(lines, findings) {
|
|
|
69
96
|
lines.push("");
|
|
70
97
|
lines.push("### Manual Proof To Run Next");
|
|
71
98
|
appendList(lines, manualProofSteps(findings).map(escapeMarkdownInline));
|
|
99
|
+
lines.push("");
|
|
100
|
+
lines.push("### Next Steps");
|
|
101
|
+
appendList(lines, nextSteps(findings).map(escapeMarkdownInline));
|
|
72
102
|
}
|
|
73
103
|
function appendFindings(lines, findings) {
|
|
74
104
|
if (findings.length === 0) {
|
|
@@ -135,3 +165,8 @@ function escapeMarkdownTableCell(value) {
|
|
|
135
165
|
function escapeMarkdownInline(value) {
|
|
136
166
|
return value.replace(/\r?\n/g, " ").replaceAll("|", "\\|").trim();
|
|
137
167
|
}
|
|
168
|
+
function summaryText(report) {
|
|
169
|
+
if (report.summary.total === 0)
|
|
170
|
+
return "0 findings";
|
|
171
|
+
return `${report.summary.total} findings: ${report.summary.critical} critical, ${report.summary.high} high, ${report.summary.medium} medium, ${report.summary.low} low, ${report.summary.info} info`;
|
|
172
|
+
}
|
package/dist/report/terminal.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { launchGateVerdict, manualProofSteps, reviewFirst } from "./launchGate.js";
|
|
1
|
+
import { launchGateVerdict, manualProofSteps, nextSteps, reviewFirst } from "./launchGate.js";
|
|
2
2
|
export function formatTerminalReport(report) {
|
|
3
|
+
if (report.command === "demo")
|
|
4
|
+
return formatDemoTerminalReport(report);
|
|
3
5
|
const lines = [];
|
|
4
6
|
lines.push(`ai-saas-guard ${report.command}`);
|
|
5
7
|
lines.push(`Root: ${report.rootDir}`);
|
|
@@ -21,6 +23,11 @@ export function formatTerminalReport(report) {
|
|
|
21
23
|
for (const step of manualProofSteps(report.findings)) {
|
|
22
24
|
lines.push(`- ${step}`);
|
|
23
25
|
}
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("Next steps:");
|
|
28
|
+
for (const step of nextSteps(report.findings)) {
|
|
29
|
+
lines.push(`- ${step}`);
|
|
30
|
+
}
|
|
24
31
|
for (const [index, item] of report.findings.entries()) {
|
|
25
32
|
lines.push("");
|
|
26
33
|
lines.push(`${index + 1}. [${item.severity.toUpperCase()}] ${item.title}`);
|
|
@@ -38,6 +45,41 @@ export function formatTerminalReport(report) {
|
|
|
38
45
|
appendCommandExtras(lines, report);
|
|
39
46
|
return lines.join("\n");
|
|
40
47
|
}
|
|
48
|
+
function formatDemoTerminalReport(report) {
|
|
49
|
+
const lines = [];
|
|
50
|
+
lines.push("ai-saas-guard demo");
|
|
51
|
+
lines.push("Synthetic public demo for the local-first launch gate.");
|
|
52
|
+
lines.push("This is not a pentest, full audit, or certification.");
|
|
53
|
+
lines.push("");
|
|
54
|
+
lines.push(`Risky demo: ${summaryText(report.demos.risky)}`);
|
|
55
|
+
lines.push(`Safe demo: ${summaryText(report.demos.safe)}`);
|
|
56
|
+
lines.push("");
|
|
57
|
+
lines.push("Review first:");
|
|
58
|
+
for (const item of reviewFirst(report.demos.risky.findings)) {
|
|
59
|
+
lines.push(`- ${item}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push("Manual proof to run next:");
|
|
63
|
+
for (const step of manualProofSteps(report.demos.risky.findings)) {
|
|
64
|
+
lines.push(`- ${step}`);
|
|
65
|
+
}
|
|
66
|
+
lines.push("");
|
|
67
|
+
lines.push("Next steps:");
|
|
68
|
+
for (const step of report.nextSteps) {
|
|
69
|
+
lines.push(`- ${step}`);
|
|
70
|
+
}
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push("Run against your app:");
|
|
73
|
+
lines.push(" npx ai-saas-guard@latest scan --root /path/to/your-saas");
|
|
74
|
+
lines.push("");
|
|
75
|
+
lines.push("Read more: https://github.com/zr9959/ai-saas-guard/blob/main/docs/demo-quickstart.md");
|
|
76
|
+
return lines.join("\n");
|
|
77
|
+
}
|
|
78
|
+
function summaryText(report) {
|
|
79
|
+
if (report.summary.total === 0)
|
|
80
|
+
return "0 findings";
|
|
81
|
+
return `${report.summary.total} findings: ${report.summary.critical} critical, ${report.summary.high} high, ${report.summary.medium} medium, ${report.summary.low} low, ${report.summary.info} info`;
|
|
82
|
+
}
|
|
41
83
|
function appendCommandExtras(lines, report) {
|
|
42
84
|
if (report.command === "check-supabase") {
|
|
43
85
|
const supabase = report;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type Severity = "critical" | "high" | "medium" | "low" | "info";
|
|
2
|
-
export type CommandName = "scan" | "check-supabase" | "check-stripe" | "check-mcp" | "check-actions" | "pr-risk";
|
|
2
|
+
export type CommandName = "scan" | "demo" | "check-supabase" | "check-stripe" | "check-mcp" | "check-actions" | "pr-risk";
|
|
3
3
|
export interface Evidence {
|
|
4
4
|
file: string;
|
|
5
5
|
line?: number;
|
|
@@ -40,6 +40,14 @@ export interface BaseReport {
|
|
|
40
40
|
findings: Finding[];
|
|
41
41
|
summary: Summary;
|
|
42
42
|
}
|
|
43
|
+
export interface ShowcaseReport extends BaseReport {
|
|
44
|
+
command: "demo";
|
|
45
|
+
demos: {
|
|
46
|
+
risky: BaseReport;
|
|
47
|
+
safe: BaseReport;
|
|
48
|
+
};
|
|
49
|
+
nextSteps: string[];
|
|
50
|
+
}
|
|
43
51
|
export interface StripeReport extends BaseReport {
|
|
44
52
|
command: "check-stripe";
|
|
45
53
|
webhookFiles: string[];
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -45,6 +45,12 @@ AI 能很快把一个 SaaS 做到“看起来能用”:能登录、能打开 c
|
|
|
45
45
|
|
|
46
46
|
## 60 秒本地检查
|
|
47
47
|
|
|
48
|
+
不用 clone 仓库,先看公开 demo 输出:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx ai-saas-guard@latest demo
|
|
52
|
+
```
|
|
53
|
+
|
|
48
54
|
无需全局安装,直接扫你的应用:
|
|
49
55
|
|
|
50
56
|
```bash
|
|
@@ -64,13 +70,10 @@ npx ai-saas-guard@latest pr-risk --root /path/to/your-saas --base origin/main --
|
|
|
64
70
|
如果你还不想先扫自己的私有仓库,可以先跑公开 fixture:
|
|
65
71
|
|
|
66
72
|
```bash
|
|
67
|
-
|
|
68
|
-
cd ai-saas-guard
|
|
69
|
-
npx ai-saas-guard@latest scan --root examples/demo-risky-saas
|
|
70
|
-
npx ai-saas-guard@latest scan --root examples/demo-safe-saas
|
|
73
|
+
npx ai-saas-guard@latest demo
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
demo 命令使用包内公开 fixture:`examples/demo-risky-saas` 当前会故意触发 19 个 finding,覆盖 Stripe、Supabase、silent-success、Next/Vercel deploy 提示和 GitHub Actions;`examples/demo-safe-saas` 在同类风险面上使用更安全的静态写法,当前返回 0 个 finding。想本地查看 fixture 文件时再看 [demo-quickstart.md](demo-quickstart.md)。
|
|
74
77
|
|
|
75
78
|
## 输出长什么样
|
|
76
79
|
|
|
@@ -93,6 +96,10 @@ Verify: force the upstream billing call to fail and confirm the route returns an
|
|
|
93
96
|
MEDIUM deploy.next.missing-security-headers
|
|
94
97
|
File: app/api/billing/checkout/route.ts
|
|
95
98
|
Verify: inspect production response headers for auth, billing, and API pages.
|
|
99
|
+
|
|
100
|
+
Next steps
|
|
101
|
+
- 先修 critical/high 的信任边界 finding。
|
|
102
|
+
- 在 staging 跑 manual proof,确认每个风险路径都会 fail closed。
|
|
96
103
|
```
|
|
97
104
|
|
|
98
105
|
## 你会得到什么
|
|
@@ -124,6 +131,7 @@ Verify: inspect production response headers for auth, billing, and API pages.
|
|
|
124
131
|
无需全局安装,直接运行:
|
|
125
132
|
|
|
126
133
|
```bash
|
|
134
|
+
npx ai-saas-guard@latest demo
|
|
127
135
|
npx ai-saas-guard@latest scan --root /path/to/your-saas
|
|
128
136
|
```
|
|
129
137
|
|
|
@@ -161,18 +169,18 @@ node dist/cli.js scan --root /path/to/your-saas
|
|
|
161
169
|
|
|
162
170
|
这个仓库是公开 GitHub 仓库。
|
|
163
171
|
|
|
164
|
-
CLI 已发布到 npm:`ai-saas-guard@0.
|
|
172
|
+
CLI 已发布到 npm:`ai-saas-guard@0.30.0`。GitHub Action 支持 `v0` 浮动标签,也支持固定版本标签,例如 `v0.30.0`。
|
|
165
173
|
|
|
166
174
|
| 模块 | 状态 |
|
|
167
175
|
| --- | --- |
|
|
168
176
|
| 公开 GitHub 仓库 | 已可用 |
|
|
169
|
-
| npm CLI | `ai-saas-guard@0.
|
|
170
|
-
| GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.
|
|
177
|
+
| npm CLI | `ai-saas-guard@0.30.0` |
|
|
178
|
+
| GitHub Action | `zr9959/ai-saas-guard@v0` 或固定标签 `v0.30.0` |
|
|
171
179
|
| 输出格式 | Terminal、JSON、SARIF 和 PR markdown |
|
|
172
180
|
| 项目配置 | `.ai-saas-guard.json` 支持规则开关、severity 覆盖、suppressions 和 fail threshold |
|
|
173
181
|
| 隐私模型 | 本地优先、只读扫描、不调用 LLM、不上传代码 |
|
|
174
|
-
| 当前版本 | `0.
|
|
175
|
-
| Action 标签 | `v0.
|
|
182
|
+
| 当前版本 | `0.30.0` 增加 `ai-saas-guard demo`、human-readable 报告里的 Next steps、更有针对性的 quickstart 反馈模板,并刷新首次试用文档 |
|
|
183
|
+
| Action 标签 | `v0.30.0`、`v0` |
|
|
176
184
|
| npm 发布 | GitHub Actions Trusted Publisher/OIDC,无需长期 npm token |
|
|
177
185
|
| 仓库可信度加固 | 严格 branch protection、Dependabot、CodeQL、fast-check fuzzing、signed release provenance assets、private vulnerability reporting、secret scanning 和 push protection |
|
|
178
186
|
| Cloudflare hosted ingress | 已部署到 `https://ai-saas-guard-hosted.zr9959.workers.dev`;签名 GitHub App webhook delivery 和 compact Check Run staging smoke 已通过 |
|
package/docs/demo-quickstart.md
CHANGED
|
@@ -2,9 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Use these public fixtures when you want to understand `ai-saas-guard` before pointing it at a private repository.
|
|
4
4
|
|
|
5
|
+
## Fastest Path
|
|
6
|
+
|
|
7
|
+
Run the packaged demo without cloning this repository:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx ai-saas-guard@latest demo
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This prints the risky and safe demo summaries, the first risky files to review, manual verification steps, and launch-focused next steps. It uses only public fixture code shipped in the npm package.
|
|
14
|
+
|
|
5
15
|
## Risky Demo
|
|
6
16
|
|
|
17
|
+
Clone the repository only if you want to inspect or edit the fixture files:
|
|
18
|
+
|
|
7
19
|
```bash
|
|
20
|
+
git clone https://github.com/zr9959/ai-saas-guard.git
|
|
21
|
+
cd ai-saas-guard
|
|
8
22
|
npx ai-saas-guard@latest scan --root examples/demo-risky-saas
|
|
9
23
|
```
|
|
10
24
|
|
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.30.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.
|
|
12
|
+
- GitHub Release: `v0.30.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.
|
|
21
|
+
1. Create and review a release tag such as `v0.30.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.
|
|
@@ -47,6 +47,11 @@ File: app/api/billing/checkout/route.ts:31
|
|
|
47
47
|
Why: Swallowed provider, auth, billing, or data errors can make a launch path look successful when it failed.
|
|
48
48
|
Verify: Force the upstream provider call to fail and confirm the route returns an error or disclosed degraded mode.
|
|
49
49
|
Fix direction: Log the failure, return an explicit error status, and avoid granting access after the failed dependency.
|
|
50
|
+
|
|
51
|
+
Next steps
|
|
52
|
+
- Fix critical and high trust-boundary findings first: auth/session, billing/webhook, tenant data, and silent-success paths.
|
|
53
|
+
- Run the manual proof steps above in staging and confirm each risky path fails closed.
|
|
54
|
+
- Treat low and info deploy/CI hygiene hints as cleanup after critical, high, and medium launch paths are understood.
|
|
50
55
|
```
|
|
51
56
|
|
|
52
57
|
## How To Read It
|
package/package.json
CHANGED