Files
Epicure/apps/web/lib/changelog.ts
T
Arnaud 3042d289a0 security: fix full audit findings (v0.32.0)
Full list of the audit's confirmed findings and their fixes:

- Stored XSS via unescaped JSON-LD on the public recipe page
  (app/r/[id]/page.tsx) — escape < before injecting.
- CSP allowed unsafe-eval in production — now dev-only (Next prod
  never eval()s; only its HMR does).
- avatarUrl accepted any URL with no ownership check — now takes an
  avatarKey issued by avatar-presign, validated server-side, same
  pattern as recipe/review photos.
- No session revocation on password change/reset — both now revoke
  other sessions (revokeOtherSessions: true, revokeSessionsOnPasswordReset).
- Rate-limit bypass via spoofable X-Forwarded-For — take the last
  (proxy-appended) hop instead of the first (client-supplied) one,
  matching the single-Traefik-hop topology.
- Webhook signing secrets stored plaintext — now AES-256-GCM
  encrypted like every other secret in this app, with a legacy-
  plaintext fallback for pre-existing rows (bare hex has no ":", our
  ciphertext format always does).
- Better Auth's own rate limiter defaulted to in-memory storage,
  ineffective across replicas — now backed by the same Redis as
  lib/rate-limit.ts (secondaryStorage), with storeSessionInDatabase
  explicit so session storage itself doesn't move as a side effect.
- Presigned upload URLs didn't bind the declared file size to the
  actual upload, letting a client under-declare size (and quota
  charge) then PUT an arbitrarily large object — switched to S3
  presigned POST with a signed content-length-range condition,
  enforced by the storage server itself.
- generateMetadata() on the recipe page skipped the visibility
  filter the page body uses, leaking a private recipe's title via
  <title> to any signed-in user with the id.
- Block/unblock had no rate limit, unlike follow/unfollow.
- AI quota was charged even when a user's own BYOK key was used
  (their own credentials/billing) — added an isByok flag through
  the config-resolution chain and skip the charge when set. Also
  wired BYOK into generate/generate-from-idea/translate/import-url,
  which never looked it up at all before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 15:05:05 +02:00

442 lines
20 KiB
TypeScript

// Mirrors CHANGELOG.md at the repo root — update both together.
export const APP_VERSION = "0.32.0";
export type ChangelogEntry = {
version: string;
date: string;
added?: string[];
fixed?: string[];
security?: string[];
notes?: string;
};
export const CHANGELOG: ChangelogEntry[] = [
{
version: "0.32.0",
date: "2026-07-14 17:10",
security: [
"Fixed a stored-XSS hole in public recipe pages' embedded structured data, and tightened the production Content-Security-Policy so script injection like it can't execute even if a similar bug slips in again.",
"Avatar photos now require proof you actually own the upload — previously any URL was accepted, including another user's uploaded photo key.",
"Changing your password now signs out every other active session, and so does resetting a forgotten password.",
"Fixed a rate-limit bypass on public share-link throttling caused by trusting a spoofable header.",
"Webhook signing secrets are now encrypted at rest, matching how API keys and other secrets are already stored.",
"Login/2FA/password-reset rate limiting now shares Redis instead of quietly resetting per server instance.",
"Photo/avatar uploads are now size-capped by the storage server itself, not just a number the client could lie about.",
"A private recipe's title could leak via its page's browser-tab title to any signed-in user who had the link — fixed to respect the same visibility rules as the page itself.",
"Blocking/unblocking a user is now rate-limited, matching follow/unfollow.",
"AI calls made with your own API key (Settings → AI Keys) no longer count against your monthly AI-call limit.",
],
},
{
version: "0.31.0",
date: "2026-07-14 16:35",
added: [
"**Recipes can now be marked as a Drink/cocktail** instead of a dish — the recipe form hides irrelevant food-only fields (prep/cook time, difficulty, batch cooking) for drinks, and you can filter your recipe list by type. Drink recipes also no longer show the food/drink pairing suggestion buttons on their own page.",
],
},
{
version: "0.30.1",
date: "2026-07-14 16:20",
notes: "Internal: fixed test mocks left stale by the earlier API-key auth-widening pass (recipes/meal-plans/shopping-lists route tests) — no user-facing change.",
},
{
version: "0.30.0",
date: "2026-07-14 16:05",
added: [
"**Admins can now set the site-wide default AI provider/model** per use case (text generation, photo/vision, meal-plan generation) from Settings → Admin, applied whenever a user hasn't picked their own model preference or brought their own API key.",
],
},
{
version: "0.29.0",
date: "2026-07-14 15:20",
added: [
"**Photo recipe import now respects your app language** — previously it defaulted to English (or whatever language was in the photo) instead of matching your locale like other AI generation features.",
],
fixed: [
"Importing a photo with no recognizable recipe used to open the recipe editor anyway with empty/garbage fields — it now shows a clear error instead.",
],
},
{
version: "0.28.3",
date: "2026-07-14 14:55",
fixed: [
"Copying an unlisted recipe's share link wrongly warned \"this link won't work until the recipe is Public\" — unlisted links work fine (that's the point of unlisted); the warning now only shows for actually-private recipes.",
],
},
{
version: "0.28.2",
date: "2026-07-14 14:45",
fixed: [
"Recipe cards on Explore and Search cramped the title next to the difficulty badge, cutting titles off early — the title now gets its own full-width line, and the grid gains a medium-viewport column step matching the Recipes page.",
],
},
{
version: "0.28.1",
date: "2026-07-14 14:25",
fixed: [
"Cooking mode's keyboard/voice shortcuts panel could overflow and overlap the surrounding screen in French (longer translated labels) — now scrolls internally and wraps instead of overflowing.",
],
},
{
version: "0.28.0",
date: "2026-07-14 14:05",
added: [
"**Meal plan and shopping list header buttons now match the recipe page's icon+tooltip style** — Share, Generate shopping list, Shopping lists, Print, Export to calendar, Send to grocery delivery all collapsed to icon-only with a tooltip, instead of crowding the header with text labels.",
"**Week-switch arrows on the meal plan now sit right next to the week date**, instead of at the far end of the button row.",
],
},
{
version: "0.27.2",
date: "2026-07-14 13:20",
fixed: [
"Dietary tag chips, the recipe tag-adding form, the \"Edit recipe\" title, and the meal-plan nutrition-goals bar were all hardcoded in English regardless of app language — now translated.",
],
},
{
version: "0.27.1",
date: "2026-07-14 12:05",
fixed: [
"API Reference (`/docs`) schemas corrected against the actual code across the whole API — recipes (batch-cook fields, tags, photos were undocumented), comments, ratings, meal plans, shopping lists, pantry, feed, and several missing error responses. A webhook-redelivery route also returned a malformed error body — now matches the rest of the API's `{ error: string }` convention.",
],
},
{
version: "0.27.0",
date: "2026-07-14 11:15",
added: [
"**API keys now work on recipes, collections, meal plans, shopping lists, pantry, feed, and AI generation** — previously most of the API only accepted a session cookie, so keys couldn't actually be used for most endpoints. Account/credential-adjacent routes (profile, AI provider keys, webhooks, notifications, messages, admin) intentionally stay session-only.",
],
},
{
version: "0.26.0",
date: "2026-07-14 10:20",
added: [
"**API Reference (`/docs`) now covers the full API**: went from 14 documented endpoints to all ~100 — pantry, meal plans, shopping lists, collections, users/social, AI generation, webhooks, conversations, notifications, and admin routes are now all documented with request/response schemas.",
],
},
{
version: "0.25.1",
date: "2026-07-14 09:40",
fixed: [
"API key \"Last used\" only showed a date — now shows date and time.",
],
},
{
version: "0.25.0",
date: "2026-07-14 09:36",
added: [
"**Gravatar is now opt-in, off by default** — previously every account without a custom photo automatically had its email hashed and sent to gravatar.com. Turn it on in Settings → Profile if you want it.",
],
},
{
version: "0.24.1",
date: "2026-07-14 09:24",
fixed: [
"My Recipes search result count showed a literal \"{count}\" instead of the number, in every locale.",
],
},
{
version: "0.24.0",
date: "2026-07-14 08:08",
added: [
"**My Recipes search now matches ingredients and tags too**, not just title/description — same improvement shipped for Explore/Search earlier.",
"**QR code on printed recipes**: a public or unlisted recipe's printout now includes a scannable code linking back to the online version.",
],
},
{
version: "0.23.0",
date: "2026-07-13 23:06",
added: [
"**Two-factor authentication**: turn it on in Settings → Security to require a code from an authenticator app when signing in, with backup codes in case you lose access to it.",
],
},
{
version: "0.22.0",
date: "2026-07-13 22:49",
added: [
"**QR code on printed shopping lists**: a public list's printout now includes a scannable code linking back to the live version — pairs with public-editable links, so someone at the store can scan and check items off in real time.",
],
},
{
version: "0.21.0",
date: "2026-07-13 22:34",
added: [
"**Rating stars on recipe cards** — average rating and review count now show on Search and Explore results, not just the recipe detail page.",
],
},
{
version: "0.20.0",
date: "2026-07-13 22:26",
added: [
"**Duplicate your own recipe** — same one-click copy used for forking others' recipes, now also on your own, for making variations without touching version history.",
],
},
{
version: "0.19.0",
date: "2026-07-13 22:21",
added: [
"**Export your meal plan to a calendar file** — download a week's meals as a .ics file to import into Google/Apple/Outlook calendar.",
],
},
{
version: "0.18.0",
date: "2026-07-13 22:16",
added: [
"**Live updates on shared meal plans**: entries added, removed, or cleared by a collaborator now appear automatically, both on the owner's own week view and everyone else's shared view.",
],
},
{
version: "0.17.0",
date: "2026-07-13 22:06",
added: [
"**Clear a day or the whole meal-plan week** in one click instead of removing each meal individually.",
],
},
{
version: "0.16.0",
date: "2026-07-13 21:59",
added: [
"**@mention autocomplete in comments**: type \"@\" and a name to search and insert a mention, instead of typing the exact username blind.",
],
},
{
version: "0.15.0",
date: "2026-07-13 21:54",
added: [
"**Search now matches ingredients and tags**, not just title/description — \"zaatar\" now finds a recipe that uses it even if the title doesn't mention it.",
"**Tag filter chips on Explore**: click a popular tag to narrow results instead of typing it.",
],
},
{
version: "0.14.0",
date: "2026-07-13 21:47",
added: [
"**Change your username** in Settings — previously auto-assigned at signup with no way to change it.",
],
},
{
version: "0.13.1",
date: "2026-07-13 17:25",
fixed: [
"People search never found anyone, for anyone — there was no signup or settings flow that ever set a username, and search (along with profiles and follow) requires one. Every account now gets one automatically on signup; existing accounts were backfilled.",
],
},
{
version: "0.13.0",
date: "2026-07-13 15:37",
added: [
"**Explore search now finds people and recipes together**: one search bar, one query — no more separate, hard-to-find people tab.",
],
},
{
version: "0.12.1",
date: "2026-07-13 13:25",
fixed: [
"Changelog entries showed literal `**bold**` markup instead of rendering it.",
],
},
{
version: "0.12.0",
date: "2026-07-13 12:52",
added: [
"**Public shopping list links can now allow editing**: turn on \"Allow editing\" alongside the public link so anyone who has it can check off, add, and reorder items — no account needed. Off by default, and turning off the public link revokes editing too.",
],
},
{
version: "0.11.0",
date: "2026-07-13 12:27",
added: [
"**Live updates on shared shopping lists**: items checked, added, recategorized, or reordered by a collaborator now appear automatically, without a manual refresh.",
],
},
{
version: "0.10.0",
date: "2026-07-13 11:55",
added: [
"**Bulk tag editing**: add or remove tags across several selected recipes at once.",
"**Bulk export**: export several selected recipes to a single Markdown file.",
"**Move recipes between collections**: select recipes inside a collection to move them to another collection or remove them from this one.",
],
},
{
version: "0.9.6",
date: "2026-07-13 09:31",
added: [
"**Rate limiting on public routes**: sign-in/sign-up now throttle after 3 attempts per 10s per IP; public recipe/shopping-list share links (`/r/`, `/s/`) throttle after 60 requests/min per IP.",
],
},
{
version: "0.9.5",
date: "2026-07-13 09:20",
fixed: [
"Explore page's \"Recipe ideas\" input got squeezed to a couple of visible characters on mobile — buttons now stay icon-only below `sm:`.",
"The per-recipe chat button could overlap the private-notes save button on short recipe pages.",
],
},
{
version: "0.9.4",
date: "2026-07-13 09:03",
fixed: [
"\"New recipe\" wrapped to its own line on mobile (especially French) instead of sharing a row with Generate/Import — icon-only below that breakpoint now.",
"The view-toggle/Select row floated flush right with a large dead gap on the left — now left-aligned outside select mode.",
],
},
{
version: "0.9.3",
date: "2026-07-13 08:38",
fixed: [
"Recipes page wasted a lot of vertical space above the recipe list on mobile — Sort/Filter now sit alongside Search on one row instead of forcing an extra one.",
],
},
{
version: "0.9.2",
date: "2026-07-13 07:14",
fixed: [
"Explore page's \"Recipe ideas\" banner buttons clashed with its violet gradient background.",
],
},
{
version: "0.9.1",
date: "2026-07-13 06:59",
fixed: [
"Destructive confirm buttons (clear chat history, discard recipe changes) had unreadable black text on a red background.",
],
},
{
version: "0.9.0",
date: "2026-07-12 23:28",
added: [
"Clear chat history: a trash-icon button in both chatbots clears that conversation's saved history.",
"Chat history now auto-expires after 90 days (daily cleanup job).",
],
},
{
version: "0.8.0",
date: "2026-07-12 22:38",
added: [
"Read-only API key scoping: create keys that can only read (GET), not create/edit/delete anything.",
"AI chat history: both chatbots now remember past conversations across sessions, and let you search across everything you've ever asked.",
],
},
{
version: "0.7.1",
date: "2026-07-12 21:36",
fixed: [
"Cooking-assistant button on the homepage sat too high.",
"The chatbots would answer honestly if asked what AI model they run on — now always identify as Epicure.",
],
},
{
version: "0.7.0",
date: "2026-07-12 19:36",
added: [
"General cooking-question chatbot on the recipes homepage — ask technique/substitution/timing questions not tied to a specific recipe.",
],
fixed: [
"API keys always showed \"never used\" — middleware was blocking every Bearer-key request before it reached the code that verifies the key and records usage.",
"The webhooks documentation page had no way back to webhooks settings.",
],
},
{
version: "0.6.0",
date: "2026-07-12 19:09",
added: [
"Granular push-notification settings: turn individual categories on/off instead of all-or-nothing.",
],
fixed: [
"The push-notification toggle in Settings always showed as off, even when notifications were genuinely still enabled.",
"The API Reference page (/docs) was blank — CSP blocked the script it needs to render.",
],
},
{
version: "0.5.1",
date: "2026-07-12 19:05",
security: [
"Recipe/review photo uploads: a stolen storage key from another user's public recipe or review could be submitted as your own, causing that user's photo to be deleted from storage when the recipe was later edited or deleted. Both routes now check the key was actually issued to you for that recipe.",
"AI URL-import could be redirected to internal/private network addresses via a crafted 3xx response, and was separately vulnerable to DNS-rebinding between validation and fetch. Both the import and outgoing webhook delivery now resolve the host once and pin the connection to that address, re-validating on every redirect hop.",
"Comment moderation and monthly AI/recipe/storage quota checks trusted a session field that can lag up to 5 minutes behind a role or tier change — both now re-check the current value in the database.",
],
},
{
version: "0.5.0",
date: "2026-07-12 18:37",
added: [
"Push notifications for shared shopping lists — get pinged when someone else checks off or adds items.",
],
fixed: [
"Batch-cook dishes never deducted pantry when marked cooked — now deducts once, on the first dish cooked per batch session.",
"Weekly meal-plan nutrition coverage compared a week's total intake against a daily goal (reading ~7x too high) — now compares a proper daily average, with a per-day breakdown and a flag for meals missing nutrition data.",
"Meal-plan week navigation could resolve the wrong Monday depending on server timezone.",
],
},
{
version: "0.4.0",
date: "2026-07-12 16:13",
added: [
"Profile pictures: upload a custom photo, or get a Gravatar-backed avatar automatically.",
"\"Imported from\" badge on recipe cards for recipes brought in from a URL.",
"Share shopping lists via a public link — no account required to view, alongside private collaborator invites.",
],
fixed: [
"Batch-cook recipes shifted left in compact view when they had no time set or a longer dish/time label.",
],
},
{
version: "0.3.1",
date: "2026-07-12 15:08",
added: [
"A free-text \"describe what you're after\" field for batch-cooking generation, matching the standard recipe generator.",
],
fixed: [
"AI recipe generation used a separate language picker instead of following the app's own language.",
"The bulk-select action bar on the recipe list could show a horizontal scrollbar on desktop even with room to grow.",
"Recipes imported from a URL never actually saved their source URL.",
],
},
{
version: "0.3.0",
date: "2026-07-12 14:46",
added: [
"Manually create and edit batch-cook recipes: a batch-cook toggle, an editable dish list with storage/reheat guidance, and per-step dish tagging in the recipe form — not just AI-generated ones anymore.",
],
},
{
version: "0.2.2",
date: "2026-07-12 13:54",
fixed: [
"Recipe photo thumbnails always tried to load from localhost:9000 in production, tripping CSP/mixed-content blocks in every browser — the public storage URL wasn't reaching client-side code.",
],
},
{
version: "0.2.1",
date: "2026-07-12 13:21",
fixed: [
"Recipe photos were lost after saving, and multi-file uploads only kept the last file selected.",
"Photo thumbnails failed to load — Next.js's image optimizer was refusing local-storage hosts.",
],
},
{
version: "0.2.0",
date: "2026-07-12",
added: [
"Batch cooking: generate a single \"mega recipe\" covering several meals at once — merged shopping list, steps tagged per dish, and per-dish storage/reheat guidance.",
"Leftover expiry reminders: mark a batch-cook dish as cooked, track its fridge life, and get a reminder before it goes bad.",
"\"Cooked it\" photo reviews: attach a photo and written review when rating a recipe.",
"Pantry-expiry recipe suggestions on the Pantry page.",
"Trending collections: star public collections, browse trending/recent under Collections → Discover.",
"Add to collection directly from the recipe list.",
"List and compact view modes for the recipe list.",
],
fixed: [
"Recipe version-comparison diff was unreadable and partially untranslated.",
"Print/export pages now always render light, regardless of app theme.",
"Presigned photo-upload URLs were signed against an internal-only address in production.",
"Recipe list/form UI issues: filter menu closing on every click, tag filter not accepting keystrokes, inconsistent badge styling, missing tooltips, floating save button on long forms.",
],
},
{
version: "0.1.0",
date: "earlier",
notes: "Initial feature set: recipes, meal planning, shopping lists, pantry tracking, collections, social features, nutrition tracking, admin dashboard, account/billing basics.",
},
];