Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions modules/ensemble/lib/widget/fintech/tabapay_post_message.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'dart:convert';

/// Builds JavaScript that forwards `postMessage` payloads to the WebView
/// [messageHandler] channel only when `event.origin` matches the origin of
/// [pageUrl].
///
/// Returns `null` when [pageUrl] is not a valid absolute `http`/`https` URI so
/// callers can fail closed instead of installing an unrestricted listener.
String? buildTabaPayPostMessageListenerScript(String pageUrl) {
final uri = Uri.tryParse(pageUrl);
if (uri == null || !uri.hasScheme || !uri.hasAuthority) {
return null;
}
final scheme = uri.scheme.toLowerCase();
if (scheme != 'http' && scheme != 'https') {
return null;
}
final origin = uri.origin;
if (origin.isEmpty) {
return null;
}
final originLiteral = jsonEncode(origin);
return '''
window.addEventListener("message", function(event) {
if (event.origin !== $originLiteral) {
return;
}
messageHandler.postMessage(event.data);
});
''';
}
8 changes: 6 additions & 2 deletions modules/ensemble/lib/widget/fintech/tabapayconnect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:webview_flutter/webview_flutter.dart';
import '../../framework/action.dart';
import '../../screen_controller.dart';
import '../../util/utils.dart';
import 'tabapay_post_message.dart';

class TabaPayConnectController extends WidgetController {
String uri =
Expand Down Expand Up @@ -86,8 +87,11 @@ class TabaPayConnectState extends EWidgetState<TabaPayConnect> {
onMessageReceived: _handleTabaPayMessage)
..setNavigationDelegate(
NavigationDelegate(onPageFinished: (String url) {
_webViewController?.runJavaScript(
'window.addEventListener("message", (event) => messageHandler.postMessage(event.data))');
final listenerScript =
buildTabaPayPostMessageListenerScript(widget.controller.uri);
if (listenerScript != null) {
_webViewController?.runJavaScript(listenerScript);
}
}));
_webViewController?.loadRequest(Uri.parse(widget.controller.uri));
}
Expand Down
33 changes: 33 additions & 0 deletions modules/ensemble/test/tabapay_post_message_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:ensemble/widget/fintech/tabapay_post_message.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('buildTabaPayPostMessageListenerScript', () {
test('includes origin check for valid https iframe URL', () {
final script = buildTabaPayPostMessageListenerScript(
'https://iframe.tabapay.com/frame',
);
expect(script, isNotNull);
expect(script, contains('"https://iframe.tabapay.com"'));
expect(script, contains('event.origin !=='));
expect(script, contains('messageHandler.postMessage(event.data)'));
});

test('returns null for invalid or non-http(s) URLs', () {
expect(buildTabaPayPostMessageListenerScript(''), isNull);
expect(buildTabaPayPostMessageListenerScript('not-a-url'), isNull);
expect(
buildTabaPayPostMessageListenerScript('javascript:alert(1)'),
isNull,
);
expect(buildTabaPayPostMessageListenerScript('file:///etc/passwd'), isNull);
});

test('JSON-encodes origin literal for safe embedding in JavaScript', () {
final script = buildTabaPayPostMessageListenerScript(
'https://pay.example.com/frame',
);
expect(script, contains('"https://pay.example.com"'));
});
});
}
Loading