|
| 1 | +# Changelog |
| 2 | + |
| 3 | +All notable changes to git-cms are documented in this file. |
| 4 | + |
| 5 | +## [Unreleased] — git-stunts branch |
| 6 | + |
| 7 | +### Added |
| 8 | + |
| 9 | +- **Content Identity Policy (M1.1):** Canonical slug validation with NFKC normalization, reserved word rejection, and `CmsValidationError` contract (`ContentIdentityPolicy.js`) |
| 10 | +- **State Machine (M1.2):** Explicit draft/published/unpublished/reverted states with enforced transition rules (`ContentStatePolicy.js`) |
| 11 | +- **Admin UI overhaul:** Split/edit/preview markdown editor (via `marked`), autosave, toast notifications, skeleton loading, drag-and-drop file uploads, metadata trailer editor, keyboard shortcuts (`Cmd+S`, `Esc`), dark mode token system |
| 12 | +- **DI seam in CmsService:** Optional `graph` constructor param enables `InMemoryGraphAdapter` injection for zero-subprocess tests |
| 13 | +- **In-memory test adapter:** Unit tests run in ~11ms instead of hundreds of ms (no `git init`/subprocess forks) |
| 14 | +- **E2E test separation:** Real-git smoke tests in `test/git-e2e.test.js`, excluded from default `test:local` runs |
| 15 | +- **`test:git-e2e` script:** Run real-git integration tests independently |
| 16 | +- **`@git-stunts/alfred` dependency:** Resilience policy library (wired but not yet integrated) |
| 17 | +- **`@git-stunts/docker-guard` dependency:** Docker isolation helpers |
| 18 | +- **ROADMAP.md:** M0–M6 milestone plan with blocking graph |
| 19 | +- **Formal LaTeX ADR** (`docs/adr-tex-2/`) |
| 20 | +- **Onboarding scripts:** `setup.sh`, `demo.sh`, `quickstart.sh` with interactive menus |
| 21 | +- **Dependency integrity check:** `check-dependency-integrity.mjs` prevents `file:` path regressions |
| 22 | + |
| 23 | +### Changed |
| 24 | + |
| 25 | +- CmsService now uses `@git-stunts/git-warp` `GitGraphAdapter` and `@git-stunts/plumbing` `GitRepositoryService` instead of raw plumbing calls |
| 26 | +- All `repo.updateRef()` calls routed through `CmsService._updateRef()` for DI/production dual-path |
| 27 | +- `listArticles()` supports both plumbing (`for-each-ref`) and in-memory (`graph.listRefs`) paths |
| 28 | +- Server endpoints return structured `{ code, field }` errors for validation failures |
| 29 | +- Swapped all `file:` dependency paths to versioned npm ranges (PP3) |
| 30 | + |
| 31 | +### Fixed |
| 32 | + |
| 33 | +- Symlink traversal hardening in static file serving |
| 34 | +- Slug canonicalization enforced at all API ingress points |
| 35 | +- Admin UI API calls aligned with server contract (query params, response shapes) |
| 36 | +- Server integration test environment stabilized for CI |
| 37 | +- **(P1) Stored XSS via markdown preview:** Sanitize `marked.parse()` output with DOMPurify |
| 38 | +- **(P1) Unpublish atomicity:** Reorder `unpublishArticle` so draft ref updates before published ref deletion |
| 39 | +- **(P2) XSS via slug/badge rendering:** Use `textContent` and DOM APIs instead of `innerHTML` interpolation |
| 40 | +- **(P2) SRI hashes:** Add `integrity` + `crossorigin` to marked and DOMPurify CDN script tags |
| 41 | +- **(P2) Null guards:** `revertArticle` and `unpublishArticle` throw `no_draft` when draft ref is missing; `_resolveArticleState` throws `article_not_found` when both draft and published refs are missing |
| 42 | +- **(P2) uploadAsset DI guard:** Throw `unsupported_in_di_mode` when `cas`/`vault` are null |
| 43 | +- **(P2) Trailer key casing:** Use camelCase `updatedAt` in `unpublishArticle` and `revertArticle` (was lowercase `updatedat` which broke `renderBadges` lookups); destructure out decoded lowercase key before spreading to avoid `TrailerInvalidError` |
| 44 | +- **(P2) XSS in `escAttr`:** Escape single quotes (`'` → `'`) to prevent injection into single-quoted attributes |
| 45 | +- **(P2) Supply-chain hardening:** Vendor Open Props CSS files locally (`public/css/`) instead of `@import` from unpkg, eliminating CDN dependency and SRI gap |
| 46 | +- **(P2) Monkey-patch safety:** E2E test restores `plumbing.execute` in `finally` block |
| 47 | +- Unknown `draftStatus` in `resolveEffectiveState` now throws `unknown_status` instead of silently falling through to draft |
| 48 | +- Removed double-canonicalization in `_resolveArticleState` |
| 49 | +- Replaced sequential `readRef` loop with `Promise.all` in `listArticles` DI path |
| 50 | +- Admin UI: fixed `removeTrailerRow` redundant positional removal, FileReader error handling, autosave-while-saving guard, Escape key scoped to editor panel, drag-and-drop scoped to drop zone |
| 51 | +- Test cleanup: extracted `createTestCms()` helper, converted try/catch assertions to `.rejects.toMatchObject()`, added guard-path tests |
| 52 | +- `TRANSITIONS` Sets now `Object.freeze`d to prevent mutation via `.add()`/`.delete()` |
| 53 | +- DI-mode `_updateRef` now performs manual CAS check against `oldSha` |
| 54 | +- Server tests assert setup call status codes to surface silent failures |
| 55 | +- Vitest exclude glob `test/git-e2e*` → `test/git-e2e**` to cover future subdirectories |
| 56 | + |
| 57 | +[Unreleased]: https://github.com/flyingrobots/git-cms/compare/main...git-stunts |
0 commit comments