feat(accounts): validate account-name characters server-side

The endpoint only checked the root prefix, so a direct API call (bypassing
the UI) could write a malformed Open directive into the ledger source.
Add _validate_account_name mirroring Beancount's core/account.py grammar
(root [\p{Lu}][\p{L}\p{Nd}-]*, sub [\p{Lu}\p{Nd}][\p{L}\p{Nd}-]*, >=1
sub-account) — verified to match beancount.core.account.is_valid across
20 cases incl. Unicode, digit-start subs, hyphens. Align the client
segment regex to the same rule (was ASCII-only, rejected valid names).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-06-16 22:55:45 +02:00
commit cd5a6edb7d
2 changed files with 52 additions and 3 deletions

View file

@ -596,14 +596,15 @@ window.app = Vue.createApp({
return
}
// Each segment under the root must be a valid Beancount account
// component: start with an uppercase letter, then letters/digits/hyphens.
// component (core/account.py ACC_COMP_NAME_RE): starts with an uppercase
// letter or digit, then letters/digits/hyphens (Unicode letters allowed).
const badSegment = name.split(':').slice(1).find(
seg => !/^[A-Z][A-Za-z0-9-]*$/.test(seg)
seg => !/^[\p{Lu}\p{Nd}][\p{L}\p{Nd}-]*$/u.test(seg)
)
if (badSegment !== undefined) {
this.$q.notify({
type: 'warning',
message: `Invalid segment "${badSegment}" — each part must start with a capital letter (letters, digits, hyphens only)`
message: `Invalid segment "${badSegment}" — letters, digits and hyphens only, starting with a capital letter or digit`
})
return
}