View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012064 | Taler | wallet (iOS App) | public | 2026-11-11 01:13 | 2026-11-11 01:13 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | minor | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012064: 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 | ||||
| Description | Settings -> 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. | ||||
| Tags | No tags attached. | ||||
| Attached Files | 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)
| ||||