check-security
Security and privacy review of a Keelokit project, in depth — a map of every piece of personal data (what, why, where, who sees it, how long), the privacy law of each market, a threat model of the cri
SKILL.mdskills/check-security/SKILL.md
GitHubCheck security — what an attacker or a regulator would find first
Autonomous until the report, like check-bugbash, with the same exception: a fix that changes a
product rule (what data is kept, for how long, who may see it) is a pending decision for the
user, with options and a recommendation. Legal questions are named and explained, never decided:
the user or their counsel decides.
Scope comes from the profile (.keelokit/profile.toml): the data map and privacy law when it has
personal-data; the staging scan when it's hosted; payment flows when it has payments. A
developer-facing project (library, CLI, plugin) gets supply-chain questions instead: what it runs
on the user's machine, which network calls it makes, what permissions it asks for, and whether
anything private ends up in what it distributes.
The day-to-day checks already run on every push: secret scan (SEC-1), pnpm audit through .keelokit/bin/audit.py (SEC-3: blocks what has a fix, lists what doesn't),
Semgrep (SAST-1), the access matrix (AUTHZ-1). This skill looks for what they can't.
1. Data map — docs/privacy/data-map.md
From the Prisma schema, the API contracts in packages/shared, the logs, analytics and error
reporting, and every third party that receives data (mail, WhatsApp, payments, hosting):
| Data | Where | Kind | Why we keep it | Who can see it | Kept for | Leaves to |
|---|
Kind: personal (identifies someone: name, email, phone, IP), sensitive (health, finances,
children, location history…), secret (passwords, tokens). A field nobody can justify is a
finding: don't collect it. Markets and languages come from docs/context/product.md; name the law
that applies to each (e.g. GDPR in the EU, LGPD in Brazil, Ley 25.326 in Argentina, Ley 18.331 in
Uruguay) and what it asks of this product in plain words. Unknowns are gaps with an owner.
2. Privacy checks
- No personal data in logs or error reports: the logger redacts the map's
personal,sensitiveandsecretfields; Sentry'sbeforeSendscrubs them. Proven with a test that logs and reports a record full of them and finds none. - Export and deletion: a person can get their data and have it deleted (or anonymised where the law requires keeping it), including from third parties; a test per path.
- Retention: what the map says is kept for N days is actually removed (a scheduled job and its test), backups included.
- Encryption:
sensitivefields encrypted at rest; everything in transit over HTTPS. - Consent and notices: what the product promises in its screens and privacy text matches what it does (COPY-1).
3. Security checks
- Threat model of each critical journey (STRIDE, one table each): who could abuse it, how, what stops them, what doesn't yet.
- Access: another tenant's ids, another role, an expired session, guessable ids, mass assignment, rate limits on login, sign-up and anything that sends a message or costs money.
- Dependencies and images:
pnpm audit, plusdocker run --rm -v "$PWD:/src" ghcr.io/google/osv-scanner:latest -r /srcand the image scan of the API's Dockerfile; licences that aren't OSI-approved (DEP-1). - Staging, from outside:
docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py -t <staging url>— headers, cookies, CORS, TLS. Never against production without the user's yes. - Secrets and config: nothing sensitive in the web bundle or the mobile app; production config complete (CFG-1).
4. Fix and leave a check
Same loop as check-bugbash: a failing test first (titled with the finding id), the fix at the
cause, the check that now covers the class (a test, a lint rule, a Semgrep rule in
.semgrep/, an invariant of class isolation, a house rule registered with
/keelokit:check-health), a row in docs/escapes.md. What can't be fixed now becomes a backlog
story with origin = "security:<date> <finding id>" (/keelokit:plan-backlog).
5. Report — docs/security/<date>/report.md
## Scope first (sha, staging URL, what was checked and what couldn't be); the findings table
(id · area · severity P0–P3 · title · status · fix commit · check added) with the same statuses as a
bug bash; ## Pending decisions (privacy choices and legal questions, with options); the data map's
changes. Refresh the dashboard: its Security section reads these reports.