Skip to content

fix: use binary encoding for LastModificationTime in KDBX 4.1 (custom data + custom icons)#62

Open
th3nu11 wants to merge 5 commits into
keeweb:masterfrom
intresrl:kdbx41-lastmodtime-fix
Open

fix: use binary encoding for LastModificationTime in KDBX 4.1 (custom data + custom icons)#62
th3nu11 wants to merge 5 commits into
keeweb:masterfrom
intresrl:kdbx41-lastmodtime-fix

Conversation

@th3nu11
Copy link
Copy Markdown

@th3nu11 th3nu11 commented May 8, 2026

Problem

KdbxCustomData.write() and KdbxMeta.writeCustomIcons() call XmlUtils.setDate() without the binary=true flag when writing LastModificationTime for KDBX 4.1 databases.

This causes the timestamp to be written as an ISO-8601 string (e.g. 2021-01-20T18:10:44.000Z) instead of the 8-byte little-endian Base64 binary that KDBX 4+ requires per spec.

Originally reported in #49 as causing KeePass / KeePassXC to reject saved files with "Invalid Base-64 string". In our testing with KeePassXC 2.x (current releases), the app appears to have since added a fallback that accepts both formats — so the real-world breakage may be limited to older client versions or the original KeePass on Windows. The fix is still correct: it brings the output into spec compliance regardless.

Affected fields:

  • CustomData items — KdbxCustomData.write()
  • CustomIcons items — KdbxMeta.writeCustomIcons()

Fix

Add , true as the third argument to XmlUtils.setDate() in both affected locations:

  • lib/format/kdbx-custom-data.ts
  • lib/format/kdbx-meta.ts

Tests

New tests added for both locations (AAA pattern):

  • unit: asserts the written value matches Base64 regex and does not match an ISO date regex
  • roundtrip: write → read back → date equals original
  • KDBX 4.0: asserts LastModificationTime is not written at all for older versions
  • integration (kdbx-custom-data): loads the real KDBX4.1.kdbx test file, writes its custom data in binary-save mode, and asserts all timestamps are Base64

Before the fix: 1 test fails. After the fix: all pass.

Relation to #50

Supersedes #50 — same one-line fix for kdbx-custom-data.ts, plus the identical fix for kdbx-meta.ts (custom icons), plus test coverage.

carlo.ballabio added 5 commits May 8, 2026 16:46
KdbxCustomData.write() calls XmlUtils.setDate() without the binary=true
flag for KDBX 4.1 custom data items. This causes LastModificationTime to
be written as an ISO string instead of Base64 binary, which KeePass and
KeePassXC reject with "Invalid Base-64 string".

Tests added:
- unit: verifies LastModificationTime is Base64 (not ISO) in binary save
- unit: roundtrip write/read preserves lastModified date for KDBX 4.1
- unit: KDBX 4.0 does not write LastModificationTime at all
- integration: loads real KDBX 4.1 file and confirms the corruption

All new tests currently fail as expected (bug present).
…data

XmlUtils.setDate() requires a third argument `true` to write the
8-byte little-endian Base64 timestamp that KDBX 4+ expects.
Without it the date was written as an ISO string, which KeePass and
KeePassXC reject as "Invalid Base-64 string".

Fixes keeweb#49.
…ncoding

KdbxMeta.writeCustomIcons() has the same bug as KdbxCustomData.write():
XmlUtils.setDate() is called without binary=true, so for KDBX 4.1 the
LastModificationTime is written as an ISO string instead of Base64.

The unit test fails until the fix is applied.
…icons

Same bug as kdbx-custom-data.ts: XmlUtils.setDate() called without
binary=true in writeCustomIcons(), writing an ISO string instead of the
8-byte Base64 timestamp that KDBX 4+ requires.
- mocha upgraded 9 → 11 (fixes nanoid/serialize-javascript CVEs in test env)
- TypeScript pinned at ~5.7 (5.9 introduces breaking Uint8Array<ArrayBufferLike>
  changes across the whole codebase — deferred to a dedicated refactor)
- All other in-range deps updated via npm update
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant