Kelp
<div align="center">
# Kelp
**Security scanner for vibe-coded apps.**
Finds the doors AI code generators leave open — hardcoded secrets, permissive
RLS, unauthenticated edge functions — and gates them out of your pull requests.
[](LICENSE)
[](https://github.com/Mic52M/kelp/actions/workflows/ci.yml)
[](CONTRIBUTING.md)
[](https://kelp.build)
[](https://glama.ai/mcp/servers/Mic52M/kelp)
[Live app](https://kelp.build) · [Docs](docs/) · [CLI](docs/CLI.md) · [MCP server](docs/MCP.md) · [GitHub Action](https://github.com/kelp-security/kelp-action) · [Architecture](docs/ARCHITECTURE.md)
</div>
---
## What it does
Kelp scans an app's **backend surface** the way an attacker would: Supabase
(RLS policies, storage ACLs, edge functions) and Firebase (Firestore and
Storage security rules), plus the Next.js route layer in front of them and the
whole source tree. Every finding is **evidence-gated**: a reviewer re-runs the
reproduction before it lands in your report, so what you see is what an attacker
would actually get.
Four surfaces, one detection engine:
| Surface | For | How you use it |
|---|---|---|
| **CLI** [`kelp`](docs/CLI.md) | Local scans, CI shells, scripts | `npx @kelp-security/cli scan ./my-app` |
| **MCP server** [`kelp mcp`](docs/MCP.md) | Claude Code, Claude Desktop, Cursor, any MCP client | `npx @kelp-security/cli mcp` |
| **GitHub Action** [`kelp/check`](https://github.com/kelp-security/kelp-action) | Pull-request gating | `uses: kelp-security/kelp-action@v1` |
| **Hosted app** [kelp.build](https://kelp.build) | Continuous scanning, dashboard, PR fixes | Connect a repo, sign in with GitHub |
Zero configuration in the common case. The Action reads the workflow's
`GITHUB_TOKEN`, the hosted app installs a GitHub App, the CLI walks the
filesystem, the MCP server speaks stdio JSON-RPC to the LLM client.
## Quickstart — CLI
```bash
npx @kelp-security/cli scan ./my-app
```
```
kelp v0.14.0 · scanning ./my-app · 214 files walked
CRITICAL src/lib/db.ts:14 VITE_SERVICE_ROLE — Supabase service_role JWT
CRITICAL firestore.rules:9 /posts is public (allow read, write: if true)
HIGH src/api/orders.ts hardcoded Stripe secret (sk_live_…)
HIGH app/api/orders/route.ts POST handler has no auth check
MEDIUM supabase/config.toml verify_jwt=false on get-order
5 findings · 8s · report at ./kelp-report.json
```
Add `--json` for machine-readable output, `--severity high` to filter, or see
[docs/CLI.md](docs/CLI.md) for the full reference.
## Quickstart, MCP server
Kelp implements the [Model Context Protocol](https://modelcontextprotocol.io) so
an LLM client (Claude Code, Claude Desktop, Cursor, or any MCP-compatible client)
can call Kelp mid-conversation, while it is writing code, instead of scanning
after the fact. Same static engine as the CLI, exposed as MCP tools, resources,
and slash commands over stdio. Runs locally, offline. No file content ever leaves
the machine.
Add to your client's MCP config (`~/.claude.json`, Claude Desktop config, or the
Cursor MCP settings):
```json
{
"mcpServers": {
"kelp": {
"command": "npx",
"args": ["-y", "@kelp-security/cli", "mcp"]
}
}
}
```
Restart the client, then ask the assistant to scan the repo, or use the
`/kelp:review-repo` slash command.
**MCP tools:** `scan_path`, `scan_snippet`, `list_rules`, `explain_finding`,
`explain_rule`. **Resources:** `kelp://rules`, `kelp://rules/{ruleId}`.
**Prompts (slash commands):** `/kelp:review-repo`, `/kelp:harden-file`.
Full guide, with per-client install snippets, in [docs/MCP.md](docs/MCP.md).
## Quickstart — GitHub Action
Add `.github/workflows/kelp-check.yml`:
```yaml
name: kelp/check
on:
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: kelp-security/kelp-action@v1
```
Kelp will run on every PR, comment the verdict on the PR, and fail the check
when new **critical or high** findings are introduced against the base branch.
See [the action docs](https://github.com/kelp-security/kelp-action) for inputs
and required-status-check setup.
## What Kelp checks today
Everything above the BOLA row is static, offline, and free: no API key, no
live target, no setup. Run it in CI on every PR.
| Class | How | Output |
|---|---|---|
| **Secrets** in source | Provider patterns (AWS/GCP/Stripe/Supabase/GitHub/OpenAI/Anthropic/…) + entropy fallback | Masked preview + line + severity |
| **Client-exposed secrets** | Backend keys named with a public prefix (`NEXT_PUBLIC_`, `VITE_`, …) that ship to the browser, e.g. a `service_role` key that bypasses RLS | Var name + line + severity |
| **Supabase RLS** | Parses `supabase/migrations/*.sql` into a schema graph: tables open to `anon`, FK leaks to unprotected tables, command-scope gaps, views that bypass RLS | Rule id + affected table |
| **Supabase Storage** | Public buckets, and `storage.objects` policies with no owner check or a blanket `true` | Rule id + bucket or policy |
| **Firebase rules** | Reads `firestore.rules` / `storage.rules`: `allow ...: if true`, unauthenticated writes, writes with no owner binding | Rule id + rule path + line |
| **Next.js routes + actions** | Heuristic over `app/**/route.ts`, `pages/api/**`, and `"use server"` files that read or write with no auth call | File + line + severity |
| **Open redirects** | A redirect sink fed by a user-controlled URL (`?next=`) with no allowlist or relative-path guard, high in auth callbacks | File + line + severity |
| **CORS misconfiguration** | Reflected `Origin` with credentials (critical), `Access-Control-Allow-Origin: *` with credentials (high) or without (medium) | File + line + severity |
| **Edge functions** | `verify_jwt=false` in `supabase/config.toml`, plus recon of the deployable surface | Function name + reproduction |
| **CORS + auth flows** | Reads config + auth callbacks for permissive defaults | Config diff |
| **BOLA** (opt-in, hosted only) | Active test with two user-provided test accounts | Human-review only, never auto-fix |
An optional agent pass (`--agent`, needs `ANTHROPIC_API_KEY`) layers an
autonomous auditor on top of the static set. See [docs/CLI.md](docs/CLI.md).
New detections land in [packages/core/src/scanners/](packages/core/src/scanners/).
Kelp reads two backends today, Supabase and Firebase, behind a `BackendAdapter`
seam. See [docs/ADAPTERS.md](docs/ADAPTERS.md) for the adapter model and what
comes next (Convex, PocketBase, and the [north-star issue](https://github.com/Mic52M/kelp/issues/45)).
## Architecture
Monorepo (npm workspaces). Three surfaces share one engine:
```
apps/
├─ web/ Next.js, hosted app at kelp.build
├─ cli/ kelp binary, standalone Node CLI + MCP server
├─ worker/ scan pipeline + integrations (GitHub, Supabase, queue)
└─ test-target/ deliberately-vulnerable fixtures for verification
packages/
├─ core/ detection engine (pure, no I/O)
└─ db/ SQL migrations
```
The **core** is intentionally I/O-free: it takes `SourceFile[]` and returns
`Finding[]`. The CLI shells file-reads to it; the worker adds the GitHub App
plumbing; the web app adds auth, storage, and the reviewer loop.
Full breakdown: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
## Evidence-gating
Kelp's most important invariant: **the model never decides a finding is real.**
Every agent-produced lead requires a reproduction (probe + expected observable,
or a source citation). The executor re-runs it and records the finding only if
the observable holds. Autonomy in reasoning, zero fabrication.
Read the full principle at [docs/EVIDENCE-GATING.md](docs/EVIDENCE-GATING.md).
## Contributing
PRs, bug reports, and new detection classes are welcome — see
[CONTRIBUTING.md](CONTRIBUTING.md) for the dev setup and the review checklist.
Security vulnerabilities go through [SECURITY.md](SECURITY.md), not the public
issue tracker.
Good first contributions:
- A new secret provider pattern in
[`packages/core/src/scanners/secrets.ts`](packages/core/src/scanners/secrets.ts).
- A new edge-function heuristic in
[`packages/core/src/agent/edge-functions.ts`](packages/core/src/agent/edge-functions.ts).
- Docs improvements — the tutorials in `docs/` are always in flight.
## License
MIT — see [LICENSE](LICENSE).
## Acknowledgements
Kelp exists because vibe-code tools ship a lot of the same footguns, and the
people using them shouldn't need a security team to catch them. Built by
[@Mic52M](https://github.com/Mic52M) — solo, in the open.
TDQS
Scored across 5 tools
Each tool has a distinct core purpose: scanning paths vs snippets, listing rules, and explaining rules vs findings. The pairings scan_path/scan_snippet and explain_rule/explain_finding have some surface similarity, but descriptions clarify their boundaries.
All tool names follow a consistent verb_noun pattern in snake_case: scan_path, scan_snippet, list_rules, explain_rule, explain_finding. The naming is predictable and makes the toolset easy to navigate.
Five tools is well-scoped for a security scanning server: two scan entry points, one rule catalog query, and two explanation helpers. Each tool serves a clear, non-redundant role in the workflow.
The toolset covers the full lifecycle expected of a static analysis scanner: discover rules, scan a path or snippet, retrieve rule details, and explain findings with remediation. There are no obvious missing operations for the stated domain.