Escape Beancount strings at the add_account writer boundary, not just the metadata-value branch #52

Open
opened 2026-06-16 21:59:38 +00:00 by padreug · 0 comments
Owner

Found in the high-effort code review of PR #46.

_escape_beancount_string (added in PR #46) is applied only to the metadata value branch of add_account (fava_client.py). The account_name and metadata keys are written into the same Open directive source text unescaped.

Today this is latent, not exploitable: the admin endpoint validates account_name, and all callers pass system-controlled metadata keys (added_by, source, description, entry-id) and safe descriptions. But add_account is shared infrastructure (callers: the admin endpoint and crud.get_or_create_user_account), and its source-construction contract currently trusts every caller for the name and keys. A future caller — or user-derived text flowing into a name/key — would corrupt chart.beancount and 500 every subsequent /api/source write.

Suggested fix

Make the writer responsible for its own output: escape (or assert-valid) account_name and metadata keys where the directive text is assembled, so safety is a property of add_account rather than of each call site. Account names can't legally contain quotes, so an assert/validate is appropriate there; keys should be escaped or constrained to the metadata-key grammar.

Found in the high-effort code review of PR #46. `_escape_beancount_string` (added in PR #46) is applied only to the metadata **value** branch of `add_account` (`fava_client.py`). The `account_name` and metadata **keys** are written into the same Open directive source text unescaped. Today this is latent, not exploitable: the admin endpoint validates `account_name`, and all callers pass system-controlled metadata keys (`added_by`, `source`, `description`, `entry-id`) and safe descriptions. But `add_account` is **shared infrastructure** (callers: the admin endpoint and `crud.get_or_create_user_account`), and its source-construction contract currently trusts every caller for the name and keys. A future caller — or user-derived text flowing into a name/key — would corrupt `chart.beancount` and 500 every subsequent `/api/source` write. ### Suggested fix Make the writer responsible for its own output: escape (or assert-valid) `account_name` and metadata keys where the directive text is assembled, so safety is a property of `add_account` rather than of each call site. Account names can't legally contain quotes, so an assert/validate is appropriate there; keys should be escaped or constrained to the metadata-key grammar.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/libra#52
No description provided.