2026-01-29 12:34:35 +00:00
# TODO - core-content
Production quality improvements for the Content Module.
**Legend:**
- P1: Critical/Security - Must fix immediately
- P2: High priority - Fix soon
- P3: Medium priority - Important improvements
- P4: Low priority - Nice to have
- P5: Nice-to-have - When time permits
- P6+: Future/Backlog - Long-term improvements
---
## P1 - Critical/Security
### SEC-001: Add CSRF protection to webhook endpoints
- **Status:** Open
- **Description:** The webhook endpoint at `POST /api/content/webhooks/{endpoint}` accepts external requests but only validates via HMAC signature. If signature verification is skipped (when no secret is configured), the endpoint is vulnerable.
- **File:** `Controllers/Api/ContentWebhookController.php:205-210`
- **Fix:** Require signature verification always OR add explicit opt-in flag to disable it, with warning logs.
- **Acceptance:** Webhooks without secrets must be explicitly enabled per-endpoint.
### SEC-002: Sanitise HTML content before rendering
- **Status:** Fixed
- **Description:** `ContentItem::getSanitisedContent()` falls back to `strip_tags()` if HTMLPurifier is unavailable. This fallback is insufficient for XSS protection.
- **File:** `Models/ContentItem.php:333-351`
- **Fix:** Always require HTMLPurifier or a robust sanitiser. Add package dependency check in boot.
- **Acceptance:** Content rendering always goes through proper XSS sanitisation.
- **Resolution:** Created `Services/HtmlSanitiser.php` using HTMLPurifier as a required dependency. Added HTMLPurifier to composer.json require. Added boot-time validation that throws RuntimeException if dependency missing. Removed insecure strip_tags() fallback. Added comprehensive XSS prevention tests in `tests/Unit/HtmlSanitiserTest.php` .
### SEC-003: Validate workspace access in MCP handlers
- **Status:** Open
- **Description:** MCP handlers check entitlements but workspace resolution via `orWhere('id', $slug)` could expose content across workspaces if numeric IDs are guessed.
- **File:** `Mcp/Handlers/ContentCreateHandler.php:212-220` , `ContentSearchHandler.php:129-137`
- **Fix:** Add explicit workspace ownership/membership check before returning data.
- **Acceptance:** Users can only access content from workspaces they own or are members of.
### SEC-004: Rate limit preview URL generation
- **Status:** Open
- **Description:** Preview token generation has no rate limiting. An attacker could enumerate valid content IDs by watching response times.
- **File:** `Controllers/ContentPreviewController.php:26-49`
- **Fix:** Add rate limiting to preview generation endpoint.
- **Acceptance:** Preview generation limited to 30/minute per user.
### SEC-005: Validate content_type enum in webhook payloads
- **Status:** Open
- **Description:** Webhook processing accepts arbitrary `content_type` strings from external sources without validation.
- **File:** `Jobs/ProcessContentWebhook.php:288-289`
- **Fix:** Validate against `ContentType` enum before assigning to model.
- **Acceptance:** Invalid content types rejected with clear error message.
---
## P2 - High Priority
### DX-001: Add missing type hints to scope methods
- **Status:** Open
- **Description:** Scope methods like `scopeForWorkspace` , `scopePublished` etc. use `$query` without `Builder` type hint.
- **Files:** `Models/ContentItem.php:147-198` , `Models/ContentBrief.php:181-215`
- **Fix:** Add `\Illuminate\Database\Eloquent\Builder` type hints.
- **Acceptance:** All scope methods have proper return types.
### DX-002: Document search service API response format
- **Status:** Open
- **Description:** `ContentSearchService::formatForApi()` returns a specific structure but it's not documented.
- **File:** `Services/ContentSearchService.php:467-493`
- **Fix:** Add PHPDoc with return type schema or create a Resource class.
- **Acceptance:** API response format documented with example JSON.
### TEST-001: Add integration tests for AI generation pipeline
- **Status:** Open
- **Description:** `AIGatewayService` has no tests. The two-stage Gemini+Claude pipeline is critical but untested.
- **File:** `Services/AIGatewayService.php`
- **Fix:** Add tests with mocked API responses for `generateDraft` , `refineDraft` , `generateAndRefine` .
- **Acceptance:** 80%+ coverage on AIGatewayService with edge case tests.
### TEST-002: Add tests for webhook signature verification
2026-01-29 14:22:09 +00:00
- **Status:** Fixed
2026-01-29 12:34:35 +00:00
- **Description:** `ContentWebhookEndpoint::verifySignature()` handles multiple formats but isn't fully tested.
- **File:** `Models/ContentWebhookEndpoint.php:204-237`
- **Fix:** Add unit tests for each signature format and grace period behaviour.
- **Acceptance:** Tests cover: sha256= prefix, grace period rotation, empty signature handling.
2026-01-29 14:22:09 +00:00
- **Resolution:** (2026-01-29) See P2-082 fix. Added comprehensive feature tests in `tests/Feature/WebhookSignatureVerificationTest.php` covering all signature formats, grace period rotation, and various failure scenarios.
2026-01-29 12:34:35 +00:00
### PERF-001: Add database index for content search
- **Status:** Open
- **Description:** LIKE-based search on `content_html` has no fulltext index, causing table scans.
- **File:** `Services/ContentSearchService.php:142-162` , `Migrations/0001_01_01_000001_create_content_tables.php`
- **Fix:** Add MySQL fulltext index on title, excerpt, content_markdown columns OR document Meilisearch as required for production.
- **Acceptance:** Search queries under 100ms for 10k+ content items.
### PERF-002: Optimise revision pruning for large datasets
- **Status:** Open
- **Description:** `ContentRevision::pruneAll()` loads all content_item_ids into memory before iterating.
- **File:** `Models/ContentRevision.php:595-609`
- **Fix:** Use `chunk()` or cursor to process in batches.
- **Acceptance:** Pruning handles 100k+ content items without memory issues.
### BUG-001: Fix content_briefs migration schema mismatch
- **Status:** Open
- **Description:** Migration defines `content_briefs` with different columns than model fillable (e.g., `user_id` vs model relationships).
- **File:** `Migrations/0001_01_01_000001_create_content_tables.php:215-238` , `Models/ContentBrief.php:49-75`
- **Fix:** Align migration with actual model usage or add a migration to fix schema.
- **Acceptance:** All ContentBrief columns are used and documented.
### BUG-002: Fix ai_usage migration column naming
- **Status:** Open
- **Description:** Migration creates `feature` column but model uses `purpose` . Creates confusion.
- **File:** `Migrations/0001_01_01_000001_create_content_tables.php:246` , `Models/AIUsage.php:46`
- **Fix:** Add migration to rename column OR update model to use `feature` .
- **Acceptance:** Column name matches model fillable property.
---
## P3 - Medium Priority
### CODE-001: Extract webhook processing logic into service
- **Status:** Open
- **Description:** `ProcessContentWebhook` job contains 500+ lines of business logic that should be in a service.
- **File:** `Jobs/ProcessContentWebhook.php`
- **Fix:** Create `ContentWebhookProcessingService` with methods for each event type.
- **Acceptance:** Job is under 100 lines, delegates to service.
### CODE-002: Create ContentBriefResource for API responses
- **Status:** Open
- **Description:** Controllers manually format brief responses. A Resource class would ensure consistency.
- **File:** `Controllers/Api/ContentBriefController.php` references `ContentBriefResource` which may not exist.
- **Fix:** Create or verify `Resources/ContentBriefResource.php` exists with proper formatting.
- **Acceptance:** All brief API responses use the Resource class.
### CODE-003: Consolidate workspace resolution logic
- **Status:** Open
- **Description:** Three different `resolveWorkspace()` methods exist with similar but not identical logic.
- **Files:** `Controllers/Api/ContentSearchController.php` , `Mcp/Handlers/*` , `Services/ContentRender.php`
- **Fix:** Create trait or shared helper in core-tenant.
- **Acceptance:** Single source of truth for workspace resolution.
### TEST-003: Add tests for revision diff algorithm
- **Status:** Open
- **Description:** `ContentRevision::getDiff()` and LCS algorithm are complex but only lightly tested.
- **File:** `Models/ContentRevision.php:233-509`
- **Fix:** Add unit tests for edge cases: empty content, identical content, very long content.
- **Acceptance:** Diff algorithm has 90%+ coverage with edge cases documented.
### TEST-004: Add webhook retry service tests
- **Status:** Open
- **Description:** `WebhookRetryService` has retry logic with exponential backoff but no tests.
- **File:** `Services/WebhookRetryService.php`
- **Fix:** Add tests for retry scheduling, backoff intervals, exhaustion handling.
- **Acceptance:** Full coverage of retry state transitions.
### FEAT-001: Add content scheduling command
- **Status:** Open
- **Description:** `PublishScheduledContent` command is registered but implementation needs verification.
- **File:** `Console/Commands/PublishScheduledContent.php`
- **Fix:** Verify command works, add scheduler entry documentation.
- **Acceptance:** Scheduled content publishes automatically at the correct time.
### FEAT-002: Add media upload validation
- **Status:** Open
- **Description:** `ContentMediaController` store method should validate file types, sizes, dimensions.
- **File:** `Controllers/Api/ContentMediaController.php`
- **Fix:** Add comprehensive validation rules for media uploads.
- **Acceptance:** Reject files over size limit, invalid types, malformed images.
### FEAT-003: Add bulk operations for content items
- **Status:** Open
- **Description:** No bulk delete, bulk status change, or bulk category assignment endpoints.
- **Files:** API routes, new controller methods needed
- **Fix:** Add bulk endpoints with proper authorisation and rate limiting.
- **Acceptance:** Can bulk-update up to 50 items per request.
---
## P4 - Low Priority
### DX-003: Add IDE helper annotations to models
- **Status:** Open
- **Description:** Models lack `@property` annotations for dynamic attributes like `status_color` .
- **Files:** All models in `Models/`
- **Fix:** Add comprehensive `@property` PHPDoc blocks for all magic attributes.
- **Acceptance:** IDE autocomplete works for all model properties.
### DX-004: Document configuration options
- **Status:** Open
- **Description:** `config.php` has comments but no comprehensive documentation of all options and their effects.
- **File:** `config.php`
- **Fix:** Add CLAUDE.md section or dedicated config docs explaining each option.
- **Acceptance:** Every config option documented with type, default, and example.
### CODE-004: Remove deprecated WordPress-specific code paths
- **Status:** Open
- **Description:** Multiple methods have WordPress-specific handling that may be unused.
- **Files:** `Models/ContentItem.php` (wp_id, wp_guid), various scopes
- **Fix:** Audit usage, add deprecation notices if still needed, or remove.
- **Acceptance:** Clear documentation of what is deprecated vs maintained.
### CODE-005: Standardise error response format
- **Status:** Open
- **Description:** Error responses vary: `['error' => ...]` , `['message' => ...]` , different status codes.
- **Files:** All controllers in `Controllers/Api/`
- **Fix:** Use consistent error format: `{error: string, code: string, message: string}` .
- **Acceptance:** All error responses follow documented schema.
### PERF-003: Add eager loading hints to API responses
- **Status:** Open
- **Description:** Some API responses trigger N+1 queries for related data.
- **Files:** `Controllers/Api/ContentBriefController.php:31-77`
- **Fix:** Add `->with(['workspace', 'contentItem'])` where appropriate.
- **Acceptance:** No N+1 queries in API responses (verified with debugbar).
### TEST-005: Add factory states for all content statuses
- **Status:** Open
- **Description:** Factory states exist but may not cover all status/type combinations.
- **Files:** `Database/Factories/*.php` (if they exist, or in test setup)
- **Fix:** Ensure factories have states for: draft, publish, future, private, pending, trash.
- **Acceptance:** Tests can easily create content in any status.
---
## P5 - Nice to Have
### FEAT-004: Add content versioning comparison UI support
- **Status:** Open
- **Description:** `ContentRevision::getDiff()` returns data but no documented UI integration.
- **File:** `Models/ContentRevision.php`
- **Fix:** Document how to integrate diff data with frontend diff viewer.
- **Acceptance:** Example Livewire component or documentation for diff display.
### FEAT-005: Add webhook event deduplication
- **Status:** Open
- **Description:** Same webhook could be received multiple times (network retry). No dedup.
- **File:** `Jobs/ProcessContentWebhook.php`
- **Fix:** Add deduplication based on payload hash + timestamp window.
- **Acceptance:** Duplicate webhooks within 5 minutes are skipped.
### FEAT-006: Add content analytics tracking
- **Status:** Open
- **Description:** No tracking of content views, engagement, or performance metrics.
- **Files:** New feature needed
- **Fix:** Integrate with core-analytics or add simple view tracking.
- **Acceptance:** Can see view counts and basic metrics per content item.
### CODE-006: Add event dispatching for content lifecycle
- **Status:** Open
- **Description:** Content creation/update/publish doesn't dispatch domain events for other modules.
- **Files:** `Models/ContentItem.php` , `Observers/ContentItemObserver.php`
- **Fix:** Dispatch events like `ContentPublished` , `ContentUpdated` etc.
- **Acceptance:** Other modules can listen for content events.
### DOCS-001: Add API documentation
- **Status:** Open
- **Description:** API endpoints lack OpenAPI/Swagger documentation.
- **Files:** `routes/api.php`
- **Fix:** Add Scribe or OpenAPI annotations for all endpoints.
- **Acceptance:** OpenAPI spec can be generated and used in API clients.
---
## P6 - Future/Backlog
### FEAT-007: Add content workflow/approval system
- **Status:** Backlog
- **Description:** No formal review/approval workflow for content before publishing.
- **Fix:** Add ContentWorkflow model with states and transitions.
### FEAT-008: Add content localisation/translation support
- **Status:** Backlog
- **Description:** No i18n support for multilingual content.
- **Fix:** Add locale field and translation linking to ContentItem.
### FEAT-009: Add content A/B testing
- **Status:** Backlog
- **Description:** No ability to test content variations.
- **Fix:** Add ContentVariant model for headline/content testing.
### PERF-004: Add content caching layer
- **Status:** Backlog
- **Description:** CDN purge exists but no server-side caching strategy documented.
- **Fix:** Document caching strategy, add Redis caching for hot content.
### CODE-007: Extract prompts to database-driven system
- **Status:** Backlog
- **Description:** AI prompts are hardcoded in `AIGatewayService` . Prompts table exists but unused for this.
- **File:** `Services/AIGatewayService.php:226-525`
- **Fix:** Load prompts from database, allow admin editing.
---
## Completed
2026-01-29 14:22:09 +00:00
### P2-082: Webhook Signature Verification (2026-01-29)
- **Description:** Signature verification was present but not comprehensively tested or centrally logged.
- **Resolution:**
- Created `Services/WebhookDeliveryLogger.php` service for centralised signature verification logging
- Updated `Controllers/Api/ContentWebhookController.php` to use the new service
- Added comprehensive feature tests in `tests/Feature/WebhookSignatureVerificationTest.php` :
- Tests for all signature header formats (X-Signature, X-Hub-Signature-256, X-WP-Webhook-Signature)
- Tests for grace period during secret rotation
- Tests for require_signature configuration
- Tests for signature failure audit logging
- Tests for timing-safe comparison verification
- Failed signature attempts are logged without storing potentially malicious payloads
- Security audit trail for all signature verification outcomes
### P2-083: Webhook Delivery Logging (2026-01-29)
- **Description:** Webhook delivery lacked comprehensive logging for debugging and audit purposes.
- **Resolution:**
- Created `Services/WebhookDeliveryLogger.php` service with:
- `logSuccess()` - logs successful deliveries with duration metrics
- `logFailure()` - logs failed deliveries with error details
- `logSignatureFailure()` - creates audit entries for signature failures
- `logSignatureNotRequired()` - warns when verification is bypassed
- `createDeliveryLog()` - creates delivery log entries with full request details
- `getDeliveryStats()` - retrieves delivery statistics for dashboards
- `getRecentSignatureFailures()` - retrieves recent failures for security monitoring
- Added comprehensive unit tests in `tests/Unit/WebhookDeliveryLoggerTest.php`
- Migration `2026_01_29_000001_add_signature_verification_fields.php` adds:
- `signature_verified` - boolean tracking verification success
- `signature_failure_reason` - stores failure reason for audit
- `processing_duration_ms` - tracks processing time
- `request_headers` - stores safe headers for debugging
- `response_code` and `response_body` - tracks response details
2026-01-29 12:34:35 +00:00
### SEC-002: HTML sanitisation fallback vulnerability (2026-01-29)
- Created `Services/HtmlSanitiser.php` using HTMLPurifier
- Added `ezyang/htmlpurifier` as required dependency in composer.json
- Updated `ContentItem::getSanitisedContent()` to use the new service
- Added boot-time validation to throw exception if HTMLPurifier is missing
- Removed insecure `strip_tags()` fallback that allowed XSS via event handlers
- Added 30+ unit tests covering XSS attack vectors and safe HTML preservation
---
## Notes
### Dependencies
- Requires `core-php` for events and base infrastructure
- Requires `core-tenant` for workspace and user models
- Requires `ezyang/htmlpurifier` for XSS sanitisation (security-critical)
- Optional: `core-agentic` for AI services (GeminiService, ClaudeService)
- Optional: `core-mcp` for MCP tool registration
### Testing
Run tests with: `composer test` from package root.
Run single test: `./vendor/bin/pest --filter=ContentSearchServiceTest`
### Last Audit
- **Date:** 2026-01-29
- **By:** Claude Code (core-content audit)
- **Files Reviewed:** ~70 PHP files