ai-saas-guard 0.1.3 → 0.2.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
@@ -41,7 +41,7 @@ It is intentionally evidence-first. Findings include a rule ID, severity, file e
41
41
 
42
42
  This repository is public on GitHub.
43
43
 
44
- The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is available through versioned release tags. If you need stricter supply-chain pinning in CI, pin the GitHub Action to a reviewed commit SHA instead of a mutable tag.
44
+ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is available through versioned release tags. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag for controlled upgrades, or a reviewed commit SHA for stricter supply-chain pinning.
45
45
 
46
46
  | Area | Status |
47
47
  | --- | --- |
@@ -50,8 +50,8 @@ The CLI is published on npm as `ai-saas-guard`, and the GitHub Action is availab
50
50
  | Local CLI from source | Available for development |
51
51
  | JSON and SARIF output | Available |
52
52
  | Composite GitHub Action | Available |
53
- | Versioned Action tags | `v0.1.3` |
54
- | npm package | `ai-saas-guard@0.1.3` |
53
+ | Versioned Action tags | `v0.2.0`, `v0` |
54
+ | npm package | `ai-saas-guard@0.2.0` |
55
55
  | npm publishing | Trusted Publisher/OIDC, no long-lived publish token |
56
56
 
57
57
  ## Quick Start
@@ -76,6 +76,7 @@ Machine-readable output:
76
76
  ```bash
77
77
  npx ai-saas-guard@latest scan --root /path/to/your-saas --json
78
78
  npx ai-saas-guard@latest scan --root /path/to/your-saas --sarif > ai-saas-guard.sarif
79
+ npx ai-saas-guard@latest pr-risk --root /path/to/your-saas --base origin/main --markdown > ai-saas-guard-pr.md
79
80
  npx ai-saas-guard@latest scan --root /path/to/your-saas --fail-on high
80
81
  ```
81
82
 
@@ -148,9 +149,11 @@ AI-generated PRs often combine unrelated work:
148
149
  - suggested PR split
149
150
  - required tests or manual verification
150
151
  - explicit git-diff diagnostics when a base ref or shallow checkout prevents PR classification
152
+ - PR-focused markdown for GitHub step summaries or PR comments
151
153
 
152
154
  ```bash
153
155
  node dist/cli.js pr-risk --root /path/to/your-saas --base origin/main --json
156
+ node dist/cli.js pr-risk --root /path/to/your-saas --base origin/main --markdown
154
157
  ```
155
158
 
156
159
  If `--base` cannot be resolved, `pr-risk` emits `pr-risk.diff-unavailable` instead of silently reporting a clean or empty diff. In GitHub Actions, use `actions/checkout` with `fetch-depth: 0` when you need merge-base comparison against `origin/main`.
@@ -160,14 +163,14 @@ If `--base` cannot be resolved, `pr-risk` emits `pr-risk.diff-unavailable` inste
160
163
  | Command | Purpose |
161
164
  | --- | --- |
162
165
  | `scan` | Broad local launch preflight across secrets, Stripe, Supabase, MCP, API routes, and deploy config |
163
- | `pr-risk` | Classify the current git diff or a base branch diff for review priority |
166
+ | `pr-risk` | Classify the current git diff or a base branch diff for review priority; supports JSON, SARIF, and PR-focused markdown |
164
167
  | `check-supabase` | Inspect migrations and policy files for RLS and ownership risks |
165
168
  | `check-stripe` | Inspect webhook handlers and billing lifecycle coverage |
166
169
  | `check-mcp` | Inventory MCP configs and classify side effects |
167
170
 
168
171
  ## GitHub Action
169
172
 
170
- The repo includes a composite Action. Use the latest release tag or pin a reviewed commit SHA for stricter supply-chain control:
173
+ The repo includes a composite Action. Use `v0` for the latest compatible pre-1.0 Action, a specific release tag such as `v0.2.0` for controlled upgrades, or pin a reviewed commit SHA for stricter supply-chain control:
171
174
 
172
175
  ```yaml
173
176
  name: ai-saas-guard
@@ -185,7 +188,7 @@ jobs:
185
188
  - uses: actions/checkout@v6.0.2
186
189
  with:
187
190
  fetch-depth: 0
188
- - uses: zr9959/ai-saas-guard@v0.1.3
191
+ - uses: zr9959/ai-saas-guard@v0
189
192
  with:
190
193
  command: pr-risk
191
194
  root: ${{ github.workspace }}
@@ -196,7 +199,7 @@ jobs:
196
199
  For SARIF upload:
197
200
 
198
201
  ```yaml
199
- - uses: zr9959/ai-saas-guard@v0.1.3
202
+ - uses: zr9959/ai-saas-guard@v0
200
203
  with:
201
204
  command: scan
202
205
  format: sarif
@@ -206,7 +209,22 @@ For SARIF upload:
206
209
  sarif_file: ai-saas-guard.sarif
207
210
  ```
208
211
 
209
- For maximum reproducibility, replace `v0.1.3` with the full commit SHA from the release notes.
212
+ For PR-readable markdown in the Actions run:
213
+
214
+ ```yaml
215
+ - uses: zr9959/ai-saas-guard@v0
216
+ with:
217
+ command: pr-risk
218
+ root: ${{ github.workspace }}
219
+ base: origin/main
220
+ format: markdown
221
+ output: ai-saas-guard-pr.md
222
+ - run: cat ai-saas-guard-pr.md >> "$GITHUB_STEP_SUMMARY"
223
+ ```
224
+
225
+ Use markdown for reviewer-facing PR triage and SARIF for GitHub code scanning alerts. See [docs/github-action.md](docs/github-action.md) for copy-paste workflows and trade-offs.
226
+
227
+ For maximum reproducibility, replace `v0` with the full commit SHA from the release notes.
210
228
 
211
229
  ## Ignore File
212
230
 
@@ -274,17 +292,16 @@ Open-source core:
274
292
  - local CLI
275
293
  - deterministic scanner rules
276
294
  - vulnerable and safe fixtures
277
- - JSON and SARIF output
295
+ - JSON, SARIF, and PR-focused markdown output
278
296
  - GitHub Action wrapper
279
297
  - rule documentation
280
298
 
281
299
  Near-term priorities:
282
300
 
283
- - PR comment summary mode
284
301
  - configurable severity and rule toggles
285
302
  - expanded Supabase RLS fixtures
286
303
  - Stripe webhook replay cookbook
287
- - SARIF upload workflow example
304
+ - launch-readiness checklist content
288
305
 
289
306
  Potential paid layer later:
290
307
 
package/action.yml CHANGED
@@ -12,7 +12,7 @@ inputs:
12
12
  required: false
13
13
  default: ${{ github.workspace }}
14
14
  format:
15
- description: "Output format: terminal, json, or sarif."
15
+ description: "Output format: terminal, json, sarif, or markdown."
16
16
  required: false
17
17
  default: terminal
18
18
  fail-on:
@@ -62,7 +62,7 @@ runs:
62
62
  esac
63
63
 
64
64
  case "${INPUT_FORMAT}" in
65
- terminal|json|sarif) ;;
65
+ terminal|json|sarif|markdown) ;;
66
66
  *)
67
67
  echo "Invalid format input: ${INPUT_FORMAT}" >&2
68
68
  exit 2
@@ -83,6 +83,8 @@ runs:
83
83
  args+=("--json")
84
84
  elif [ "${INPUT_FORMAT}" = "sarif" ]; then
85
85
  args+=("--sarif")
86
+ elif [ "${INPUT_FORMAT}" = "markdown" ]; then
87
+ args+=("--markdown")
86
88
  fi
87
89
 
88
90
  if [ "${INPUT_FAIL_ON}" != "none" ]; then
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { resolve } from "node:path";
3
3
  import { checkMcp, checkStripe, checkSupabase, classifyPrRisk, scanRepository } from "./index.js";
4
4
  import { formatJsonReport } from "./report/json.js";
5
+ import { formatMarkdownReport } from "./report/markdown.js";
5
6
  import { formatSarifReport } from "./report/sarif.js";
6
7
  import { formatTerminalReport } from "./report/terminal.js";
7
8
  async function main(argv) {
@@ -57,10 +58,14 @@ function parseArgs(argv) {
57
58
  result.format = "sarif";
58
59
  continue;
59
60
  }
61
+ if (arg === "--markdown") {
62
+ result.format = "markdown";
63
+ continue;
64
+ }
60
65
  if (arg === "--format") {
61
66
  const value = argv[index + 1];
62
- if (value !== "terminal" && value !== "json" && value !== "sarif") {
63
- throw new Error("--format requires terminal, json, or sarif");
67
+ if (value !== "terminal" && value !== "json" && value !== "sarif" && value !== "markdown") {
68
+ throw new Error("--format requires terminal, json, sarif, or markdown");
64
69
  }
65
70
  result.format = value;
66
71
  index += 1;
@@ -108,6 +113,8 @@ function formatReport(report, format) {
108
113
  return formatJsonReport(report);
109
114
  if (format === "sarif")
110
115
  return formatSarifReport(report);
116
+ if (format === "markdown")
117
+ return formatMarkdownReport(report);
111
118
  return `${formatTerminalReport(report)}\n`;
112
119
  }
113
120
  function shouldFail(report, failOn) {
@@ -138,7 +145,7 @@ Usage:
138
145
  ai-saas-guard check-supabase [--root <repo>] [--json|--sarif] [--fail-on <severity>]
139
146
  ai-saas-guard check-stripe [--root <repo>] [--json|--sarif] [--fail-on <severity>]
140
147
  ai-saas-guard check-mcp [--root <repo>] [--json|--sarif] [--fail-on <severity>]
141
- ai-saas-guard pr-risk [--root <repo>] [--base <branch>] [--json|--sarif] [--fail-on <severity>]
148
+ ai-saas-guard pr-risk [--root <repo>] [--base <branch>] [--json|--sarif|--markdown] [--fail-on <severity>]
142
149
 
143
150
  Defaults:
144
151
  - read-only
@@ -146,6 +153,7 @@ Defaults:
146
153
  - no account or login required
147
154
  - terminal output by default, JSON with --json
148
155
  - SARIF output for GitHub code scanning with --sarif
156
+ - PR-focused markdown summary with --markdown
149
157
  `;
150
158
  }
151
159
  main(process.argv.slice(2)).then((code) => {
@@ -0,0 +1,2 @@
1
+ import type { BaseReport } from "../types.js";
2
+ export declare function formatMarkdownReport(report: BaseReport): string;
@@ -0,0 +1,83 @@
1
+ export function formatMarkdownReport(report) {
2
+ if (report.command === "pr-risk")
3
+ return `${formatPrRiskMarkdown(report)}\n`;
4
+ return `${formatGenericMarkdown(report)}\n`;
5
+ }
6
+ function formatPrRiskMarkdown(report) {
7
+ const lines = [];
8
+ lines.push("## ai-saas-guard PR risk summary");
9
+ lines.push("");
10
+ lines.push(summaryLine(report));
11
+ if (report.categories.length > 0) {
12
+ lines.push("");
13
+ lines.push(`**Risk categories:** ${report.categories.map((category) => `\`${category}\``).join(", ")}`);
14
+ }
15
+ lines.push("");
16
+ lines.push("### Review first");
17
+ if (report.topRiskyFiles.length === 0) {
18
+ lines.push("");
19
+ lines.push("No changed trust-boundary files were classified by `pr-risk`.");
20
+ }
21
+ else {
22
+ lines.push("");
23
+ lines.push("| File | Score | Categories | Diff |");
24
+ lines.push("| --- | ---: | --- | ---: |");
25
+ for (const file of report.topRiskyFiles.slice(0, 10)) {
26
+ lines.push(`| \`${escapeMarkdownTableCell(file.path)}\` | ${file.score} | ${file.categories.map((category) => `\`${escapeMarkdownTableCell(category)}\``).join("<br>")} | +${file.added} / -${file.removed} |`);
27
+ }
28
+ }
29
+ lines.push("");
30
+ lines.push("### Required verification");
31
+ appendList(lines, report.requiredTests.length > 0 ? report.requiredTests : report.reviewChecklist);
32
+ lines.push("");
33
+ lines.push("### Suggested PR split");
34
+ appendList(lines, report.suggestedSplit.length > 0 ? report.suggestedSplit : ["No split suggestion from the current diff."]);
35
+ lines.push("");
36
+ lines.push("### Findings");
37
+ appendFindings(lines, report.findings);
38
+ return lines.join("\n");
39
+ }
40
+ function formatGenericMarkdown(report) {
41
+ const lines = [];
42
+ lines.push(`## ai-saas-guard ${report.command}`);
43
+ lines.push("");
44
+ lines.push(summaryLine(report));
45
+ lines.push("");
46
+ lines.push("### Findings");
47
+ appendFindings(lines, report.findings);
48
+ return lines.join("\n");
49
+ }
50
+ function summaryLine(report) {
51
+ return `**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}`;
52
+ }
53
+ function appendList(lines, items) {
54
+ for (const item of items) {
55
+ lines.push(`- ${item}`);
56
+ }
57
+ }
58
+ function appendFindings(lines, findings) {
59
+ if (findings.length === 0) {
60
+ lines.push("");
61
+ lines.push("No heuristic launch-readiness risks found by this command.");
62
+ return;
63
+ }
64
+ for (const [index, finding] of findings.entries()) {
65
+ lines.push("");
66
+ lines.push(`${index + 1}. **[${finding.severity.toUpperCase()}] ${finding.title}**`);
67
+ lines.push(` - Rule: \`${finding.ruleId}\``);
68
+ lines.push(` - Evidence: ${formatEvidence(finding.evidence[0])}`);
69
+ lines.push(` - Why: ${finding.why}`);
70
+ lines.push(` - Verify: ${finding.suggestedVerification}`);
71
+ lines.push(` - Fix direction: ${finding.suggestedFix}`);
72
+ }
73
+ }
74
+ function formatEvidence(evidence) {
75
+ if (!evidence)
76
+ return "`none`";
77
+ const location = evidence.line ? `${evidence.file}:${evidence.line}` : evidence.file;
78
+ const detail = evidence.snippet ?? evidence.match;
79
+ return detail ? `\`${location}\` - ${detail}` : `\`${location}\``;
80
+ }
81
+ function escapeMarkdownTableCell(value) {
82
+ return value.replaceAll("|", "\\|").replaceAll("\n", " ");
83
+ }
@@ -0,0 +1,69 @@
1
+ # GitHub Action Usage
2
+
3
+ `ai-saas-guard` ships as a composite GitHub Action for pull request and code scanning workflows.
4
+
5
+ Use `zr9959/ai-saas-guard@v0` for the latest compatible pre-1.0 Action. Use a specific tag such as `v0.2.0` or a reviewed commit SHA when reproducibility is more important than automatic minor updates.
6
+
7
+ ## PR Summary
8
+
9
+ Use markdown when reviewers need a short, evidence-first summary of risky files, required verification, and suggested PR split.
10
+
11
+ ```yaml
12
+ name: ai-saas-guard-pr-summary
13
+
14
+ on:
15
+ pull_request:
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ jobs:
21
+ pr-summary:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6.0.2
25
+ with:
26
+ fetch-depth: 0
27
+ - uses: zr9959/ai-saas-guard@v0
28
+ with:
29
+ command: pr-risk
30
+ root: ${{ github.workspace }}
31
+ base: origin/main
32
+ format: markdown
33
+ output: ai-saas-guard-pr.md
34
+ - run: cat ai-saas-guard-pr.md >> "$GITHUB_STEP_SUMMARY"
35
+ ```
36
+
37
+ Use markdown for PR review triage. It is intentionally short enough for a GitHub step summary or a PR comment created by your own workflow. It does not require a hosted service.
38
+
39
+ ## SARIF Upload
40
+
41
+ Use SARIF when you want findings to appear in GitHub code scanning alerts.
42
+
43
+ ```yaml
44
+ name: ai-saas-guard-sarif
45
+
46
+ on:
47
+ pull_request:
48
+
49
+ permissions:
50
+ contents: read
51
+ security-events: write
52
+
53
+ jobs:
54
+ code-scanning:
55
+ runs-on: ubuntu-latest
56
+ steps:
57
+ - uses: actions/checkout@v6.0.2
58
+ - uses: zr9959/ai-saas-guard@v0
59
+ with:
60
+ command: scan
61
+ root: ${{ github.workspace }}
62
+ format: sarif
63
+ output: ai-saas-guard.sarif
64
+ - uses: github/codeql-action/upload-sarif@v3
65
+ with:
66
+ sarif_file: ai-saas-guard.sarif
67
+ ```
68
+
69
+ Use SARIF for tracking alerts over time. Use markdown for reviewer guidance on a specific PR. Many teams should run both: markdown for quick review order, SARIF for code scanning visibility.
@@ -5,10 +5,10 @@
5
5
  ## Current State
6
6
 
7
7
  - Package name: `ai-saas-guard`
8
- - Current version: `0.1.3`
8
+ - Current version: `0.2.0`
9
9
  - npm registry state: published at <https://www.npmjs.com/package/ai-saas-guard>
10
10
  - First npm-published version: `0.1.1`
11
- - GitHub Release: `v0.1.3`
11
+ - GitHub Release: `v0.2.0`
12
12
  - Publish workflow: `.github/workflows/npm-publish.yml`
13
13
  - Trusted Publisher: GitHub Actions, `zr9959/ai-saas-guard`, workflow `npm-publish.yml`, allowed action `npm publish`
14
14
  - Long-lived npm publish token: not required
@@ -17,7 +17,7 @@
17
17
 
18
18
  Use GitHub Actions with npm Trusted Publisher/OIDC:
19
19
 
20
- 1. Create and review a release tag such as `v0.1.3`.
20
+ 1. Create and review a release tag such as `v0.2.0`.
21
21
  2. Publish from the GitHub Release or run the `Publish npm` workflow manually with `ref` set to that tag.
22
22
  3. Keep `permissions.id-token: write` in the workflow so npm can exchange the GitHub Actions OIDC identity for a short-lived publish credential.
23
23
  4. Run `npm publish --access public` from the workflow. Trusted publishing automatically generates provenance for this public package from this public repository.
@@ -46,6 +46,7 @@ Implemented surfaces:
46
46
  - Next/Vercel deploy and runtime footguns
47
47
  - PR diff risk triage for auth, billing, RLS, env, tests removed, and large mixed diffs
48
48
  - PR diff diagnostics when a base ref or shallow checkout prevents comparison
49
+ - PR-focused markdown summary output for GitHub step summaries or PR comments
49
50
  - JSON output
50
51
  - SARIF output
51
52
  - composite GitHub Action wrapper
@@ -98,13 +99,9 @@ GitHub Project:
98
99
  Current issue set:
99
100
 
100
101
  - #1 Add launch-readiness checklist content
101
- - #2 Add GitHub Action release packaging
102
102
  - #3 Add configurable rule severity and rule toggles
103
- - #4 Add PR comment summary mode
104
103
  - #5 Write Stripe webhook replay cookbook
105
- - #6 Add SARIF upload workflow example
106
104
  - #7 Expand Supabase RLS fixtures and ownership patterns
107
- - #8 Publish ai-saas-guard to npm
108
105
 
109
106
  CI:
110
107
 
@@ -116,7 +113,7 @@ CI:
116
113
  Publishing:
117
114
 
118
115
  - npm package: `ai-saas-guard`
119
- - Current release line: `v0.1.3`
116
+ - Current release line: `v0.2.0`
120
117
  - Publish workflow: `.github/workflows/npm-publish.yml`
121
118
  - Trusted Publisher: GitHub Actions for `zr9959/ai-saas-guard`, workflow `npm-publish.yml`
122
119
  - Long-lived npm publish tokens should not be required.
@@ -143,13 +140,11 @@ Not allowed:
143
140
 
144
141
  Recommended order:
145
142
 
146
- 1. Add PR comment summary mode.
147
- 2. Add configurable severity and rule toggles.
148
- 3. Expand Supabase RLS fixtures and ownership patterns.
149
- 4. Write Stripe webhook replay cookbook.
150
- 5. Add SARIF upload workflow example.
151
- 6. Improve false-positive suppression and rule stability labels.
152
- 7. Add a GitHub App design note for the potential hosted layer.
143
+ 1. Add configurable severity and rule toggles.
144
+ 2. Expand Supabase RLS fixtures and ownership patterns.
145
+ 3. Write Stripe webhook replay cookbook.
146
+ 4. Improve false-positive suppression and rule stability labels.
147
+ 5. Add a GitHub App design note for the potential hosted layer.
153
148
 
154
149
  For every feature, keep the scanner evidence-first:
155
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-saas-guard",
3
- "version": "0.1.3",
3
+ "version": "0.2.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",