Skip to content

feat: security hardening, service layer, API v1, and paste lifecycle - #111

Merged
anoncam merged 1 commit into
mainfrom
feat/security-hardening-and-api-v1
Feb 22, 2026
Merged

anoncam merged 1 commit into
mainfrom
feat/security-hardening-and-api-v1

Conversation

@anoncam

@anoncam anoncam commented Feb 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • 14 security findings remediated from a comprehensive codebase audit (4 Critical, 6 High, 2 Medium, 2 Low)
  • Service layer architecture with dependency-injected storage, paste, and encryption services
  • API v1 with structured JSON responses (/api/v1/health, /api/v1/paste CRUD)
  • Paste lifecycle management: configurable expiration (X-Expire) and burn-after-reading with read counts (X-Burn-Reads)
  • Request validation with typed schemas for all upload endpoints
  • Agent team definitions (.agents/) for coordinated development workflows

Security Fixes

Severity ID Fix
Critical C1 crypto.getRandomValues() replaces Math.random() for paste IDs; default length 8→16
Critical C2 HTML sanitizer strips <script>, <iframe>, event handlers from markdown output
High H1 CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy on all responses
High H2 CORS restricted from * to paste.d3d.dev + localhost
High H3 sanitizeFilename() prevents Content-Disposition header injection
High H4 sanitizeKeyName() + validatePathWithinDir() on all key storage functions
High H5 25MB upload size limit on standard uploads (413 response)
High H6 RSA key generation upgraded from 2048 to 4096 bits
Medium M5 crypto.randomBytes() replaces Math.random() for fallback PGP key IDs
Medium M7 TEST_MODE passphrase bypass removed from production code
Low L2 rel="noopener noreferrer" on all target="_blank" links
Low L4 Node.js engine requirement updated to >=18
Low L6 Wrangler compatibility date updated to 2025-01-01
Bonus -- Stale footer version fixed (1.13.2→1.23.2), keyboard focus indicators restored

New Features

Service Layer (src/services/)

  • StorageService — R2/KV abstraction with paste lifecycle management
  • PasteService — CRUD business logic with duration parsing and URL building
  • EncryptionService — Server-side encryption utilities with Web Crypto API
  • Factory pattern via createServices() with dependency injection

API v1 (src/api/v1/)

  • GET /api/v1/health — Health check with service status
  • POST /api/v1/paste — Create paste with validation
  • GET /api/v1/paste/:id — Retrieve paste as structured JSON
  • DELETE /api/v1/paste/:id — Delete paste
  • Consistent { success, data, error } response format

Paste Lifecycle

  • X-Expire: 1h|24h|7d|30d header sets auto-expiration
  • X-Burn-Reads: N header sets read-count-based deletion
  • Expired pastes auto-deleted on retrieval with X-Expired: true header
  • Response headers include X-Expires-At and X-Remaining-Reads

Request Validation (src/validation/)

  • Pure TypeScript schemas (no runtime deps) for Cloudflare Workers compatibility
  • validateTextUpload, validateWebUpload with field-level error details
  • parseJsonBody, checkValidation middleware helpers

Files Changed

  • 26 files changed: 5,774 insertions, 897 deletions
  • 9 new modules across src/services/, src/validation/, src/api/v1/
  • 8 agent definitions in .agents/ with audit reports and sprint plan

Test plan

  • Verify npm run build compiles cleanly
  • Verify existing paste creation/retrieval still works
  • Test X-Expire: 1h header creates expiring paste
  • Test X-Burn-Reads: 3 header limits read count
  • Test GET /api/v1/health returns structured JSON
  • Test POST /api/v1/paste creates paste via JSON API
  • Verify security headers present on all responses (CSP, HSTS, X-Frame-Options)
  • Verify CORS rejects requests from unauthorized origins
  • Verify uploads > 25MB are rejected with 413
  • Verify markdown pastes with <script> tags are sanitized

🤖 Generated with Claude Code

Security fixes (14 findings remediated):
- Replace Math.random() with crypto.getRandomValues() for paste IDs (C1)
- Sanitize markdown HTML output to prevent stored XSS (C2)
- Add security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options (H1)
- Restrict CORS from wildcard to allowed origins (H2)
- Sanitize Content-Disposition filenames against header injection (H3)
- Add path traversal protection to all key storage functions (H4)
- Add 25MB upload size limit for standard uploads (H5)
- Upgrade RSA key generation from 2048 to 4096 bits (H6)
- Replace Math.random() with crypto.randomBytes() for fallback key IDs (M5)
- Remove TEST_MODE passphrase bypass from production code (M7)
- Add rel="noopener noreferrer" to all target="_blank" links (L2)
- Update Node.js engine requirement to >=18 (L4)
- Update Cloudflare Workers compatibility date to 2025-01-01 (L6)
- Fix stale footer version and restore keyboard focus indicators

New features:
- Service layer architecture (src/services/) with storage, paste, and encryption services
- API v1 with structured JSON responses (/api/v1/health, /api/v1/paste CRUD)
- Paste expiration via X-Expire header (1h, 24h, 7d, 30d, custom durations)
- Burn-after-reading with configurable read counts via X-Burn-Reads header
- Request validation with typed schemas for all upload endpoints
- Agent team definitions for coordinated development (.agents/)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anoncam
anoncam merged commit 9f320dd into main Feb 22, 2026
2 checks passed
github-actions Bot added a commit that referenced this pull request Feb 22, 2026
Version bump type: minor
PR: #111
Title: feat: security hardening, service layer, API v1, and paste lifecycle
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant