shippingszn 0.8.4 → 0.8.5
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 +30 -58
- package/dist/index.js +62 -98
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,13 +12,14 @@ pnpm dlx shippingszn
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The CLI **never writes, modifies, or deletes** any files - it only reads.
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
By default it posts one anonymous Wall of Launches entry with score, severity
|
|
16
|
+
counts, files scanned, scanner version, and safe stack tags. It never uploads
|
|
17
|
+
source code, paths, filenames, repo URLs, project names, secrets, handles, or
|
|
18
|
+
emails. Use `--no-publish` for a private local run.
|
|
17
19
|
|
|
18
20
|
Human output starts with a Launch Readiness Score, scope covered, the top next
|
|
19
21
|
step to fix, and severity counts. Run with `--json` to get machine-readable
|
|
20
|
-
findings
|
|
21
|
-
fields:
|
|
22
|
+
findings plus the Wall/report handoff fields:
|
|
22
23
|
|
|
23
24
|
```json
|
|
24
25
|
{
|
|
@@ -32,24 +33,15 @@ fields:
|
|
|
32
33
|
"coveragePenalty": 0,
|
|
33
34
|
"coverageSummary": "CLI scan covered 4 readiness areas with 4 areas requiring owner verification.",
|
|
34
35
|
"topNextStep": "Fix Lock up your API keys and passwords: Possible OpenAI API key hardcoded in source.",
|
|
35
|
-
"proofCreatePath": "https://shippingszn.com/scan",
|
|
36
|
-
"proofUploadHint": "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON in the web scan proof flow to create a shareable launch proof result.",
|
|
37
|
-
"proofUrl": "https://shippingszn.com/proof/00000000-0000-4000-8000-000000000123",
|
|
38
|
-
"proofResultId": "00000000-0000-4000-8000-000000000123",
|
|
39
|
-
"badgeMarkdown": "[](https://shippingszn.com/proof/00000000-0000-4000-8000-000000000123)",
|
|
40
36
|
"wallUrl": "https://shippingszn.com/wall",
|
|
41
|
-
"reportUrl": "https://shippingszn.com/report
|
|
37
|
+
"reportUrl": "https://shippingszn.com/report"
|
|
42
38
|
},
|
|
43
39
|
"findings": [
|
|
44
40
|
{
|
|
45
41
|
"severity": "critical",
|
|
46
42
|
"message": "Possible OpenAI API key hardcoded in source.",
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
"verify": "Re-run npx shippingszn --json and confirm this exact finding is gone...",
|
|
50
|
-
"escalation": "Escalate before launch if this touches secrets, auth, payments...",
|
|
51
|
-
"proofNextStep": "After the fix verifies clean, run npx shippingszn --json..."
|
|
52
|
-
}
|
|
43
|
+
"whatFailed": "A secret-like token appears in a scanned file.",
|
|
44
|
+
"whyItBlocksLaunch": "A leaked key can spend money or expose customer data before you notice."
|
|
53
45
|
}
|
|
54
46
|
]
|
|
55
47
|
}
|
|
@@ -59,26 +51,20 @@ When Critical or High findings make paid confidence relevant, the summary also
|
|
|
59
51
|
includes a `/report` link. Clean scans and lower-risk findings do not push the
|
|
60
52
|
paid report CTA.
|
|
61
53
|
|
|
62
|
-
##
|
|
54
|
+
## Wall publishing
|
|
63
55
|
|
|
64
|
-
|
|
56
|
+
Plain `npx shippingszn` feeds the Wall automatically. The public row is
|
|
57
|
+
anonymous and intentionally small: score, launch label, files scanned, finding
|
|
58
|
+
counts, detected stack tags, scanner version, and timestamp.
|
|
59
|
+
|
|
60
|
+
Use this when you want a completely private local run:
|
|
65
61
|
|
|
66
62
|
```bash
|
|
67
|
-
npx shippingszn --
|
|
63
|
+
npx shippingszn --no-publish
|
|
68
64
|
```
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`launchReadiness.proofResultId`, `launchReadiness.badgeMarkdown`,
|
|
73
|
-
`launchReadiness.wallUrl`, and `launchReadiness.reportUrl` to the JSON. It
|
|
74
|
-
also posts a proof-linked severity summary to the Wall of Launches. If proof
|
|
75
|
-
upload fails, findings still print and JSON includes
|
|
76
|
-
`launchReadiness.proofUploadError`; if the Wall publish fails, JSON includes
|
|
77
|
-
`launchReadiness.wallPublishError`.
|
|
78
|
-
|
|
79
|
-
For a manual handoff, you can still run `npx shippingszn --json >
|
|
80
|
-
shippingszn-scan.json` and paste or upload the file at
|
|
81
|
-
`https://shippingszn.com/scan`.
|
|
66
|
+
For a paid-report handoff, run `npx shippingszn --json > shippingszn-scan.json`
|
|
67
|
+
and paste or upload the file at `https://shippingszn.com/scan`.
|
|
82
68
|
|
|
83
69
|
## What gets checked
|
|
84
70
|
|
|
@@ -156,12 +142,8 @@ shippingszn [path] [options]
|
|
|
156
142
|
|
|
157
143
|
Options:
|
|
158
144
|
--json Output a machine-readable JSON report.
|
|
159
|
-
--publish
|
|
160
|
-
|
|
161
|
-
filenames, or project names.
|
|
162
|
-
--proof Opt in to posting this scan to /api/scan-results and
|
|
163
|
-
printing a proof URL, Wall URL, report URL, and badge.
|
|
164
|
-
Can also be enabled with SHIPPINGSZN_PROOF=1.
|
|
145
|
+
--publish Legacy alias. Anonymous Wall stats publish by default.
|
|
146
|
+
--no-publish Disable the anonymous Wall post for this run.
|
|
165
147
|
--base-url <url> Base URL used to build links back to checklist items.
|
|
166
148
|
--cwd <path> Directory to scan. Default: current working directory.
|
|
167
149
|
--no-color Disable ANSI colors in the human-readable report.
|
|
@@ -179,32 +161,22 @@ This makes the CLI suitable for CI:
|
|
|
179
161
|
|
|
180
162
|
```yaml
|
|
181
163
|
# .github/workflows/launch-check.yml
|
|
182
|
-
- run: npx shippingszn --json
|
|
164
|
+
- run: npx shippingszn --json > launch-check.json
|
|
183
165
|
```
|
|
184
166
|
|
|
185
|
-
For PR scan
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
`--publish` out of the workflow unless you explicitly want to publish an
|
|
189
|
-
anonymous Wall summary when proof upload fails.
|
|
167
|
+
For PR scan signal, have GitHub Actions run the scanner and post the JSON
|
|
168
|
+
summary as a comment: score, severity counts, top next step, Wall URL, report
|
|
169
|
+
URL, and worst Critical/High findings.
|
|
190
170
|
|
|
191
171
|
## Privacy
|
|
192
172
|
|
|
193
|
-
`shippingszn` reads files on your machine. It never uploads source code
|
|
194
|
-
makes
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
messages, remediation prompts, verification text, checklist permalinks,
|
|
201
|
-
severity counts, score, file count, target folder name, and scanner version. It
|
|
202
|
-
does not upload source code or secret values.
|
|
203
|
-
|
|
204
|
-
If you only want an anonymous Wall of Launches summary, pass `--publish`.
|
|
205
|
-
That opt-in posts files scanned count, finding counts by severity, detected
|
|
206
|
-
stack tags, and scanner version. It never uploads source code, file paths,
|
|
207
|
-
filenames, project names, secrets, or report contents.
|
|
173
|
+
`shippingszn` reads files on your machine. It never uploads source code. By
|
|
174
|
+
default it makes one best-effort outbound request to post anonymous Wall stats:
|
|
175
|
+
score, launch label, files scanned count, finding counts by severity, detected
|
|
176
|
+
stack tags, scanner version, and timestamp. It never uploads source code, file
|
|
177
|
+
paths, filenames, project names, repo URLs, secrets, emails, handles, or report
|
|
178
|
+
contents. Use `--no-publish` or `SHIPPINGSZN_DISABLE_PUBLISH=1` to disable that
|
|
179
|
+
request.
|
|
208
180
|
|
|
209
181
|
## License
|
|
210
182
|
|
package/dist/index.js
CHANGED
|
@@ -1506,10 +1506,6 @@ import { promises as fs2 } from "node:fs";
|
|
|
1506
1506
|
import * as path10 from "node:path";
|
|
1507
1507
|
var DEFAULT_BASE_URL = "https://shippingszn.com";
|
|
1508
1508
|
var PUBLISH_TIMEOUT_MS = 3e3;
|
|
1509
|
-
function shouldPublish() {
|
|
1510
|
-
const v = process.env["SHIPPINGSZN_PUBLISH"] ?? "";
|
|
1511
|
-
return v === "1" || v.toLowerCase() === "true" || v === "yes";
|
|
1512
|
-
}
|
|
1513
1509
|
async function detectStack(cwd2) {
|
|
1514
1510
|
const tags = /* @__PURE__ */ new Set();
|
|
1515
1511
|
try {
|
|
@@ -1555,8 +1551,11 @@ async function detectStack(cwd2) {
|
|
|
1555
1551
|
}
|
|
1556
1552
|
return [...tags].slice(0, 12);
|
|
1557
1553
|
}
|
|
1558
|
-
function buildPayload(totals, filesScanned, stack, scannerVersion) {
|
|
1554
|
+
function buildPayload(totals, filesScanned, stack, scannerVersion, score, label) {
|
|
1559
1555
|
const out = {
|
|
1556
|
+
source: "cli",
|
|
1557
|
+
score,
|
|
1558
|
+
label,
|
|
1560
1559
|
filesScanned,
|
|
1561
1560
|
findingsCritical: totals.critical ?? 0,
|
|
1562
1561
|
findingsHigh: totals.high ?? 0,
|
|
@@ -1568,14 +1567,15 @@ function buildPayload(totals, filesScanned, stack, scannerVersion) {
|
|
|
1568
1567
|
return out;
|
|
1569
1568
|
}
|
|
1570
1569
|
async function publishScan(totals, filesScanned, opts) {
|
|
1571
|
-
if (!shouldPublish()) return "skipped";
|
|
1572
1570
|
const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;
|
|
1573
1571
|
const stack = await detectStack(opts.cwd);
|
|
1574
1572
|
const payload = buildPayload(
|
|
1575
1573
|
totals,
|
|
1576
1574
|
filesScanned,
|
|
1577
1575
|
stack,
|
|
1578
|
-
opts.scannerVersion
|
|
1576
|
+
opts.scannerVersion,
|
|
1577
|
+
opts.score,
|
|
1578
|
+
opts.label
|
|
1579
1579
|
);
|
|
1580
1580
|
const controller = new AbortController();
|
|
1581
1581
|
const timer = setTimeout(() => controller.abort(), PUBLISH_TIMEOUT_MS);
|
|
@@ -1644,13 +1644,8 @@ function buildProofPayload(report, scannerVersion) {
|
|
|
1644
1644
|
body: clampText(finding2.message, 3e3),
|
|
1645
1645
|
whatFailed: clampText(finding2.whatFailed, 3e3),
|
|
1646
1646
|
whyItBlocksLaunch: clampText(finding2.whyItBlocksLaunch, 3e3),
|
|
1647
|
-
fixInstructions: clampText(finding2.fixInstructions, 4e3),
|
|
1648
|
-
aiBuilderPrompt: clampText(finding2.aiBuilderPrompt, 4e3),
|
|
1649
1647
|
evidence: clampText(finding2.evidence, 3e3),
|
|
1650
1648
|
confidence: finding2.confidence,
|
|
1651
|
-
fixPrompt: clampText(finding2.aiBuilderPrompt, 4e3),
|
|
1652
|
-
verify: clampText(finding2.verificationStep, 3e3),
|
|
1653
|
-
verificationStep: clampText(finding2.verificationStep, 3e3),
|
|
1654
1649
|
...locationFromFinding(finding2) ? { location: clampText(locationFromFinding(finding2), 500) } : {},
|
|
1655
1650
|
permalink: finding2.permalink,
|
|
1656
1651
|
itemTitle: clampText(finding2.itemTitle, 160)
|
|
@@ -1761,52 +1756,6 @@ async function uploadProof(report, opts) {
|
|
|
1761
1756
|
}
|
|
1762
1757
|
}
|
|
1763
1758
|
|
|
1764
|
-
// src/remediation.ts
|
|
1765
|
-
var SEVERITY_INTENT = {
|
|
1766
|
-
critical: "Treat this as launch-blocking until fixed.",
|
|
1767
|
-
high: "Fix this before public traffic or paid acquisition.",
|
|
1768
|
-
medium: "Fix this before announcing or indexing the launch.",
|
|
1769
|
-
lower: "Fix this when polishing the launch artifact."
|
|
1770
|
-
};
|
|
1771
|
-
var ESCALATION = {
|
|
1772
|
-
critical: "Escalate before launch if this touches secrets, auth, payments, customer data, production infrastructure, or anything already exposed publicly.",
|
|
1773
|
-
high: "Escalate if the fix affects auth, payments, deploy config, browser security, or a user-visible production path.",
|
|
1774
|
-
medium: "Escalate only if the fix changes routing, indexing, analytics, or public launch messaging.",
|
|
1775
|
-
lower: "Escalation is usually unnecessary unless this blocks installability, branding, or a promised launch surface."
|
|
1776
|
-
};
|
|
1777
|
-
function locationForPrompt(finding2) {
|
|
1778
|
-
if (!finding2.file) return "No specific file was attached to the finding.";
|
|
1779
|
-
const line = finding2.line ? `:${finding2.line}` : "";
|
|
1780
|
-
return `Likely location: ${finding2.file}${line}.`;
|
|
1781
|
-
}
|
|
1782
|
-
function proofStepForSeverity(severity, proofCreatePath, reportUrl) {
|
|
1783
|
-
const proofStep = `After the fix verifies clean, run npx shippingszn --json and paste or upload the JSON at ${proofCreatePath} to create launch proof.`;
|
|
1784
|
-
if (severity === "critical" || severity === "high") {
|
|
1785
|
-
return `${proofStep} If this app has users, revenue, client trust, or paid API exposure, attach the clean scan to a paid launch-readiness report at ${reportUrl}.`;
|
|
1786
|
-
}
|
|
1787
|
-
return proofStep;
|
|
1788
|
-
}
|
|
1789
|
-
function buildRemediationPrompt({
|
|
1790
|
-
finding: finding2,
|
|
1791
|
-
item,
|
|
1792
|
-
proofCreatePath,
|
|
1793
|
-
reportUrl
|
|
1794
|
-
}) {
|
|
1795
|
-
const itemTitle = item?.title ?? finding2.itemId;
|
|
1796
|
-
const location = locationForPrompt(finding2);
|
|
1797
|
-
const intent = SEVERITY_INTENT[finding2.severity];
|
|
1798
|
-
return {
|
|
1799
|
-
fixPrompt: `You are fixing a shippingszn launch-readiness finding. Severity: ${finding2.severity}. Checklist area: ${itemTitle}. Finding: ${finding2.message} ${location} ${intent} Make the smallest production-safe code or config change that removes the underlying risk, preserve existing behavior, and list the files changed. Do not suppress the scanner unless you can prove the finding is a false positive.`,
|
|
1800
|
-
verify: "Re-run npx shippingszn --json and confirm this exact finding is gone. Also run the app's normal typecheck/test/build command when the fix changes code, config, routing, security behavior, or public assets.",
|
|
1801
|
-
escalation: ESCALATION[finding2.severity],
|
|
1802
|
-
proofNextStep: proofStepForSeverity(
|
|
1803
|
-
finding2.severity,
|
|
1804
|
-
proofCreatePath,
|
|
1805
|
-
reportUrl
|
|
1806
|
-
)
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
1759
|
// ../../lib/launch-readiness/src/index.ts
|
|
1811
1760
|
var SEVERITY_WEIGHTS = {
|
|
1812
1761
|
critical: 35,
|
|
@@ -1843,7 +1792,7 @@ var SCORE_BANDS = [
|
|
|
1843
1792
|
id: "launchable",
|
|
1844
1793
|
minScore: 90,
|
|
1845
1794
|
maxScore: 100,
|
|
1846
|
-
label: "Launchable
|
|
1795
|
+
label: "Launchable",
|
|
1847
1796
|
decision: "go",
|
|
1848
1797
|
goNoGoLabel: "Go"
|
|
1849
1798
|
}
|
|
@@ -2224,12 +2173,12 @@ function decisionText(band, counts, source) {
|
|
|
2224
2173
|
return `Fix the high-risk launch gaps before a real campaign, customer demo, or paid traffic.`;
|
|
2225
2174
|
}
|
|
2226
2175
|
if (counts.medium > 0 || counts.lower > 0) {
|
|
2227
|
-
return `No critical or high blocker was detected, but the remaining readiness gaps should be fixed and verified before treating the ${sourceLabel(source)} as
|
|
2176
|
+
return `No critical or high blocker was detected, but the remaining readiness gaps should be fixed and verified before treating the ${sourceLabel(source)} as launchable.`;
|
|
2228
2177
|
}
|
|
2229
2178
|
if (source === "url") {
|
|
2230
2179
|
return "No public-page blockers were found. Run the CLI or GitHub Action before calling the app launch-ready.";
|
|
2231
2180
|
}
|
|
2232
|
-
return `${band.goNoGoLabel}: no scan-detected blockers were found.
|
|
2181
|
+
return `${band.goNoGoLabel}: no scan-detected blockers were found. Monitor first production traffic.`;
|
|
2233
2182
|
}
|
|
2234
2183
|
function topNextStep(blockers, counts, source) {
|
|
2235
2184
|
const top = blockers[0];
|
|
@@ -2240,9 +2189,9 @@ function topNextStep(blockers, counts, source) {
|
|
|
2240
2189
|
return "Run the repo scan next so secrets, auth, API routes, and generated-code risks are checked before launch.";
|
|
2241
2190
|
}
|
|
2242
2191
|
if (counts.medium > 0 || counts.lower > 0) {
|
|
2243
|
-
return "Fix the remaining readiness gaps and rescan before
|
|
2192
|
+
return "Fix the remaining readiness gaps and rescan before launch.";
|
|
2244
2193
|
}
|
|
2245
|
-
return "
|
|
2194
|
+
return "No obvious launch blockers caught. Monitor first production traffic and keep the paid report for higher-stakes launches.";
|
|
2246
2195
|
}
|
|
2247
2196
|
function aggregateConfidence(source, findings) {
|
|
2248
2197
|
if (findings.some((finding2) => finding2.confidence === "low")) return "low";
|
|
@@ -2327,7 +2276,7 @@ function parseArgs(argv2) {
|
|
|
2327
2276
|
help: false,
|
|
2328
2277
|
version: false,
|
|
2329
2278
|
noColor: !!process2.env.NO_COLOR,
|
|
2330
|
-
publish:
|
|
2279
|
+
publish: !publishDisabledByEnv(),
|
|
2331
2280
|
proof: shouldUploadProof()
|
|
2332
2281
|
};
|
|
2333
2282
|
for (let i = 0; i < argv2.length; i++) {
|
|
@@ -2336,6 +2285,7 @@ function parseArgs(argv2) {
|
|
|
2336
2285
|
else if (a === "--version" || a === "-v") opts.version = true;
|
|
2337
2286
|
else if (a === "--json") opts.json = true;
|
|
2338
2287
|
else if (a === "--publish") opts.publish = true;
|
|
2288
|
+
else if (a === "--no-publish") opts.publish = false;
|
|
2339
2289
|
else if (a === "--proof") opts.proof = true;
|
|
2340
2290
|
else if (a === "--no-color") opts.noColor = true;
|
|
2341
2291
|
else if (a === "--base-url") opts.baseUrl = argv2[++i] ?? opts.baseUrl;
|
|
@@ -2344,6 +2294,21 @@ function parseArgs(argv2) {
|
|
|
2344
2294
|
}
|
|
2345
2295
|
return opts;
|
|
2346
2296
|
}
|
|
2297
|
+
function envTruthy(value) {
|
|
2298
|
+
if (!value) return false;
|
|
2299
|
+
const normalized = value.trim().toLowerCase();
|
|
2300
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
2301
|
+
}
|
|
2302
|
+
function envFalsey(value) {
|
|
2303
|
+
if (!value) return false;
|
|
2304
|
+
const normalized = value.trim().toLowerCase();
|
|
2305
|
+
return normalized === "0" || normalized === "false" || normalized === "no";
|
|
2306
|
+
}
|
|
2307
|
+
function publishDisabledByEnv() {
|
|
2308
|
+
if (envTruthy(process2.env.SHIPPINGSZN_DISABLE_PUBLISH)) return true;
|
|
2309
|
+
if (envFalsey(process2.env.SHIPPINGSZN_PUBLISH)) return true;
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2347
2312
|
function color(enabled) {
|
|
2348
2313
|
const wrap = (codes) => (s) => enabled ? `\x1B[${codes}m${s}\x1B[0m` : s;
|
|
2349
2314
|
return {
|
|
@@ -2377,12 +2342,8 @@ Usage:
|
|
|
2377
2342
|
|
|
2378
2343
|
Options:
|
|
2379
2344
|
--json Output a machine-readable JSON report.
|
|
2380
|
-
--publish
|
|
2381
|
-
|
|
2382
|
-
filenames, or project names.
|
|
2383
|
-
--proof Opt in to posting this scan to /api/scan-results and
|
|
2384
|
-
printing a proof URL, Wall URL, report URL, and badge.
|
|
2385
|
-
Can also be enabled with SHIPPINGSZN_PROOF=1.
|
|
2345
|
+
--publish Legacy alias. Anonymous Wall stats publish by default.
|
|
2346
|
+
--no-publish Disable the anonymous Wall post for this run.
|
|
2386
2347
|
--base-url <url> Base URL used to build links back to checklist items.
|
|
2387
2348
|
(default: ${DEFAULT_BASE_URL2})
|
|
2388
2349
|
--cwd <path> Directory to scan. Default: current working directory.
|
|
@@ -2390,8 +2351,9 @@ Options:
|
|
|
2390
2351
|
-h, --help Show this help.
|
|
2391
2352
|
-v, --version Print version.
|
|
2392
2353
|
|
|
2393
|
-
The scanner only reads files. It never writes, modifies, deletes
|
|
2394
|
-
|
|
2354
|
+
The scanner only reads files. It never writes, modifies, or deletes. It posts
|
|
2355
|
+
anonymous aggregate Wall stats by default: no source code, paths, filenames,
|
|
2356
|
+
project names, secrets, handles, emails, or repo URLs.
|
|
2395
2357
|
Exit code is non-zero if any Critical findings are detected.
|
|
2396
2358
|
`
|
|
2397
2359
|
);
|
|
@@ -2432,18 +2394,13 @@ async function run() {
|
|
|
2432
2394
|
const trimmedBaseUrl = opts.baseUrl.replace(/\/$/, "");
|
|
2433
2395
|
const reportUrl = `${trimmedBaseUrl}/report`;
|
|
2434
2396
|
const proofCreatePath = `${trimmedBaseUrl}/scan`;
|
|
2435
|
-
const proofUploadHint = "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON
|
|
2397
|
+
const proofUploadHint = "Run `npx shippingszn --json > shippingszn-scan.json`, then paste or upload that JSON on shippingszn.com/scan if you want to attach the local scan to a paid report handoff.";
|
|
2436
2398
|
const tracked_aware = applyTrackingAwareSeverity(all, tracked);
|
|
2437
2399
|
const source = scanSource();
|
|
2438
2400
|
const enriched = tracked_aware.map((f) => {
|
|
2439
2401
|
const item = CHECKLIST_ITEMS[f.itemId];
|
|
2440
|
-
const remediation = buildRemediationPrompt({
|
|
2441
|
-
finding: f,
|
|
2442
|
-
item,
|
|
2443
|
-
proofCreatePath,
|
|
2444
|
-
reportUrl
|
|
2445
|
-
});
|
|
2446
2402
|
const itemTitle = item?.title ?? f.itemId;
|
|
2403
|
+
const permalink = permalinkFor(f.itemId, opts.baseUrl);
|
|
2447
2404
|
const normalized = normalizeLaunchFinding(
|
|
2448
2405
|
{
|
|
2449
2406
|
severity: f.severity,
|
|
@@ -2453,22 +2410,26 @@ async function run() {
|
|
|
2453
2410
|
evidence: f.evidence,
|
|
2454
2411
|
file: f.file,
|
|
2455
2412
|
line: f.line,
|
|
2456
|
-
permalink
|
|
2457
|
-
itemTitle
|
|
2458
|
-
fixInstructions: remediation.fixPrompt,
|
|
2459
|
-
aiBuilderPrompt: remediation.fixPrompt,
|
|
2460
|
-
verificationStep: remediation.verify,
|
|
2461
|
-
fixPrompt: remediation.fixPrompt,
|
|
2462
|
-
verify: remediation.verify
|
|
2413
|
+
permalink,
|
|
2414
|
+
itemTitle
|
|
2463
2415
|
},
|
|
2464
2416
|
source
|
|
2465
2417
|
);
|
|
2466
2418
|
return {
|
|
2467
2419
|
...f,
|
|
2468
|
-
|
|
2420
|
+
severity: normalized.severity,
|
|
2421
|
+
title: normalized.title,
|
|
2422
|
+
body: normalized.body,
|
|
2423
|
+
message: normalized.message,
|
|
2424
|
+
whatFailed: normalized.whatFailed,
|
|
2425
|
+
whyItBlocksLaunch: normalized.whyItBlocksLaunch,
|
|
2426
|
+
evidence: normalized.evidence,
|
|
2427
|
+
confidence: normalized.confidence,
|
|
2428
|
+
...normalized.location ? { location: normalized.location } : {},
|
|
2429
|
+
...normalized.file ? { file: normalized.file } : {},
|
|
2430
|
+
...normalized.line ? { line: normalized.line } : {},
|
|
2469
2431
|
itemTitle,
|
|
2470
|
-
permalink
|
|
2471
|
-
remediation
|
|
2432
|
+
permalink
|
|
2472
2433
|
};
|
|
2473
2434
|
});
|
|
2474
2435
|
enriched.sort((a, b) => {
|
|
@@ -2535,16 +2496,18 @@ async function run() {
|
|
|
2535
2496
|
}
|
|
2536
2497
|
let publishResult = "skipped";
|
|
2537
2498
|
try {
|
|
2538
|
-
if (opts.publish) {
|
|
2539
|
-
process2.env["SHIPPINGSZN_PUBLISH"] = "1";
|
|
2540
|
-
}
|
|
2541
2499
|
const proofAlreadyPublishedWall = proofResult.status === "uploaded" && !!proofResult.wallUrl;
|
|
2542
|
-
if (!proofAlreadyPublishedWall) {
|
|
2500
|
+
if (opts.publish && !proofAlreadyPublishedWall) {
|
|
2543
2501
|
publishResult = await publishScan(totals, files.length, {
|
|
2544
2502
|
cwd: opts.cwd,
|
|
2545
2503
|
baseUrl: opts.baseUrl,
|
|
2546
|
-
scannerVersion: PKG_VERSION
|
|
2504
|
+
scannerVersion: PKG_VERSION,
|
|
2505
|
+
score: launchReadiness.score,
|
|
2506
|
+
label: launchReadiness.label
|
|
2547
2507
|
});
|
|
2508
|
+
if (publishResult === "published") {
|
|
2509
|
+
report.launchReadiness.wallUrl = `${opts.baseUrl.replace(/\/$/, "")}/wall`;
|
|
2510
|
+
}
|
|
2548
2511
|
}
|
|
2549
2512
|
} catch {
|
|
2550
2513
|
}
|
|
@@ -2582,12 +2545,12 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
|
|
|
2582
2545
|
`
|
|
2583
2546
|
);
|
|
2584
2547
|
process2.stdout.write(
|
|
2585
|
-
`${c.bold("
|
|
2548
|
+
`${c.bold("Action plan:")} ${c.dim("Exact AI-builder prompts and verification steps are in the paid report.")}
|
|
2586
2549
|
`
|
|
2587
2550
|
);
|
|
2588
2551
|
if (launchReadiness.reportUrl) {
|
|
2589
2552
|
process2.stdout.write(
|
|
2590
|
-
`${c.bold("Need
|
|
2553
|
+
`${c.bold("Need a written launch decision?")} ${c.dim(`Get the $49 report: ${launchReadiness.reportUrl}`)}
|
|
2591
2554
|
`
|
|
2592
2555
|
);
|
|
2593
2556
|
}
|
|
@@ -2622,8 +2585,9 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
|
|
|
2622
2585
|
`
|
|
2623
2586
|
);
|
|
2624
2587
|
} else {
|
|
2588
|
+
const wallLine = publishResult === "published" ? `Posted anonymous scan stats to ${opts.baseUrl.replace(/\/$/, "")}/wall.` : opts.publish ? "Anonymous Wall stats post by default. If the network is blocked, the local scan still finishes." : "Private local run: Wall publish disabled.";
|
|
2625
2589
|
process2.stdout.write(
|
|
2626
|
-
`${c.bold("
|
|
2590
|
+
`${c.bold("Wall signal:")} ${c.dim(wallLine)}
|
|
2627
2591
|
`
|
|
2628
2592
|
);
|
|
2629
2593
|
}
|
|
@@ -2631,7 +2595,7 @@ ${c.bold("shippingszn")} ${c.dim(`v${PKG_VERSION}`)}
|
|
|
2631
2595
|
if (enriched.length === 0) {
|
|
2632
2596
|
process2.stdout.write(
|
|
2633
2597
|
c.green(
|
|
2634
|
-
"No findings.
|
|
2598
|
+
"No findings. The scanner did not catch obvious launch blockers in this pass.\n\n"
|
|
2635
2599
|
)
|
|
2636
2600
|
);
|
|
2637
2601
|
if (publishResult === "published") {
|
package/package.json
CHANGED