fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: harden shop payment bridge - #668

Merged
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay
Aug 18, 2026
Merged

fix: harden shop payment bridge#668
jvsena42 merged 6 commits into
masterfrom
fix/shop-origin-and-pin-pay

Conversation

@ben-kaufman

@ben-kaufmanben-kaufman commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Description

Hardens the Bitrefill shop payment bridge while preserving the intentional QuickPay behavior shared with Android.

  1. Accept native shop messages only from the main frame at the exact https://embed.bitrefill.com origin and default HTTPS port.
  2. Make bridge injection idempotent and retain broader HTTPS Bitrefill navigation separately from the privileged payment origin.
  3. Block off-origin checkout navigation with user feedback without restricting the BTC Map caller.
  4. Accept only payment request scanner types from shop messages; setup, auth, channel, node, and gift payloads are rejected without clearing existing payment state.
  5. Keep QuickPay eligibility independent of payment-PIN settings, matching the accepted product behavior documented on Android PR 1158.

Companion Android PR: synonymdev/bitkit-android#1158

Linked Issues/Tasks

VulnHunter 01b finding 02 (shop WebView any-origin payment_intent). Finding 03 is accepted product behavior and is not changed here.

Screenshot / Video

N/A, security behavior only.

QA Notes

  • Shop → gift card → Bitrefill checkout → payment_intent: the appropriate Send flow opens with the invoice.
  • Shop checkout stays on HTTPS Bitrefill pages; an off-origin main-frame or window navigation is blocked with a warning.
  • A subframe or non-embed.bitrefill.com sender cannot invoke the native payment bridge.
  • A trusted shop message containing a non-payment scanner payload is rejected.
  • QuickPay remains available for an eligible small payment when enabled, including when payment-PIN settings are on.
  • Shop Discover → BTC Map continues to load.

Focused unit coverage: ShopOriginTests, ShopPaymentRequestTests, and PaymentNavigationHelperTests. Translation validation passes.

ben-kaufmanand others added 2 commits August 13, 2026 14:23
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@greptile-apps

Copy link
Copy Markdown

Greptile Summary

The PR restricts Bitrefill checkout messages and top-level navigation to trusted HTTPS origins and disables QuickPay when payment PIN protection is enabled.

  • Adds centralized Bitrefill host and message-origin validation.
  • Applies navigation restrictions through the shared shop WebView, inadvertently blocking the existing BTC Map caller.
  • Routes PIN-protected payments through confirmation and adds focused unit tests.

Confidence Score: 4/5

The PR should not merge until the Bitrefill-only navigation policy is scoped so that Shop Discover can still load BTC Map.

The shared WebView cancels Shop Discover's initial btcmap.org main-frame request because the new delegate allows only Bitrefill HTTPS origins.

Files Needing Attention: Bitkit/Components/ShopWebView.swift

Important Files Changed

FilenameOverview
Bitkit/Components/ShopWebView.swiftAdds origin checks for messages and navigation, but the unconditional Bitrefill policy breaks the shared WebView's BTC Map caller.
Bitkit/Utilities/ShopOrigin.swiftAdds HTTPS Bitrefill host validation and sender-origin filtering with appropriate apex and subdomain matching.
Bitkit/Utilities/PaymentNavigationHelper.swiftPrevents QuickPay selection when both PIN and payment-PIN protection are enabled.
Bitkit/Views/Shop/ShopMain.swiftRejects empty payment URIs before handing trusted checkout messages to payment parsing.
BitkitTests/ShopOriginTests.swiftCovers host and bridge-script validation but does not exercise the shared WebView navigation policy.
BitkitTests/PaymentNavigationHelperTests.swiftCovers every boolean combination of the new payment-PIN gate.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[ShopWebView initial navigation] --> B{Main frame?}
B -- No --> C[Allow subresource]
B -- Yes --> D{Bitrefill HTTPS origin?}
D -- Yes --> E[Allow checkout]
D -- No --> F[Cancel navigation]
G[Shop Discover: btcmap.org] --> A
F --> H[BTC Map does not load]
Loading

Reviews (1): Last reviewed commit: "fix: gate shop origin and pin pay" | Re-trigger Greptile

Comment threadBitkit/Components/ShopWebView.swift Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
@ovitrifovitrif mentioned this pull request Aug 16, 2026
4 tasks
ovitrif
ovitrif previously approved these changes Aug 16, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good. Shop checkout rejects payment_intent messages from non-Bitrefill origins, and QuickPay no longer bypasses PIN-for-payments.

@ovitrif

Copy link
Copy Markdown
Collaborator

Simulator QA on to.bitkit @ 9c215ea (iPhone 16, iOS 18.5). PIN 1234, QuickPay on, spending 97316 sats after a Blocktank transfer.

  • 1. Shop → Gift Cards → Amazon.fr €100 cart → Checkout (email step). Did not submit. Shop talks to real embed.bitrefill.com, so checkout would emit a mainnet invoice a regtest wallet cannot act on.
  • 2. Gift Cards → eSIMs, product, checkout, and Help stayed on Bitrefill. Privacy Policy stayed on Bitrefill (Bitrefill 404 page). Did not find a live off-Bitrefill main-frame URL to block.
  • 3. PIN on + PIN for payments on + QuickPay on → 500-sat LN invoice: Confirm opened, not QuickPay. Swipe To Pay asked for PIN.
  • 4. PIN on + PIN for payments off + QuickPay on → 600-sat LN invoice: QuickPay opened. The payment itself failed (Failed to send the given payment) because the invoice was on a different regtest than this wallet; the check was that QuickPay opened, not that it settled.

jvsena42
jvsena42 previously requested changes Aug 17, 2026
Comment threadBitkit/Components/ShopWebView.swift
@ben-kaufmanben-kaufman changed the title fix: gate shop origin and pin payfix: harden shop payment bridgeAug 17, 2026

@ovitrifovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. Shop checkout now accepts only main-frame payment requests from the trusted Bitrefill embed origin, and QuickPay remains independent of payment-PIN settings.

@jvsena42
jvsena42 merged commit 6f478eb into masterAug 18, 2026
11 checks passed
@jvsena42
jvsena42 deleted the fix/shop-origin-and-pin-pay branch August 18, 2026 10:17
@piotr-iohkpiotr-iohk mentioned this pull request Aug 21, 2026
Sign up for freeto 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

@ben-kaufman@ovitrif@jvsena42