From e5a1c81fe81904dfd7e3f5100a04088173effc1c Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Fri, 23 Jun 2023 09:00:51 -0700 Subject: [PATCH 1/6] fix(ios): set cors headers in asset handler for live reload --- .../Capacitor/CAPBridgeViewController.swift | 1 + ios/Capacitor/Capacitor/CAPWebView.swift | 1 + ios/Capacitor/Capacitor/WebViewAssetHandler.swift | 12 ++++++++++++ 3 files changed, 14 insertions(+) diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index f08e53a975..56f243e1a3 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -44,6 +44,7 @@ import Cordova // get the web view let assetHandler = WebViewAssetHandler(router: router()) assetHandler.setAssetPath(configuration.appLocation.path) + assetHandler.setServerUrl(configuration.serverURL) let delegationHandler = WebViewDelegationHandler() prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler) view = webView diff --git a/ios/Capacitor/Capacitor/CAPWebView.swift b/ios/Capacitor/Capacitor/CAPWebView.swift index dde6dda813..244a169380 100644 --- a/ios/Capacitor/Capacitor/CAPWebView.swift +++ b/ios/Capacitor/Capacitor/CAPWebView.swift @@ -28,6 +28,7 @@ open class CAPWebView: UIView { private lazy var assetHandler: WebViewAssetHandler = { let handler = WebViewAssetHandler(router: router) handler.setAssetPath(configuration.appLocation.path) + handler.setServerUrl(bridge.config.serverURL) return handler }() diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index 6dce404ef9..187ff360c7 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -5,6 +5,7 @@ import MobileCoreServices // swiftlint:disable type_body_length internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { private var router: Router + private var serverUrl: URL? init(router: Router) { self.router = router @@ -15,6 +16,10 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { router.basePath = assetPath } + func setServerUrl(_ serverUrl: URL?) { + self.serverUrl = serverUrl + } + func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { let startPath: String let url = urlSchemeTask.request.url! @@ -36,6 +41,13 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { "Content-Type": mimeType, "Cache-Control": "no-cache" ] + + // if using live reload, then set CORS headers + if self.serverUrl != nil && self.serverUrl?.scheme != localUrl.scheme { + headers["Access-Control-Allow-Origin"] = self.serverUrl?.absoluteString + headers["Access-Control-Allow-Methods"] = "GET, OPTIONS" + } + if let rangeString = urlSchemeTask.request.value(forHTTPHeaderField: "Range"), let totalSize = try fileUrl.resourceValues(forKeys: [.fileSizeKey]).fileSize, isMediaExtension(pathExtension: url.pathExtension) { From d7856de62a4c058ac474ae91a5fd221dabf99c0a Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Mon, 26 Jun 2023 17:14:42 +0200 Subject: [PATCH 2/6] fix(ios): Return proper MIME Type for local WASM files (#6675) --- ios/Capacitor/Capacitor/WebViewAssetHandler.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift index 187ff360c7..e67b14260b 100644 --- a/ios/Capacitor/Capacitor/WebViewAssetHandler.swift +++ b/ios/Capacitor/Capacitor/WebViewAssetHandler.swift @@ -409,6 +409,7 @@ internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler { "vsw": "application/vnd.visio", "vsx": "application/vnd.visio", "vtx": "application/vnd.visio", + "wasm": "application/wasm", "wav": "audio/wav", "wax": "audio/x-ms-wax", "wbmp": "image/vnd.wap.wbmp", From 9818a76ab4ea6660b444354f239344d37c77d3b3 Mon Sep 17 00:00:00 2001 From: Youri van Mill Date: Mon, 26 Jun 2023 20:20:22 +0200 Subject: [PATCH 3/6] feat(cli): add apksigner as a build option (#6442) Co-authored-by: Mark Anderson --- cli/src/android/build.ts | 78 ++++++++++++++++++++++++++++++++++------ cli/src/config.ts | 3 -- cli/src/declarations.ts | 8 +++++ cli/src/definitions.ts | 1 + cli/src/index.ts | 8 +++++ cli/src/tasks/build.ts | 5 +++ 6 files changed, 89 insertions(+), 14 deletions(-) diff --git a/cli/src/android/build.ts b/cli/src/android/build.ts index 79503501b3..73c31ebf6f 100644 --- a/cli/src/android/build.ts +++ b/cli/src/android/build.ts @@ -19,15 +19,6 @@ export async function buildAndroid( : `assemble${flavor}Release`; const gradleArgs = [arg]; - if ( - !buildOptions.keystorepath || - !buildOptions.keystorealias || - !buildOptions.keystorealiaspass || - !buildOptions.keystorepass - ) { - throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)'; - } - try { await runTask('Running Gradle build', async () => runCommand('./gradlew', gradleArgs, { @@ -63,6 +54,73 @@ export async function buildAndroid( `-release-signed.${releaseType.toLowerCase()}`, ); + if (buildOptions.signingtype == 'jarsigner') { + await signWithJarSigner( + config, + buildOptions, + releasePath, + signedReleaseName, + unsignedReleaseName, + ); + } else { + await signWithApkSigner( + config, + buildOptions, + releasePath, + signedReleaseName, + unsignedReleaseName, + ); + } + + logSuccess(`Successfully generated ${signedReleaseName} at: ${releasePath}`); +} + +async function signWithApkSigner( + config: Config, + buildOptions: BuildCommandOptions, + releasePath: string, + signedReleaseName: string, + unsignedReleaseName: string, +) { + if (!buildOptions.keystorepath || !buildOptions.keystorepass) { + throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password)'; + } + + const signingArgs = [ + 'sign', + '--ks', + buildOptions.keystorepath, + '--ks-pass', + `pass:${buildOptions.keystorepass}`, + '--in', + `${join(releasePath, unsignedReleaseName)}`, + '--out', + `${join(releasePath, signedReleaseName)}`, + ]; + + await runTask('Signing Release', async () => { + await runCommand('apksigner', signingArgs, { + cwd: config.android.platformDirAbs, + }); + }); +} + +async function signWithJarSigner( + config: Config, + buildOptions: BuildCommandOptions, + releasePath: string, + signedReleaseName: string, + unsignedReleaseName: string, +) { + if ( + !buildOptions.keystorepath || + !buildOptions.keystorealias || + !buildOptions.keystorealiaspass || + !buildOptions.keystorepass + ) { + throw 'Missing options. Please supply all options for android signing. (Keystore Path, Keystore Password, Keystore Key Alias, Keystore Key Password)'; + } + const signingArgs = [ '-sigalg', 'SHA1withRSA', @@ -85,6 +143,4 @@ export async function buildAndroid( cwd: config.android.platformDirAbs, }); }); - - logSuccess(`Successfully generated ${signedReleaseName} at: ${releasePath}`); } diff --git a/cli/src/config.ts b/cli/src/config.ts index 7e13e10ce6..670be52550 100644 --- a/cli/src/config.ts +++ b/cli/src/config.ts @@ -239,9 +239,6 @@ async function loadAndroidConfig( const buildOptions = { keystorePath: extConfig.android?.buildOptions?.keystorePath, keystorePassword: extConfig.android?.buildOptions?.keystorePassword, - keystoreAlias: extConfig.android?.buildOptions?.keystoreAlias, - keystoreAliasPassword: - extConfig.android?.buildOptions?.keystoreAliasPassword, releaseType: extConfig.android?.buildOptions?.releaseType, }; diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index e3e48f64e1..7f123e30af 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -259,6 +259,14 @@ export interface CapacitorConfig { * @default "AAB" */ releaseType?: 'AAB' | 'APK'; + + /** + * Program to sign your build with + * + * @since 5.1.0 + * @default "jarsigner" + */ + signingType?: 'apksigner' | 'jarsigner'; }; /** diff --git a/cli/src/definitions.ts b/cli/src/definitions.ts index c507216605..8ac32b892f 100644 --- a/cli/src/definitions.ts +++ b/cli/src/definitions.ts @@ -103,6 +103,7 @@ export interface AndroidConfig extends PlatformConfig { keystoreAlias?: string; keystoreAliasPassword?: string; releaseType?: 'AAB' | 'APK'; + signingType?: 'apksigner' | 'jarsigner'; }; } diff --git a/cli/src/index.ts b/cli/src/index.ts index 608f12c0dc..9385aee565 100644 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -155,6 +155,12 @@ export function runProgram(config: Config): void { 'Android release type; APK or AAB', ).choices(['AAB', 'APK']), ) + .addOption( + new Option( + '--signing-type ', + 'Program used to sign apps (default: jarsigner)', + ).choices(['apksigner', 'jarsigner']), + ) .action( wrapAction( telemetryAction( @@ -168,6 +174,7 @@ export function runProgram(config: Config): void { keystorealias, keystorealiaspass, androidreleasetype, + signingtype, }, ) => { const { buildCommand } = await import('./tasks/build'); @@ -178,6 +185,7 @@ export function runProgram(config: Config): void { keystorealias, keystorealiaspass, androidreleasetype, + signingtype, }); }, ), diff --git a/cli/src/tasks/build.ts b/cli/src/tasks/build.ts index 9a108c9e0c..22d6268398 100644 --- a/cli/src/tasks/build.ts +++ b/cli/src/tasks/build.ts @@ -12,6 +12,7 @@ export interface BuildCommandOptions { keystorealias?: string; keystorealiaspass?: string; androidreleasetype?: 'AAB' | 'APK'; + signingtype?: 'apksigner' | 'jarsigner'; } export async function buildCommand( @@ -46,6 +47,10 @@ export async function buildCommand( buildOptions.androidreleasetype || config.android.buildOptions.releaseType || 'AAB', + signingtype: + buildOptions.signingtype || + config.android.buildOptions.signingType || + 'jarsigner', }; try { From 2ab6c7dfb751600526134db331f13875a64338c3 Mon Sep 17 00:00:00 2001 From: Ionitron Date: Tue, 27 Jun 2023 03:52:30 -0400 Subject: [PATCH 4/6] chore(readme): add new contributor (#6677) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0f4165ab3e..ca62d0b2da 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Made possible by the Capacitor community. 💖

+ From 7f5f0ca4220d40d6a19c778c18f9534ef3b65899 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 28 Jun 2023 10:29:33 +0200 Subject: [PATCH 5/6] fix(android): Move bridge localUrl initialization to initWebView (#6685) --- .../main/java/com/getcapacitor/Bridge.java | 72 +++++++++---------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 532afadb6e..64c769f9a2 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -104,6 +104,7 @@ public class Bridge { private String appUrlConfig; private HostMask appAllowNavigationMask; private Set allowedOriginRules = new HashSet(); + private ArrayList authorities = new ArrayList<>(); // A reference to the main WebView for the app private final WebView webView; public final MockCordovaInterfaceImpl cordovaInterface; @@ -208,7 +209,6 @@ private Bridge( // Grab any intent info that our app was launched with Intent intent = context.getIntent(); this.intentUri = intent.getData(); - // Register our core plugins this.registerAllPlugins(); @@ -231,7 +231,9 @@ private void setAllowedOriginRules() { allowedOriginRules.add(allowNavigation); } } + authorities.addAll(Arrays.asList(appAllowNavigationConfig)); } + this.appAllowNavigationMask = HostMask.Parser.parse(appAllowNavigationConfig); } public App getApp() { @@ -239,43 +241,6 @@ public App getApp() { } private void loadWebView() { - appUrlConfig = this.getServerUrl(); - String[] appAllowNavigationConfig = this.config.getAllowNavigation(); - - ArrayList authorities = new ArrayList<>(); - - if (appAllowNavigationConfig != null) { - authorities.addAll(Arrays.asList(appAllowNavigationConfig)); - } - this.appAllowNavigationMask = HostMask.Parser.parse(appAllowNavigationConfig); - String authority = this.getHost(); - authorities.add(authority); - String scheme = this.getScheme(); - - localUrl = scheme + "://" + authority; - - if (appUrlConfig != null) { - try { - URL appUrlObject = new URL(appUrlConfig); - authorities.add(appUrlObject.getAuthority()); - } catch (Exception ex) { - Logger.error("Provided server url is invalid: " + ex.getMessage()); - return; - } - localUrl = appUrlConfig; - appUrl = appUrlConfig; - } else { - appUrl = localUrl; - // custom URL schemes requires path ending with / - if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) { - appUrl += "/"; - } - } - - String appUrlPath = this.config.getStartPath(); - if (appUrlPath != null && !appUrlPath.trim().isEmpty()) { - appUrl += appUrlPath; - } final boolean html5mode = this.config.isHTML5Mode(); // Start the local web server @@ -295,7 +260,6 @@ private void loadWebView() { setServerBasePath(path); } } - if (!this.isMinimumWebViewInstalled()) { String errorUrl = this.getErrorUrl(); if (errorUrl != null) { @@ -586,6 +550,36 @@ private void initWebView() { } WebView.setWebContentsDebuggingEnabled(this.config.isWebContentsDebuggingEnabled()); + + appUrlConfig = this.getServerUrl(); + String authority = this.getHost(); + authorities.add(authority); + String scheme = this.getScheme(); + + localUrl = scheme + "://" + authority; + + if (appUrlConfig != null) { + try { + URL appUrlObject = new URL(appUrlConfig); + authorities.add(appUrlObject.getAuthority()); + } catch (Exception ex) { + Logger.error("Provided server url is invalid: " + ex.getMessage()); + return; + } + localUrl = appUrlConfig; + appUrl = appUrlConfig; + } else { + appUrl = localUrl; + // custom URL schemes requires path ending with / + if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) { + appUrl += "/"; + } + } + + String appUrlPath = this.config.getStartPath(); + if (appUrlPath != null && !appUrlPath.trim().isEmpty()) { + appUrl += appUrlPath; + } } /** From 7906d3616e8bfb2e2c1c81ee123424c06fc4e5ab Mon Sep 17 00:00:00 2001 From: Chace Daniels Date: Wed, 28 Jun 2023 17:24:05 -0700 Subject: [PATCH 6/6] feat(android): add check for excluded domains before ssl request --- .../plugin/util/HttpRequestHandler.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java index 40b4218a56..569ad9a2ea 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; @@ -396,7 +397,7 @@ public static JSObject request(PluginCall call, String httpMethod, Bridge bridge CapacitorHttpUrlConnection connection = connectionBuilder.build(); - if (null != bridge) { + if (null != bridge && !isDomainExcludedFromSSL(bridge, url)) { connection.setSSLSocketFactory(bridge); } @@ -414,6 +415,16 @@ public static JSObject request(PluginCall call, String httpMethod, Bridge bridge return buildResponse(connection, responseType); } + private static Boolean isDomainExcludedFromSSL(Bridge bridge, URL url) { + try { + Class sslPinningImpl = Class.forName("io.ionic.sslpinning.SSLPinning"); + Method method = sslPinningImpl.getDeclaredMethod("isDomainExcluded", Bridge.class, URL.class); + return (Boolean) method.invoke(sslPinningImpl.newInstance(), bridge, url); + } catch (Exception ignored) { + return false; + } + } + @FunctionalInterface public interface ProgressEmitter { void emit(Integer bytes, Integer contentLength);