mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-29 19:14:22 -05:00
* [AI] Add secure custom font support for custom themes
Implement safe font-family references in custom themes via CSS variables
(--font-body, --font-mono, --font-heading, etc.) validated against a
curated allowlist of system-installed and web-safe fonts.
Security approach: Only fonts already present on the user's OS or bundled
with the app are allowed. No @font-face, no url(), no external font
loading — this prevents third-party tracking via font requests while
still enabling meaningful font customization in themes.
Key changes:
- Add SAFE_FONT_FAMILIES allowlist (~80 fonts: generic families, bundled
fonts, and common system fonts across platforms)
- Add validateFontFamilyValue() for comma-separated font stack validation
- Route --font-{body,mono,heading,family,ui,display,code} properties
through the font validator instead of the color validator
- Update index.html to use var(--font-body, ...) with current Inter
Variable stack as fallback
- Add comprehensive tests for valid/invalid font values and security
edge cases (url injection, javascript:, expression(), etc.)
https://claude.ai/code/session_01D4ASLpcBCvWF1nzPLz9Tw5
* [AI] Add @font-face support with data: URI embedding for custom themes
Enable truly custom fonts in themes while maintaining zero runtime network
requests. Theme authors can include font files in their GitHub repos, and
fonts are automatically downloaded and embedded as data: URIs at install
time — the same approach used for theme CSS itself.
Security model:
- @font-face blocks only allow data: URIs (no http/https/relative URLs)
- Font MIME types are validated (font/woff2, font/ttf, etc.)
- Individual font files capped at 2MB, total at 10MB
- @font-face properties are allowlisted (font-family, src, font-weight,
font-style, font-display, font-stretch, unicode-range only)
- Font-family names from @font-face are available in --font-* variables
- No runtime network requests — all fonts stored locally after install
Key additions:
- extractFontFaceBlocks(): parse @font-face from theme CSS
- validateFontFaceBlock(): validate properties and data: URIs
- splitDeclarations(): semicolon-aware parser that respects data: URIs
- embedThemeFonts(): fetch font files from GitHub, convert to data: URIs
- ThemeInstaller calls embedThemeFonts() during catalog theme installation
- 30+ new test cases for @font-face validation and security edge cases
Example theme CSS with custom fonts:
@font-face {
font-family: 'My Font';
src: url('./MyFont.woff2') format('woff2');
}
:root { --font-body: 'My Font', sans-serif; }
https://claude.ai/code/session_01D4ASLpcBCvWF1nzPLz9Tw5
* [AI] Rename --font-body CSS variable to --font-family
https://claude.ai/code/session_01D4ASLpcBCvWF1nzPLz9Tw5
* [AI] Remove font-family allowlist and broaden --font-* regex
- Remove SAFE_FONT_FAMILIES allowlist and SAFE_FONT_FAMILIES_LOWER lookup.
Any font name is now valid in --font-* properties. Referencing a font
that isn't installed simply triggers the browser's normal fallback — no
network requests, no security risk. Function calls (url(), expression(),
etc.) are still blocked.
- Change the --font-* property regex from a specific list
(family|mono|heading|...) to match all --font-* variables, so theme
authors can use any --font-prefixed custom property.
https://claude.ai/code/session_01D4ASLpcBCvWF1nzPLz9Tw5
* [AI] Simplify and improve custom font validation code
Code quality improvements from review:
- Remove dead `declaredFonts` Set (was populated but never read after
allowlist removal)
- Extract `stripQuotes()` helper to deduplicate quote-stripping logic
between `validateFontFamilyValue` and `validateFontFaceBlock`
- Replace confusing `const searchFrom = 0` loop with `for (;;)` idiom
in `extractFontFaceBlocks`
- Use index tracking (`content.substring(start, i)`) instead of
character-by-character string concatenation in `splitDeclarations`
- Use `splitDeclarations` in `validateRootContent` instead of naive
`split(';')` for consistency and correctness
- Parallelize font fetches in `embedThemeFonts` with `Promise.all`
instead of sequential awaits
- Replace byte-by-byte base64 conversion with chunked
`arrayBufferToBase64()` helper (8KB chunks)
- Reuse indexOf-based @font-face parsing in `embedThemeFonts` instead
of fragile `[^}]*` regex that can't handle large data URIs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enhance font validation in customThemes.ts
* Add custom release notes for upcoming feature: support for custom fonts in themes
* [AI] Simplify @font-face validation to only block external URLs
Remove ~210 lines of overly thorough font validation (MIME type allowlists,
base64 encoding checks, format hint validation, @font-face property allowlists,
font-family name regex) and replace with a single function that enforces the
actual security goal: rejecting non-data: URIs to prevent external resource
loading. Size limits for DoS prevention are preserved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Update Content Security Policy to include font-src directive
Enhance the Content Security Policy in both the desktop client and sync server to allow font loading from data URIs. This change ensures that custom fonts can be embedded securely while maintaining the existing security measures for other resources.
* Enhance font-family validation to disallow empty values
Update the `validateFontFamilyValue` function to throw an error for empty font-family values, improving security and validation accuracy. Adjust tests to reflect this change, ensuring that empty values are properly handled as invalid.
* Enhance validation for CSS custom properties in customThemes.ts
Add comprehensive checks in the `validateRootContent` function to ensure CSS custom properties start with '--', contain valid characters, and do not end with a dash. This improves error handling for invalid property names, ensuring better compliance with CSS standards.
* [AI] Fix path traversal, spaces in font URLs, and add embedThemeFonts tests
Reject path-traversal (../) and root-anchored (/) font paths in
embedThemeFonts to prevent URL manipulation. Fix URL regex to handle
quoted filenames with spaces (e.g. "Inter Variable.woff2"). Add unit
tests covering both security validations and normal embedding flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Implement font size budget enforcement in embedThemeFonts function
* Add global unstubbing in afterEach for embedThemeFonts tests
---------
Co-authored-by: Claude <noreply@anthropic.com>
96 B
96 B
category, authors
| category | authors | |
|---|---|---|
| Enhancements |
|
Custom Themes: allow using a custom font