Skip to content

Commit f3011f2

Browse files
jonradoffclaude
andcommitted
Update CHANGELOG for v4.2.0 with full security, fork MCP, and performance details
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6525fec commit f3011f2

1 file changed

Lines changed: 45 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,62 @@ All notable changes to LightCMS are documented here, organized by version.
44

55
---
66

7-
## v4.2.0 — Performance Improvements
8-
9-
### Database
7+
## v4.2.0 — Security Hardening, Fork MCP Tools & Performance Overhaul
8+
9+
### Security
10+
11+
- **CORS lockdown**: Chat widget endpoints (`/api/chat/*`) now restrict `Access-Control-Allow-Origin` to the site's configured `BASE_URL` instead of `*`. Falls back to `*` only when `BASE_URL` is unset.
12+
- **Prompt injection defense**: User query text in the chat widget is wrapped in `<user_question>...</user_question>` XML delimiters before being interpolated into the Anthropic prompt. The `</` sequence is escaped to prevent tag injection.
13+
- **Prompt template validation**: Saved chat widget system/user prompts are validated to only allow known placeholders (`{siteName}`, `{question}`, `{excerpts}`). Unknown placeholders are rejected at save time.
14+
- **Configurable upload size limit**: `MaxUploadBytes` added to `SiteConfig`, settable via the admin Configuration page and the `update_site_config` API. Defaults to 1 MiB (2× the largest asset stored at time of release). Enforced via `http.MaxBytesReader` on both the file upload endpoint and the asset upload endpoint.
15+
- **API body size limit**: All `/api/v1/` endpoints now enforce a 10 MiB request body cap via `APIBodySizeLimit` middleware, preventing memory exhaustion from oversized payloads.
16+
- **Fly.io IP spoofing fix**: `TrustedProxyConfig` adds a `TrustFlyProxy` mode that reads `Fly-Client-IP` (set exclusively by Fly.io's edge proxy) instead of `X-Forwarded-For`, which can be set by anyone. `DefaultCloudConfig()` now uses this mode, preventing rate-limit and audit-log bypass.
17+
- **Session secret entropy**: Production deployments now hard-fail on startup if `SESSION_SECRET` is shorter than 32 characters (previously 16).
18+
- **Per-endpoint rate limiters**: New limiters protect expensive endpoints beyond the global 300 req/min cap — regenerate (2/min), search-replace execute (10/min), asset-from-url (10/min), bulk-update (5/min), export (5/min), reindex-embeddings (1/min).
19+
- **Rate limiter map pruning**: A background goroutine prunes stale token entries from all rate limiter maps every 5 minutes, preventing unbounded memory growth over long-running deployments.
20+
21+
### Fork MCP Tools (8 new tools)
22+
23+
Full MCP and REST API coverage for the fork workspaces system introduced in v4.0.0:
24+
25+
- **`list_forks`** — List all forks with status and page count.
26+
- **`create_fork`** — Create a named fork workspace.
27+
- **`get_fork`** — Retrieve fork details including merge conflicts from last merge.
28+
- **`fork_page`** — Add a page to a fork (accepts content ID or path); returns fork page ID for use with `update_content`.
29+
- **`remove_fork_page`** — Remove a page from a fork (reverts to live content on preview).
30+
- **`merge_fork`** — Merge all fork changes into live content (admin only).
31+
- **`archive_fork`** — Archive a fork without merging.
32+
- **`delete_fork`** — Permanently delete a fork and all its page copies.
33+
34+
### Performance
35+
36+
- **Admin template caching**: Admin HTML templates are compiled once at startup (via `sync.Once`) and cached. Previously each admin page request re-parsed the full template from source — eliminated entirely.
37+
- **Content list pagination**: The admin Content page now loads 100 items at a time with Previous/Next controls and a total count. Previously it loaded the entire `content` collection into memory, which OOM'd the server on large sites.
38+
- **Content indexes**: Added `{updated_at: -1}` and compound `{fork_id, deleted, updated_at}` indexes to fix `Sort exceeded memory limit` errors on the MongoDB Atlas free tier when the collection grew past ~32 MiB.
39+
- **Search/replace streaming**: All four search/replace handlers (global preview, global execute, scoped preview, scoped execute) now stream documents one at a time via cursor iteration instead of loading the entire collection into a Go slice. Memory is bounded regardless of collection size. Coverage is complete — every document is still checked, nothing is truncated.
40+
- **New `StreamContent` / `StreamContentScoped`** service methods return raw `*mongo.Cursor` for callers that need bounded-memory processing.
41+
- **`QueryContentForDirective` cap**: `lc:query` template directives now apply a 10,000-item `SetLimit` cap, preventing a single index page from OOM-ing the server on a large site.
42+
- **Wikilink index cache**: `buildWikilinkIndex` caches results for 60 seconds (TTL), invalidated immediately on any title, path, or publish-status change. Previously a full `content` collection scan ran for every page publish — on bulk operations this was O(n) scans for n pages.
43+
- **`UpdateWikilinksOnRename` streaming**: Changed from `FindAll` (full load into slice) to streaming cursor iteration, bounding memory when many pages reference a renamed item.
44+
- **Export scope push-down**: `APIExportContent` now uses `ListContentScoped` to push all filters (template, category, folder, content IDs) to MongoDB, instead of loading all content then filtering in Go.
45+
46+
### Database (earlier v4.2.0 work)
1047

1148
- **New indexes**: Added `settings.type`, `login_attempts.ip`, `theme_versions.version`, and `content.plain_text` (text index) to eliminate collection scans on hot query paths.
1249
- **`ListAssets` projection**: Excludes the binary `data` field from asset listing queries, dramatically reducing wire transfer for asset list operations.
1350
- **Atomic login rate limiting**: `RecordFailedLogin` replaced a read-then-write pattern with `FindOneAndUpdate` + `$inc`, eliminating a race condition under concurrent login attempts.
1451

15-
### Search
52+
### Search (earlier v4.2.0 work)
1653

1754
- **Parallel hybrid search**: `SearchHybrid` now runs `SearchFullText` and `SearchSemantic` concurrently via goroutines, halving latency when both sources are available.
1855
- **`sort.Slice` everywhere**: All ranking insertion sorts in `SearchFullText`, `Suggest`, and `RebuildKeywords` replaced with `sort.Slice` / `sort.SliceStable` for O(n log n) behaviour on larger result sets.
19-
- **Pre-normalized search config**: `getSearchConfig` lowercases and trims all path/template lists once on cache load, removing redundant per-query `strings.ToLower`/`strings.TrimSpace` calls in `pathBoost`, `isDemotedPath`, and `isBoostedTemplate`.
56+
- **Pre-normalized search config**: `getSearchConfig` lowercases and trims all path/template lists once on cache load, removing redundant per-query `strings.ToLower`/`strings.TrimSpace` calls.
2057

21-
### Content Regeneration
58+
### Content Regeneration (earlier v4.2.0 work)
2259

2360
- **Parallel `RegenerateAllContent`**: Sequential per-page loop replaced with a semaphore-bounded goroutine pool (6 workers), enabling concurrent static page generation.
24-
- **Single wikilink index per bulk regen**: `buildWikilinkIndex` is called once before the worker pool starts and the result is shared across all workers, instead of once per page.
25-
- **Targeted `UpdateWikilinksOnRename`**: Added a `$regex` pre-filter so only documents likely referencing the old title/path are loaded, avoiding a full collection scan on rename.
61+
- **Single wikilink index per bulk regen**: `buildWikilinkIndex` is called once before the worker pool starts and shared across all workers.
62+
- **Targeted `UpdateWikilinksOnRename`**: `$regex` pre-filter limits the scan to documents that likely reference the old title/path.
2663

2764
---
2865

0 commit comments

Comments
 (0)