View Issue Details

IDProjectCategoryView StatusLast Update
0012064Talerwallet (iOS App)public2026-11-11 01:13
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Summary0012064: Add bank account (X-TALER-BANK): a plain account name is accepted and then answered with "Internal core error" -- the form never asks for the BANK-HOST/ACCOUNT target it needs
DescriptionSettings -> Bank Accounts -> "+" -> X-TALER-BANK: typing an ordinary account
name and pressing Add ends on the generic error page, titled "Internal core
error", with wallet-core's JSON as the body (screenshot attached):

    {
      "hint" : "payto URI is malformed (COMPONENTS_LENGTH)",
      "when" : { "t_s" : 1794359333 },
      "code" : 24
    }

Nothing internal went wrong. The form accepted input it should not have
accepted, and then reported its own omission as a fault in the core.

Cause
-----

TalerWallet1/Views/Settings/Bank/BankEditView.swift, updateAccount():

    components.host = isIBAN ? "iban" : "x-taler-bank"
    let target = isIBAN ? iban.filter { !$0.isWhitespace }.uppercased() : xTaler
    components.path = "/" + target

An x-taler-bank target is BANK-HOST/ACCOUNT (RFC 8905), and wallet-core's
parser requires at least two path components (taler-util payto.ts,
`case PaytoType.TalerBank: if (cs.length < 2) -> COMPONENTS_LENGTH`). The
form has a single field labelled "X-TALER-BANK:", with no placeholder or
hint saying that a host is wanted, and the Add button's only check for this
method is `xTaler.isEmpty`. So the natural input -- the account name -- is
accepted, sent as `payto://x-taler-bank/<name>?receiver-name=...`, and
rejected.

The source already knows: the same function carries a TODO, "for
x-taler-bank the target must be BANK-HOST/ACCOUNT ... there is still no
separate input field for it, so a hand-typed account without a host
produces an incomplete target." What it does not say is what the user then
sees, which is this report.

The IBAN branch of the same form does this properly: `validateIban` runs on
every change and Add stays disabled until it passes.

Steps to reproduce (fresh install, no fault injected)
-----------------------------------------------------

1. Settings -> Bank Accounts -> "+"
2. Select X-TALER-BANK in the segmented control
3. Label: harness-bank; Account holder: Fuzz Harness
4. X-TALER-BANK: harness-deposit
5. Add -> "Internal core error"

wallet-core log of that run:

    addBankAccount{"label":"harness-bank","paytoUri":"payto://x-taler-bank/harness-deposit?receiver-name=Fuzz%20Harness"}
    addBankAccount with error: { "code": 24, ..., "hint": "payto URI is malformed (COMPONENTS_LENGTH)" }

First seen in a UI fuzzing episode (c2h-20-e00026, 2026-11-10), where the
field held a hostile string and a latency fault was active; reproduced
2026-11-11 as a deterministic UI test with a plain account name and no
fault, two runs out of two (harness/scenarios/candidate/
bank-add-xtaler-no-host.json on the fuzzing branch), so neither the string
nor the fault matters.

Proposed fix (patch attached, xtaler-bank-target-validation.diff)
-----------------------------------------------------------------

Validate the target in the form: at least two non-empty, whitespace-free
components. Add stays disabled until it holds, a one-line hint appears
under the field while it does not ("Enter the account as
BANK-HOST/ACCOUNT, for example bank.example.com/alice"), and
updateAccount() applies the same check. Autocapitalisation and
autocorrection are switched off for the field, since both corrupt a host
name. One file, against master.

Status of the patch: it applies to master and the GNU_Taler scheme BUILDS
with it (Xcode 27.2, iOS Simulator, 2026-11-11). It has NOT been run on a
device -- the only simulator lane here is occupied by a coverage campaign
whose binary must stay unchanged -- so the hint's layout is unverified. A
separate host field would be the better design; this is the smallest change
that stops the error page.

Severity: minor. No data is lost and IBAN accounts are unaffected, but as
shipped an x-taler-bank account cannot be added by anyone who does not
already know the payto syntax, and the message blames the wrong party.
Related: 11934 (expected wallet-core errors titled "Internal core error")
is the display half of this; 11963 is the same form's IBAN branch.
TagsNo tags attached.
Attached Files
12064-internal-core-error.png (61,957 bytes)   
12064-internal-core-error.png (61,957 bytes)   
12064-form-before-add.png (49,300 bytes)   
12064-form-before-add.png (49,300 bytes)   
12064-xtaler-bank-target-validation.diff (3,441 bytes)   
diff --git a/TalerWallet1/Views/Settings/Bank/BankEditView.swift b/TalerWallet1/Views/Settings/Bank/BankEditView.swift
index c77b9f01..43c055b1 100755
--- a/TalerWallet1/Views/Settings/Bank/BankEditView.swift
+++ b/TalerWallet1/Views/Settings/Bank/BankEditView.swift
@@ -59,6 +59,16 @@ struct BankEditView: View {
 //        }
 //    }
 
+    /// An x-taler-bank target is BANK-HOST/ACCOUNT (RFC 8905). wallet-core rejects
+    /// anything with fewer than two components as
+    /// "payto URI is malformed (COMPONENTS_LENGTH)", which reached the user as
+    /// "Internal core error" with the raw JSON. Check it here instead.
+    private var xTalerValid: Bool {
+        let parts = xTaler.split(separator: "/", omittingEmptySubsequences: false)
+        return parts.count >= 2
+            && parts.allSatisfy { !$0.isEmpty && !$0.contains(where: { $0.isWhitespace }) }
+    }
+
     @ViewBuilder func barButton() -> some View {
         let isNew = accountID == nil
         let titleStr = isNew ? String(localized: "Add", comment: "button title: Add bank account")
@@ -66,7 +76,7 @@ struct BankEditView: View {
         let a11yStr = isNew ? String(localized: "Add bank account", comment: "a11y")
                             : String(localized: "Update bank account", comment: "a11y")
         let notValid: Bool = paytoType == .iban ? (iban.isEmpty || !ibanValid)
-                                                : xTaler.isEmpty
+                                                : !xTalerValid
         DoneButton(titleStr: titleStr, accessibilityLabelStr: titleStr) {
             Task {
                 if await updateAccount() {
@@ -105,7 +115,7 @@ struct BankEditView: View {
         let isIBAN = paytoType == .iban
         if !accountHolder.isEmpty && !accountLabel.isEmpty {
             if isIBAN ? !iban.isEmpty
-                      : !xTaler.isEmpty {
+                      : xTalerValid {
                 // Build the payto URI with URLComponents so each component is escaped as a
                 // component. The previous code percent-encoded the ALREADY-ASSEMBLED string
                 // with .urlQueryAllowed, which leaves "?", "&" and "=" untouched — an
@@ -266,12 +276,21 @@ struct BankEditView: View {
                         .foregroundColor(WalletColors().fieldForeground)     // text color
                         .background(WalletColors().fieldBackground)
                         .textFieldStyle(.roundedBorder)
-                        .padding(.bottom)
+                        .autocapitalization(.none)
+                        .disableAutocorrection(true)
+                        .padding(.bottom, xTaler.isEmpty || xTalerValid ? nil : 0)
                         .onChange(of: xTaler) { newValue in
                             if userTyped {
                                 unchanged = false
                             }
                         }
+                    if !xTaler.isEmpty && !xTalerValid {
+                        Text("Enter the account as BANK-HOST/ACCOUNT, for example bank.example.com/alice",
+                             comment: "hint below the x-taler-bank field")
+                            .talerFont(.body)
+                            .foregroundColor(.red)
+                            .padding(.bottom)
+                    }
                 } else {
                     Text("unknown payment method")
                         .talerFont(.title3)

Required notifications

Address or linkRemarkAdded byNotifiedActions

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:13bot-cgNew Issue
2026-11-11 01:13bot-cgFile Added: 12064-internal-core-error.png
2026-11-11 01:13bot-cgFile Added: 12064-form-before-add.png
2026-11-11 01:13bot-cgFile Added: 12064-xtaler-bank-target-validation.diff