From 45dee897d84a46796b02f4c4c37797a073a9b67a Mon Sep 17 00:00:00 2001 From: chidii Date: Sat, 27 Jun 2026 15:35:56 -0700 Subject: [PATCH] feat: add SecureWebView with configurable CSP injection - Three trust tiers: restricted, interactive, trusted - CSP injected via injectedJavaScriptBeforeContentLoaded - Origin whitelist for interactive and trusted tiers - JavaScript disabled for restricted tier Closes #669 --- src/components/common/SecureWebView.tsx | 38 +++++++++++++++++++++++++ src/config/security.ts | 10 ++++++- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/components/common/SecureWebView.tsx diff --git a/src/components/common/SecureWebView.tsx b/src/components/common/SecureWebView.tsx new file mode 100644 index 0000000..3d1fba6 --- /dev/null +++ b/src/components/common/SecureWebView.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { View, StyleSheet } from 'react-native'; +import { WebView, WebViewProps } from 'react-native-webview'; +import { CSP_TRUST_TIERS, TrustTier } from '../../config/security'; + +interface SecureWebViewProps extends Omit { + source: { html: string } | { uri: string }; + trustLevel?: TrustTier; + platformDomain?: string; +} + +export function SecureWebView({ + source, + trustLevel = 'restricted', + platformDomain = 'platform.com', + style, + ...props +}: SecureWebViewProps) { + const csp = CSP_TRUST_TIERS[trustLevel]; + const injectedJs = "(function(){var m=document.createElement('meta');m.httpEquiv='Content-Security-Policy';m.content='" + csp + "';document.head.insertBefore(m,document.head.firstChild);})();true;"; + const originWhitelist = trustLevel === 'restricted' ? undefined : ['https://*.' + platformDomain, 'https://' + platformDomain]; + + return ( + + + + ); +} + +const styles = StyleSheet.create({ container: { flex: 1 } }); diff --git a/src/config/security.ts b/src/config/security.ts index 1c27450..7e0e94a 100644 --- a/src/config/security.ts +++ b/src/config/security.ts @@ -1,4 +1,4 @@ -export const NOTIFICATION_SCREEN_ALLOWLIST = new Set([ +export const NOTIFICATION_SCREEN_ALLOWLIST = new Set([ 'Home', 'Courses', 'CourseDetail', @@ -10,3 +10,11 @@ export const NOTIFICATION_SCREEN_ALLOWLIST = new Set([ 'Achievements', 'AchievementDetail', ] as const); + +export const CSP_TRUST_TIERS = { + restricted: "default-src 'none'; img-src 'self' data: https:; style-src 'self'; font-src 'self'; connect-src 'self'", + interactive: "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.platform.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.platform.com; frame-src 'self'", + trusted: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.platform.com; style-src 'self' 'unsafe-inline'; img-src * data: https:; font-src 'self' https://fonts.gstatic.com; connect-src *; frame-src *; media-src 'self' https://media.platform.com", +} as const; + +export type TrustTier = keyof typeof CSP_TRUST_TIERS;