diff --git a/.claude/agents/security-reviewer.md b/.claude/agents/security-reviewer.md new file mode 100644 index 0000000..2280d28 --- /dev/null +++ b/.claude/agents/security-reviewer.md @@ -0,0 +1,64 @@ +# Security Reviewer + +You are a security-focused code reviewer specializing in cryptographic implementations and secure data handling. + +## Scope + +Focus on these security-critical areas of the dedpaste codebase: + +### Cryptography (cli/) +- `pgpUtils.ts` — PGP key generation, encryption, decryption, signing +- `encryptionUtils.ts` — Encryption/decryption workflows +- `encryptionHelpers.ts` — Encryption helper functions +- `keyManager.ts` — Key storage and retrieval +- `unifiedKeyManager.ts` — Unified key management layer +- `keybaseUtils.ts` — Keybase integration for key discovery + +### Data Handling (src/) +- `index.ts` — Worker request handling, paste storage/retrieval, auth + +## Review Checklist + +### Cryptographic Issues +- [ ] Weak or deprecated algorithms (check openpgp configuration) +- [ ] Hardcoded keys, IVs, or salts +- [ ] Insufficient key lengths +- [ ] Improper random number generation +- [ ] Missing or incorrect signature verification +- [ ] Key material in logs or error messages + +### Input Validation +- [ ] Unsanitized user input in paste content or metadata +- [ ] Path traversal in paste ID handling +- [ ] Missing size limits on uploads +- [ ] Header injection via user-controlled values + +### Information Leakage +- [ ] Sensitive data in error messages or stack traces +- [ ] Timing differences in authentication checks +- [ ] Key fingerprints or metadata exposed unintentionally +- [ ] Debug logging that leaks secrets + +### Authentication & Authorization +- [ ] One-time paste deletion is properly enforced +- [ ] Burn-after-reading can't be bypassed +- [ ] Password-protected pastes use constant-time comparison +- [ ] R2/KV access patterns don't allow enumeration + +### Dependency Security +- [ ] Check `npm audit` for known vulnerabilities +- [ ] Verify openpgp version is current and not affected by CVEs +- [ ] Review third-party key discovery (Keybase) trust model + +## Output + +Provide findings in this format: + +### [SEVERITY] — Finding Title +- **File**: path/to/file.ts:line +- **Issue**: Description of the vulnerability +- **Impact**: What an attacker could achieve +- **Recommendation**: How to fix it +- **Reference**: CWE or relevant security standard + +Severity levels: CRITICAL, HIGH, MEDIUM, LOW, INFO diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..9c57367 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,62 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true", + "description": "Auto-format edited files with Prettier" + } + ], + "PreToolUse": [ + { + "matcher": "Edit|Write", + "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '(\\.env|\\.dev\\.vars|wrangler\\.toml)$'; then echo 'BLOCK: This file contains secrets or deployment config. Edit manually.' && exit 1; fi", + "description": "Block edits to .env, .dev.vars, and wrangler.toml" + }, + { + "matcher": "Edit|Write", + "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '(package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)$'; then echo 'BLOCK: Lock files should not be edited directly. Use npm install.' && exit 1; fi", + "description": "Block direct edits to lock files" + } + ] + }, + "permissions": { + "allow": [ + "Bash(cat:*)", + "Bash(chmod:*)", + "Bash(grep:*)", + "Bash(node:*)", + "Bash(npm install:*)", + "Bash(npm link)", + "Bash(npm run build:*)", + "Bash(npm run dev:*)", + "Bash(npm run format:*)", + "Bash(npm run lint:*)", + "Bash(npm test:*)", + "Bash(npm audit:*)", + "Bash(npx tsc:*)", + "Bash(npx prettier:*)", + "Bash(npx eslint:*)", + "Bash(git add:*)", + "Bash(git checkout:*)", + "Bash(git commit:*)", + "Bash(git diff:*)", + "Bash(git fetch:*)", + "Bash(git log:*)", + "Bash(git merge:*)", + "Bash(git pull:*)", + "Bash(git push:*)", + "Bash(git remote:*)", + "Bash(git stash:*)", + "Bash(git status:*)", + "Bash(git tag:*)", + "Bash(gh issue:*)", + "Bash(gh pr:*)", + "Bash(gh run:*)", + "Bash(wrangler:*)", + "Bash(dedpaste:*)", + "WebSearch" + ], + "deny": [] + } +} diff --git a/.claude/skills/deploy-check/SKILL.md b/.claude/skills/deploy-check/SKILL.md new file mode 100644 index 0000000..87e102b --- /dev/null +++ b/.claude/skills/deploy-check/SKILL.md @@ -0,0 +1,41 @@ +--- +name: deploy-check +description: Build, type-check, test, and deploy to Cloudflare Workers with verification +disable-model-invocation: true +--- + +# Deploy Check + +Run the full pre-deploy validation pipeline and deploy to Cloudflare Workers. +Stop immediately if any step fails and report the error. + +## Steps + +1. **Type check** — Run `npx tsc --noEmit` to catch type errors without emitting files +2. **Build** — Run `npm run build` to compile TypeScript for both worker and CLI +3. **Lint** — Run `npm run lint` to check for code quality issues +4. **Test** — Run `npm test` to run the test suite +5. **Deploy** — Run `npm run deploy` to deploy to Cloudflare Workers +6. **Verify** — After deployment, run `curl -s -o /dev/null -w "%{http_code}" https://dedpaste.com` to confirm the worker is responding (expect 200 or 404 for root) + +## Failure Handling + +- If type check fails: Report the type errors and suggest fixes +- If build fails: Report compilation errors +- If lint fails: Run `npm run format` first, then re-lint. If lint still fails, report remaining issues +- If tests fail: Report which tests failed and why +- If deploy fails: Check wrangler output for auth or config issues +- If verification fails: Check Cloudflare dashboard status + +## Output + +Provide a summary table: + +| Step | Status | Duration | +|------|--------|----------| +| Type check | ✅/❌ | Xs | +| Build | ✅/❌ | Xs | +| Lint | ✅/❌ | Xs | +| Test | ✅/❌ | Xs | +| Deploy | ✅/❌ | Xs | +| Verify | ✅/❌ | Xs | diff --git a/.claude/skills/release-notes/SKILL.md b/.claude/skills/release-notes/SKILL.md new file mode 100644 index 0000000..0beb0b6 --- /dev/null +++ b/.claude/skills/release-notes/SKILL.md @@ -0,0 +1,56 @@ +--- +name: release-notes +description: Generate release notes from conventional commits since the last git tag +disable-model-invocation: true +--- + +# Release Notes Generator + +Generate a formatted changelog from conventional commits since the last release tag. + +## Steps + +1. **Find the latest tag** — Run `git describe --tags --abbrev=0` to get the most recent tag +2. **Collect commits** — Run `git log ..HEAD --oneline --no-merges` to get all commits since that tag +3. **Parse conventional commits** — Group commits by type prefix: + - `feat:` → **Features** + - `fix:` → **Bug Fixes** + - `perf:` → **Performance** + - `docs:` → **Documentation** + - `refactor:` → **Refactoring** + - `test:` → **Tests** + - `chore:` / `ci:` / `build:` → **Maintenance** + - `BREAKING CHANGE:` or `!:` → **Breaking Changes** (highlighted at top) +4. **Determine next version** — Based on commit types: + - Any breaking change → major bump + - Any `feat:` → minor bump + - Only `fix:` / other → patch bump +5. **Format output** — Generate markdown release notes + +## Output Format + +```markdown +# v{version} — {date} + +## Breaking Changes +- {description} ({short-hash}) + +## Features +- {scope}: {description} ({short-hash}) + +## Bug Fixes +- {description} ({short-hash}) + +## Maintenance +- {description} ({short-hash}) + +**Full Changelog**: {compare-url} +``` + +## Notes + +- If there are no commits since the last tag, report that there's nothing to release +- If there are no tags at all, use the initial commit as the base +- Include the GitHub compare URL: `https://github.com/anoncam/dedpaste/compare/{old-tag}...{new-tag}` +- Strip the conventional commit prefix from descriptions for cleaner output +- Include the scope in parentheses if present (e.g., `feat(cli): add encryption`) diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..0ed5b8f --- /dev/null +++ b/.mcp.json @@ -0,0 +1,9 @@ +{ + "mcpServers": { + "context7": { + "command": "npx", + "args": ["-y", "@upstash/context7-mcp@latest"], + "description": "Live documentation lookup for openpgp, commander, inquirer, Cloudflare Workers APIs" + } + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6b7c3b1 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "tabWidth": 2, + "useTabs": false, + "printWidth": 100, + "bracketSpacing": true, + "arrowParens": "always" +}