Skip to content

fix: escape FinTS control characters in HNSHA/HNSHK/HNVSK segments + CVE-2026-26278#39

Merged
larsdecker merged 5 commits intomasterfrom
copilot/fix-signature-structure-errors
Apr 14, 2026
Merged

fix: escape FinTS control characters in HNSHA/HNSHK/HNVSK segments + CVE-2026-26278#39
larsdecker merged 5 commits intomasterfrom
copilot/fix-signature-structure-errors

Conversation

Copy link
Copy Markdown

Copilot AI commented Apr 14, 2026

Passwords or usernames containing FinTS control characters (@, +, :, ', ?) were serialized raw in three outbound segments. On ING, a PIN like abc@xyz causes the server to interpret @xyz as a binary-length prefix inside the HNVSD inner payload → error 9010 / dialog aborted (9800).

Escaping fixes

HKIDN already used Format.stringEscaped() correctly; the three affected segments did not:

Segment Field Before After
HNSHA pin, tan raw Format.stringEscaped()
HNSHK name raw Format.stringEscaped()
HNVSK name raw Format.stringEscaped()
// hnsha.ts — before
return [Format.num(secRef), Format.empty(), tan ? [pin, tan] : pin];

// after
return [Format.num(secRef), Format.empty(), tan ? [Format.stringEscaped(pin), Format.stringEscaped(tan)] : Format.stringEscaped(pin)];

Tests

Added serialization round-trip cases to all three test files covering @ and + in the affected fields, e.g. secret@passsecret?@pass, user@example.comuser?@example.com. HNVSK also exercises deserialization since parse() already unescapes ?@@.

Dependency

Bumped fast-xml-parser from ^5.3.3 to ^5.5.6 to address CVE-2026-26278 (numeric entity expansion bypassing entity expansion limits). The CLI has no direct dependency on fast-xml-parser and picks up the fix transitively through fints-lib.

Version

fints-lib bumped 0.8.00.9.0.

Copilot AI and others added 2 commits April 14, 2026 22:47
Passwords or usernames containing FinTS control characters (@ + : ' ?)
were serialized raw, causing error 9010 on ING servers.

- hnsha.ts: wrap pin and tan with Format.stringEscaped()
- hnshk.ts: wrap name with Format.stringEscaped()
- hnvsk.ts: wrap name with Format.stringEscaped()
- Add serialization tests with @ in pin/tan/name for all three segments
- Bump version to 0.9.0

Agent-Logs-Url: https://github.com/larsdecker/fints/sessions/41587821-3c4e-484b-b42f-a2514748f3f0

Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
@larsdecker
Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request

…re-structure-errors

# Conflicts:
#	yarn.lock

Co-authored-by: larsdecker <1968186+larsdecker@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Apr 14, 2026

@copilot resolve the merge conflicts in this pull request

Done — merged origin/master into this branch (commit 9b49b4c). The only conflict was in yarn.lock; it was resolved by taking master's lock file as the base and re-running npm install so our ^5.5.6 constraint is correctly reflected. All 309 tests pass.

Copilot AI requested a review from larsdecker April 14, 2026 22:58
@larsdecker larsdecker marked this pull request as ready for review April 14, 2026 22:59
Copilot AI review requested due to automatic review settings April 14, 2026 22:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes outbound FinTS serialization for credentials containing FinTS control characters in security-related segments (HNSHA/HNSHK/HNVSK) and updates fast-xml-parser to address CVE-2026-26278 in fints-lib.

Changes:

  • Escape FinTS control characters in HNSHA.pin/tan, HNSHK.name, and HNVSK.name using Format.stringEscaped().
  • Add segment serialization/deserialization test cases covering @ and + escaping.
  • Bump fast-xml-parser dependency (and update lockfile) and bump fints-lib version to 0.9.0.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
yarn.lock Lockfile updates for dependency bump(s); currently includes absolute-path workspace keys.
packages/fints/package.json Bumps fints-lib version and fast-xml-parser dependency range.
packages/fints/src/segments/hnsha.ts Escapes pin/tan during serialization.
packages/fints/src/segments/hnshk.ts Escapes name during serialization.
packages/fints/src/segments/hnvsk.ts Escapes name during serialization.
packages/fints/src/segments/tests/test-hnsha.ts Adds escaping coverage for PIN/TAN serialization.
packages/fints/src/segments/tests/test-hnshk.ts Adds escaping coverage for name serialization; updates date/time mocking.
packages/fints/src/segments/tests/test-hnvsk.ts Adds escaping + unescaping coverage for name; updates date/time mocking.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +6
jest.spyOn(Format, "date").mockReturnValue("20180907");
jest.spyOn(Format, "time").mockReturnValue("080000");
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jest.spyOn(Format, "date/time").mockReturnValue(...) is executed at module load and never restored. Since the package’s Jest config doesn’t enable restoreMocks, these mocks can leak into other test files in the same run. Add an afterAll/afterEach that restores mocks (or scope the spies inside a describe with proper teardown) so other tests aren’t affected.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +6
jest.spyOn(Format, "date").mockReturnValue("20180907");
jest.spyOn(Format, "time").mockReturnValue("080000");
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jest.spyOn(Format, "date/time").mockReturnValue(...) is executed at module load and never restored. Since the package’s Jest config doesn’t enable restoreMocks, these mocks can leak into other test files in the same run. Add an afterAll/afterEach that restores mocks (or scope the spies inside a describe with proper teardown) so other tests aren’t affected.

Copilot uses AI. Check for mistakes.
@larsdecker
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

Copy link
Copy Markdown
Author

Copilot AI commented Apr 14, 2026

@copilot apply changes based on the comments in this thread

Done in 4aad7be. Added afterAll(() => jest.restoreAllMocks()) at the top level of both test-hnshk.ts and test-hnvsk.ts so the Format.date/Format.time spies are restored after all tests in each file complete, preventing them from leaking into other test files.

@larsdecker larsdecker merged commit 3b63c82 into master Apr 14, 2026
5 checks passed
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.

3 participants