From f0769c1b5908c8b9d5acee3aa2d70f5fb6a25105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Pereiro?= Date: Wed, 21 Oct 2026 21:55:06 +0200 Subject: [PATCH 1/2] Add launch-check skill --- docs/README.skills.md | 1 + skills/launch-check/SKILL.md | 83 + .../launch-check/references/how-it-works.md | 106 + skills/launch-check/scripts/geo_audit.py | 2241 +++++++++++++++++ skills/launch-check/scripts/launch_check.py | 1290 ++++++++++ skills/launch-check/scripts/polish_audit.py | 1192 +++++++++ 6 files changed, 4913 insertions(+) create mode 100644 skills/launch-check/SKILL.md create mode 100644 skills/launch-check/references/how-it-works.md create mode 100644 skills/launch-check/scripts/geo_audit.py create mode 100644 skills/launch-check/scripts/launch_check.py create mode 100644 skills/launch-check/scripts/polish_audit.py diff --git a/docs/README.skills.md b/docs/README.skills.md index 055d5793fd..a9ccff0c11 100644 --- a/docs/README.skills.md +++ b/docs/README.skills.md @@ -261,6 +261,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to | [kotlin-springboot](../skills/kotlin-springboot/SKILL.md)
`gh skills install github/awesome-copilot kotlin-springboot` | Get best practices for developing applications with Spring Boot and Kotlin. | None | | [landing-page-conversion-audit](../skills/landing-page-conversion-audit/SKILL.md)
`gh skills install github/awesome-copilot landing-page-conversion-audit` | Audit a landing page, sales page or checkout page for conversion leaks and return a fix list ordered by expected revenue impact. Use when asked to review, critique or improve a landing page, sales page, opt-in page, product page or checkout flow, when conversion rate is low, when paid traffic is not converting, or when someone asks "why isn't this page converting" or wants a CRO / landing page review. | None | | [latchshot-page-capture](../skills/latchshot-page-capture/SKILL.md)
`gh skills install github/awesome-copilot latchshot-page-capture` | Use this skill when a user needs a screenshot, website thumbnail, full-page capture, or PDF of a public HTTP(S) webpage saved as a local artifact through Latchshot, including report, QA, archive, and social-preview workflows. Do not use it for private or authenticated pages, raw HTML, scraping or extraction, arbitrary browser actions, CAPTCHA or anti-bot bypass, or local-file capture. | `scripts/latchshot.mjs` | +| [launch-check](../skills/launch-check/SKILL.md)
`gh skills install github/awesome-copilot launch-check` | Pre-launch report card for apps and sites built with AI (Lovable, Bolt, Replit, Cursor, v0, Claude). One command checks a live URL passively and scores 4 areas 0-100 with traffic lights - security (secret keys visible in the page JavaScript, Supabase service_role keys, open Supabase or Firebase tables with --owner-check, missing security headers, HTTPS, cookies), phone design at 375 px, being found by Google and AI search, and speed and basics - then writes a plain-English "fix this first" list to paste to your AI and a one-page HTML report. Use when someone asks "is my app safe to launch", "did I leak my API key", "check my vibe-coded app", "is my Supabase open", "pre-launch checklist", or "is my site ready". | `references/how-it-works.md`
`scripts/geo_audit.py`
`scripts/launch_check.py`
`scripts/polish_audit.py` | | [lean-comments](../skills/lean-comments/SKILL.md)
`gh skills install github/awesome-copilot lean-comments` | Audits, writes, and refines maintained first-party source-code comments and declaration-level documentation across languages. Use when adding, editing, reviewing, cleaning up, or auditing comments, doc comments, docstrings, TODO, FIXME, NOTE, suppressions, or other source commentary, including while modifying code and deciding whether a comment is warranted at all. Defaults to no comment unless it preserves meaningful, non-obvious information that cannot reasonably be recovered from the code or nearby repository context. | None | | [legacy-circuit-mockups](../skills/legacy-circuit-mockups/SKILL.md)
`gh skills install github/awesome-copilot legacy-circuit-mockups` | Generate breadboard circuit mockups and visual diagrams using HTML5 Canvas drawing techniques. Use when asked to create circuit layouts, visualize electronic component placements, draw breadboard diagrams, mockup 6502 builds, generate retro computer schematics, or design vintage electronics projects. Supports 555 timers, W65C02S microprocessors, 28C256 EEPROMs, W65C22 VIA chips, 7400-series logic gates, LEDs, resistors, capacitors, switches, buttons, crystals, and wires. | `references/28256-eeprom.md`
`references/555.md`
`references/6502.md`
`references/6522.md`
`references/6C62256.md`
`references/7400-series.md`
`references/assembly-compiler.md`
`references/assembly-language.md`
`references/basic-electronic-components.md`
`references/breadboard.md`
`references/common-breadboard-components.md`
`references/connecting-electronic-components.md`
`references/emulator-28256-eeprom.md`
`references/emulator-6502.md`
`references/emulator-6522.md`
`references/emulator-6C62256.md`
`references/emulator-lcd.md`
`references/lcd.md`
`references/minipro.md`
`references/t48eeprom-programmer.md` | | [linkedin-post-formatter](../skills/linkedin-post-formatter/SKILL.md)
`gh skills install github/awesome-copilot linkedin-post-formatter` | Format and draft compelling LinkedIn posts using Unicode bold/italic styling, visual separators, structured sections, and engagement-optimized patterns. USE FOR: draft LinkedIn post, format text for LinkedIn, create social media post, write thought leadership post, convert content to LinkedIn format, LinkedIn carousel text, Unicode bold italic formatting. | `references/unicode-charmap.md` | diff --git a/skills/launch-check/SKILL.md b/skills/launch-check/SKILL.md new file mode 100644 index 0000000000..046cc6e818 --- /dev/null +++ b/skills/launch-check/SKILL.md @@ -0,0 +1,83 @@ +--- +name: 'launch-check' +description: 'Pre-launch report card for apps and sites built with AI (Lovable, Bolt, Replit, Cursor, v0, Claude). One command checks a live URL passively and scores 4 areas 0-100 with traffic lights - security (secret keys visible in the page JavaScript, Supabase service_role keys, open Supabase or Firebase tables with --owner-check, missing security headers, HTTPS, cookies), phone design at 375 px, being found by Google and AI search, and speed and basics - then writes a plain-English "fix this first" list to paste to your AI and a one-page HTML report. Use when someone asks "is my app safe to launch", "did I leak my API key", "check my vibe-coded app", "is my Supabase open", "pre-launch checklist", or "is my site ready".' +--- + +# Launch Check + +`scripts/launch_check.py` is a deterministic checker (Python standard library; Playwright optional). It loads the +site the way a visitor's browser does, scans what it downloaded, runs two vendored audits, and prints one +report card. Do not judge the site by reading it yourself: run the tool, then explain and fix what it found. + +## Run it + +1. Find Python 3.9+: `python3 --version`, else `python --version`, else `py -3 --version` (Windows). Call it ``. +2. `` = the folder that holds this SKILL.md. +3. ` "/scripts/launch_check.py" https://the-app.com --report launch-report.html` + - `--owner-check`: ONLY when the user confirms they own the site. If the page contains a Supabase URL + public + anon key (or a Firebase databaseURL), it asks the API which tables exist and reads at most 1 row per table + (GET only, max 20 tables, never writes, row contents are never printed or saved; only "table X: readable by + anyone (N columns)"). Without the flag it only says that a database was found. + - `--repo .`: also scan the local project for committed `.env` files and secrets in tracked files. + - `--prompt`: print only the fix list as a prompt for the AI that built the app. `--json`: everything. + - `--skip phone,search,speed`: faster partial run. `--fail-under 80`: exit 1 below this score. +4. Phone design and page-load metrics need Playwright (` -m pip install playwright && -m playwright + install chromium`). Without it, phone design is skipped with a note; the rest still runs. + +Typical time: 6-8 s on a small site, 30-60 s on a large marketing site (the search check reads 5 pages politely). + +## Read the output + +``` +SCORE 30/100 NOT READY: 5 things to fix before you launch +FAIL Security 0 Anyone can see your Stripe secret key in your website's code (+7 more) +WARN Phone design 77 The page is wider than a phone screen (+4 more) +FIX THIS FIRST + 1. HIGH Anyone can see your Stripe secret key in your website's code + in /assets/index-4f9c2a.js value sk_l…(50 chars) +``` + +- PASS / WARN / FAIL = green (80+) / amber (50-79) / red (under 50) per area. SKIP = the area could not run (reason in a `note:` line). +- Overall score = weighted mean (security 40 %, the other three 20 % each). Any exposed secret or open table caps it at 39. +- Verdict: NOT READY = a HIGH security finding (or score under 50); ALMOST READY = other HIGH findings or score under 80; READY TO LAUNCH otherwise. +- `value sk_l…(50 chars)`: secrets are always cut to their first 4 characters. The full value is never printed or sent anywhere. +- A Supabase anon / publishable key or a Google (Firebase) key in the page is normal and only INFO/LOW: safety depends on Row Level Security and key restrictions, which `--owner-check` tests for Supabase and Firebase. +- Exit code 0 = ready (score >= `--fail-under`, nothing HIGH), 1 = findings, 2 = bad URL or the site could not be loaded (HTTP 403 usually means it blocks automated visitors: run it on a preview URL). + +## What to do next + +1. Exposed secret key (Stripe, OpenAI, Anthropic, AWS, Supabase service_role ...): it is already public. Move the + call server-side (API route, edge function), put the key in a server environment variable, and ROTATE the key + in the provider dashboard. Deleting it from the code is not enough. +2. Open table (`db-table-open`) or open Firebase: turn on Row Level Security / database rules with per-user + policies, then re-run with `--owner-check` until the table is gone from the list. +3. Paste the "Paste to your AI" block from the HTML report (or `--prompt`) into the AI that built the app, apply, + redeploy, re-run. Repeat until the verdict is READY TO LAUNCH. +4. Headers (CSP, HSTS, X-Frame-Options, nosniff): set them in the host config (`vercel.json`, `netlify.toml`, + `_headers`, Cloudflare rules) or the server middleware. +5. "Google and ChatGPT see an almost empty page": the app renders all text with JavaScript; prerender or + server-render the public pages (landing, pricing) so crawlers read them. + +## How it stays safe + +- Passive by default: it downloads only what a browser does (the page, its same-site scripts, links on the home + page, `/favicon.ico`, plus `robots.txt`, `sitemap.xml`, `llms.txt` for the search check). It never probes hidden + paths such as `/.env` or `/admin`. +- Hostile-input rules: http(s) only, redirects and scripts only on the same site, size and count caps, terminal + output stripped of control characters, HTML report fully escaped. `--owner-check` only talks to + `https://.supabase.co`, Firebase hosts, or the audited site itself, and never uses a service_role key. +- `scripts/polish_audit.py` (phone design) and `scripts/geo_audit.py` (Google & AI search) are vendored copies of + the site-polish-audit and geo-seo-audit tools; launch_check.py runs them with `--json`. + +## Limits (honest) + +- Not a penetration test: no login flows, no API fuzzing, no server-side auth or paywall checks, no write tests. + A good score means none of these common launch mistakes were found, not that the app is secure. +- Scripts served from another host (CDN subdomain) are not scanned; the report says how many were skipped. +- Secret patterns cover the common providers; custom tokens and keys inside source maps are not detected. +- Sites behind bot protection may answer 403 to the checker; rate limits (429) are not counted as broken links. +- Phone design and speed need a Chromium browser through Playwright. + +## Source + +MIT, maintained at https://github.com/Ces107/agent-forge/tree/main/skills/launch-check (also packaged there as a Claude Code plugin). diff --git a/skills/launch-check/references/how-it-works.md b/skills/launch-check/references/how-it-works.md new file mode 100644 index 0000000000..b20d768e60 --- /dev/null +++ b/skills/launch-check/references/how-it-works.md @@ -0,0 +1,106 @@ +# launch-check: how it works + +## Why this exists (sources) + +- CVE-2025-48757: 303 Supabase endpoints in 170 of 1,645 Lovable showcase apps (about 10.3 %) could be read or + written with the public anon key; CVSS 9.3 (Matt Palmer, https://mattpalmer.io/posts/statement-on-CVE-2025-48757/ ; + record https://cveawg.mitre.org/api/cve/CVE-2025-48757). Lovable disputes the CVE. +- Moltbook (Feb 2026): Supabase URL + key in client JavaScript, no Row Level Security, about 4.75 M records + readable, found by reading the JS bundle (Wiz, https://www.wiz.io/blog/exposed-moltbook-database-reveals-millions-of-api-keys). +- About 1 in 5 of ~130,000 sites published from 13 vibe-coding platforms exposes at least one secret + (Red Hunt Labs, 2025-09-23, https://redhuntlabs.com/blog/echoes-of-ai-exposure-thousands-of-secrets-leaking-through-vibe-coded-sites-wave-15-project-resonance/). +- Of 1,072 Supabase-backed vibe-coded apps, 16 % had a critical issue and 39 had tables fully readable with the + anon key (Symbiotic Security, 2026-06-02, https://www.symbioticsec.ai/blog/we-scanned-1-072-vibe-coded-apps-98-had-security-flaws). + +## Pipeline + +1. **Fetch** the URL with urllib (no automatic redirects). Redirects are followed by hand, at most 5, only to the + same host (a leading `www.` is ignored) and only http(s). Bodies are capped (3 MB per file, 20 scripts, 15 MB in + all). Identity: `launch-check/1.0` user agent. +2. **Security (passive)** + - Scans the HTML, inline scripts and every same-origin `" for b in blocks) + (jsonld_dir / name).write_text(body + "\n", encoding="utf-8") + snippets.append({"url": page.url, "file": f"jsonld/{name}", "types": [b["@type"] for b in blocks]}) + if snippets: + (jsonld_dir / "manifest.json").write_text(json.dumps(snippets, indent=2), encoding="utf-8") + written.append(f"jsonld/ ({len(snippets)} pages)") + + # meta fixes + rows = [] + for page in pages: + parser = page.parser + title = (parser.title or "").strip() + desc = (meta_content(parser, "description") or "").strip() + if not desc: + first = next((p for p in parser.paragraphs if words(p) >= 8), "") + rows.append([page.url, "description-missing", "", trim(first, 155)]) + elif not 70 <= len(desc) <= 160: + rows.append([page.url, "description-length", desc, trim(desc, 155) if len(desc) > 160 else ""]) + if title and len(title) > 60: + rows.append([page.url, "title-long", title, trim(title, 60)]) + if not title: + rows.append([page.url, "title-missing", "", parser.headings[0][1] if parser.headings else ""]) + for f in report["findings"]: + if f["rule"] in ("title-duplicate", "desc-duplicate"): + for url in f["urls"]: + rows.append([urlparse.urljoin(origin, url), f["rule"], "", "write a unique one"]) + if rows: + with open(out_dir / "meta-fixes.csv", "w", newline="", encoding="utf-8") as handle: + writer = csv.writer(handle) + writer.writerow(["url", "issue", "current", "suggestion"]) + writer.writerows(rows) + written.append(f"meta-fixes.csv ({len(rows)} rows)") + + readme = [EMIT_MARKER, "", + f"Site: {audit.start} Score: {report['score']}/100 Generated from {report['pages_crawled']} crawled pages.", + "Nothing here is applied automatically. Review each file, then copy it into your site.", ""] + howto = { + "llms.txt": "Put at the site root as /llms.txt (text/plain). Edit the summary and drop pages you do not want listed.", + "robots.txt": "Compare with your current robots.txt, then replace it. Answer/search bots are allowed; training bots keep your existing choice.", + "sitemap.xml": "Serve as /sitemap.xml after review.", + "jsonld/": "Paste each file's