Repository navigation
feat: security hardening, service layer, API v1, and paste lifecycle - #111
Merged
Merged
Conversation
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>
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/v1/health,/api/v1/pasteCRUD)X-Expire) and burn-after-reading with read counts (X-Burn-Reads).agents/) for coordinated development workflowsSecurity Fixes
crypto.getRandomValues()replacesMath.random()for paste IDs; default length 8→16<script>,<iframe>, event handlers from markdown output*topaste.d3d.dev+ localhostsanitizeFilename()prevents Content-Disposition header injectionsanitizeKeyName()+validatePathWithinDir()on all key storage functionscrypto.randomBytes()replacesMath.random()for fallback PGP key IDsTEST_MODEpassphrase bypass removed from production coderel="noopener noreferrer"on alltarget="_blank"links>=182025-01-01New Features
Service Layer (
src/services/)StorageService— R2/KV abstraction with paste lifecycle managementPasteService— CRUD business logic with duration parsing and URL buildingEncryptionService— Server-side encryption utilities with Web Crypto APIcreateServices()with dependency injectionAPI v1 (
src/api/v1/)GET /api/v1/health— Health check with service statusPOST /api/v1/paste— Create paste with validationGET /api/v1/paste/:id— Retrieve paste as structured JSONDELETE /api/v1/paste/:id— Delete paste{ success, data, error }response formatPaste Lifecycle
X-Expire: 1h|24h|7d|30dheader sets auto-expirationX-Burn-Reads: Nheader sets read-count-based deletionX-Expired: trueheaderX-Expires-AtandX-Remaining-ReadsRequest Validation (
src/validation/)validateTextUpload,validateWebUploadwith field-level error detailsparseJsonBody,checkValidationmiddleware helpersFiles Changed
src/services/,src/validation/,src/api/v1/.agents/with audit reports and sprint planTest plan
npm run buildcompiles cleanlyX-Expire: 1hheader creates expiring pasteX-Burn-Reads: 3header limits read countGET /api/v1/healthreturns structured JSONPOST /api/v1/pastecreates paste via JSON API<script>tags are sanitized🤖 Generated with Claude Code