react-native-security-suite 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/LICENSE +1 -2
  2. package/README.md +20 -1
  3. package/android/build.gradle +86 -43
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +1 -3
  6. package/android/src/main/AndroidManifestDeprecated.xml +3 -0
  7. package/android/src/main/java/com/{reactnativesecuritysuite → securitysuite}/SecuritySuiteModule.java +8 -1
  8. package/android/src/main/java/com/{reactnativesecuritysuite → securitysuite}/SecuritySuitePackage.java +1 -1
  9. package/android/src/main/java/com/securitysuite/Sslpinning.java +302 -0
  10. package/android/src/main/java/com/{reactnativesecuritysuite → securitysuite}/StorageEncryption.java +1 -2
  11. package/ios/ReactNativeSslPinning-Bridging-Header.h +2 -0
  12. package/ios/{SecuritySuite.m → SecuritySuite.mm} +2 -0
  13. package/ios/SecuritySuite.swift +55 -1
  14. package/ios/SecuritySuite.xcodeproj/project.pbxproj +20 -10
  15. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -3
  16. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcuserdata/mohammadnavabi.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  17. package/ios/SecuritySuite.xcodeproj/xcuserdata/mohammadnavabi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +6 -0
  18. package/ios/SslPinning.swift +195 -0
  19. package/lib/commonjs/helpers.js +0 -3
  20. package/lib/commonjs/helpers.js.map +1 -1
  21. package/lib/commonjs/index.js +46 -38
  22. package/lib/commonjs/index.js.map +1 -1
  23. package/lib/module/helpers.js +0 -1
  24. package/lib/module/helpers.js.map +1 -1
  25. package/lib/module/index.js +43 -14
  26. package/lib/module/index.js.map +1 -1
  27. package/lib/typescript/helpers.d.ts +1 -0
  28. package/lib/typescript/helpers.d.ts.map +1 -0
  29. package/lib/typescript/index.d.ts +4 -1
  30. package/lib/typescript/index.d.ts.map +1 -0
  31. package/package.json +45 -35
  32. package/react-native-security-suite.podspec +18 -2
  33. package/src/index.tsx +66 -16
  34. package/android/.gradle/7.4/checksums/checksums.lock +0 -0
  35. package/android/.gradle/7.4/checksums/md5-checksums.bin +0 -0
  36. package/android/.gradle/7.4/checksums/sha1-checksums.bin +0 -0
  37. package/android/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock +0 -0
  38. package/android/.gradle/7.4/dependencies-accessors/gc.properties +0 -0
  39. package/android/.gradle/7.4/executionHistory/executionHistory.bin +0 -0
  40. package/android/.gradle/7.4/executionHistory/executionHistory.lock +0 -0
  41. package/android/.gradle/7.4/fileChanges/last-build.bin +0 -0
  42. package/android/.gradle/7.4/fileHashes/fileHashes.bin +0 -0
  43. package/android/.gradle/7.4/fileHashes/fileHashes.lock +0 -0
  44. package/android/.gradle/7.4/fileHashes/resourceHashesCache.bin +0 -0
  45. package/android/.gradle/7.4/gc.properties +0 -0
  46. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  47. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  48. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  49. package/android/.gradle/file-system.probe +0 -0
  50. package/android/.gradle/vcs-1/gc.properties +0 -0
  51. package/android/.idea/compiler.xml +0 -6
  52. package/android/.idea/gradle.xml +0 -17
  53. package/android/.idea/jarRepositories.xml +0 -40
  54. package/android/.idea/misc.xml +0 -10
  55. package/android/.idea/vcs.xml +0 -6
  56. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  57. package/android/gradle/wrapper/gradle-wrapper.properties +0 -5
  58. package/android/gradlew +0 -234
  59. package/android/gradlew.bat +0 -89
  60. package/android/local.properties +0 -8
  61. package/ios/SecuritySuite.xcodeproj/project.xcworkspace/xcuserdata/Navabi.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  62. /package/ios/SecuritySuite.xcodeproj/xcuserdata/{Navabi.xcuserdatad → mohammadnavabi.xcuserdatad}/xcschemes/xcschememanagement.plist +0 -0
package/LICENSE CHANGED
@@ -1,7 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Mohammad Navabi
4
-
3
+ Copyright (c) 2023 Mohammad Navabi
5
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
5
  of this software and associated documentation files (the "Software"), to deal
7
6
  in the Software without restriction, including without limitation the rights
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # react-native-security-suite
2
2
 
3
3
  Security solutions for Android and iOS
4
+ SSL Pinning
4
5
  A native implementation encryption/decryption
5
6
  Root/Jailbreak detection
6
7
 
@@ -25,9 +26,27 @@ import {
25
26
  encrypt,
26
27
  decrypt,
27
28
  deviceHasSecurityRisk,
29
+ fetch,
28
30
  } from 'react-native-security-suite';
29
31
 
30
- // ...
32
+ // SSL Pinning
33
+ const response = await fetch('URL', {
34
+ body: {},
35
+ headers: {},
36
+ certificates: [
37
+ /* certs */
38
+ ],
39
+ validDomains: [
40
+ /* your valid domain */
41
+ ],
42
+ timeout: 6000,
43
+ });
44
+ let responseJson = await response.json();
45
+ console.log('SSL Pinning server response: ', responseJson);
46
+
47
+ // ------- OR --------
48
+
49
+ // Hard Encrypt/Decrypt with sharedKey
31
50
  const publicKey = await getPublicKey();
32
51
  console.log('Public key: ', publicKey);
33
52
  /*
@@ -1,60 +1,103 @@
1
1
  buildscript {
2
- if (project == rootProject) {
3
- repositories {
4
- google()
5
- mavenCentral()
6
- jcenter()
7
- }
8
-
9
- dependencies {
10
- classpath 'com.android.tools.build:gradle:3.5.3'
11
- }
12
- }
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:7.2.1"
9
+ }
13
10
  }
14
11
 
15
- apply plugin: 'com.android.library'
12
+ def isNewArchitectureEnabled() {
13
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
14
+ }
15
+
16
+ apply plugin: "com.android.library"
17
+
18
+
19
+ def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
16
20
 
17
- def safeExtGet(prop, fallback) {
18
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
21
+ if (isNewArchitectureEnabled()) {
22
+ apply plugin: "com.facebook.react"
19
23
  }
20
24
 
21
- android {
22
- compileSdkVersion safeExtGet('SecuritySuite_compileSdkVersion', 31)
23
- defaultConfig {
24
- minSdkVersion safeExtGet('SecuritySuite_minSdkVersion', 21)
25
- targetSdkVersion safeExtGet('SecuritySuite_targetSdkVersion', 31)
26
- versionCode 1
27
- versionName "1.0"
25
+ def getExtOrDefault(name) {
26
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["SecuritySuite_" + name]
27
+ }
28
28
 
29
- }
29
+ def getExtOrIntegerDefault(name) {
30
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["SecuritySuite_" + name]).toInteger()
31
+ }
30
32
 
31
- buildTypes {
32
- release {
33
- minifyEnabled false
34
- }
35
- }
36
- lintOptions {
37
- disable 'GradleCompatible'
33
+ def supportsNamespace() {
34
+ def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
35
+ def major = parsed[0].toInteger()
36
+ def minor = parsed[1].toInteger()
37
+
38
+ // Namespace support was added in 7.3.0
39
+ if (major == 7 && minor >= 3) {
40
+ return true
41
+ }
42
+
43
+ return major >= 8
44
+ }
45
+
46
+ android {
47
+ if (supportsNamespace()) {
48
+ namespace "com.securitysuite"
49
+ } else {
50
+ sourceSets {
51
+ main {
52
+ manifest.srcFile "src/main/AndroidManifestDeprecated.xml"
53
+ }
38
54
  }
39
- compileOptions {
40
- sourceCompatibility JavaVersion.VERSION_1_8
41
- targetCompatibility JavaVersion.VERSION_1_8
55
+ }
56
+
57
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
58
+
59
+ defaultConfig {
60
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
61
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
62
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
63
+ }
64
+ buildTypes {
65
+ release {
66
+ minifyEnabled false
42
67
  }
68
+ }
69
+
70
+ lintOptions {
71
+ disable "GradleCompatible"
72
+ }
73
+
74
+ compileOptions {
75
+ sourceCompatibility JavaVersion.VERSION_1_8
76
+ targetCompatibility JavaVersion.VERSION_1_8
77
+ }
78
+
43
79
  }
44
80
 
45
81
  repositories {
46
- mavenLocal()
47
- maven {
48
- // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
49
- url("$rootDir/../node_modules/react-native/android")
50
- }
51
- google()
52
- mavenCentral()
53
- jcenter()
82
+ mavenCentral()
83
+ google()
84
+ jcenter()
54
85
  }
55
86
 
87
+
56
88
  dependencies {
57
- //noinspection GradleDynamicVersion
58
- implementation "com.facebook.react:react-native:+" // From node_modules
59
- implementation "com.scottyab:rootbeer-lib:0.1.0"
89
+ // For < 0.71, this will be from the local maven repo
90
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
91
+ //noinspection GradleDynamicVersion
92
+ implementation "com.facebook.react:react-native:+"
93
+ implementation "com.squareup.okhttp3:okhttp:3.0.1"
94
+ implementation "com.scottyab:rootbeer-lib:0.1.0"
95
+ }
96
+
97
+ if (isNewArchitectureEnabled()) {
98
+ react {
99
+ jsRootDir = file("../src/")
100
+ libraryName = "SecuritySuite"
101
+ codegenJavaPackageName = "com.securitysuite"
102
+ }
60
103
  }
@@ -0,0 +1,5 @@
1
+ SecuritySuite_kotlinVersion=1.7.0
2
+ SecuritySuite_minSdkVersion=21
3
+ SecuritySuite_targetSdkVersion=31
4
+ SecuritySuite_compileSdkVersion=31
5
+ SecuritySuite_ndkversion=21.4.7075529
@@ -1,4 +1,2 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.reactnativesecuritysuite">
3
-
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
4
2
  </manifest>
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.securitysuite">
3
+ </manifest>
@@ -1,10 +1,11 @@
1
- package com.reactnativesecuritysuite;
1
+ package com.securitysuite;
2
2
 
3
3
  import com.facebook.react.bridge.Callback;
4
4
  import com.facebook.react.bridge.Promise;
5
5
  import com.facebook.react.bridge.ReactApplicationContext;
6
6
  import com.facebook.react.bridge.ReactContextBaseJavaModule;
7
7
  import com.facebook.react.bridge.ReactMethod;
8
+ import com.facebook.react.bridge.ReadableMap;
8
9
  import com.facebook.react.module.annotations.ReactModule;
9
10
  import com.scottyab.rootbeer.RootBeer;
10
11
 
@@ -188,6 +189,12 @@ public class SecuritySuiteModule extends ReactContextBaseJavaModule {
188
189
  Settings.Secure.ANDROID_ID);
189
190
  }
190
191
 
192
+ @ReactMethod
193
+ public void fetch(String url, final ReadableMap options, Callback callback) {
194
+ Sslpinning sslpinning = new Sslpinning(context);
195
+ sslpinning.fetch(url, options, callback);
196
+ }
197
+
191
198
  @ReactMethod
192
199
  public void deviceHasSecurityRisk(Promise promise) {
193
200
  RootBeer rootBeer = new RootBeer(context);
@@ -1,4 +1,4 @@
1
- package com.reactnativesecuritysuite;
1
+ package com.securitysuite;
2
2
 
3
3
  import androidx.annotation.NonNull;
4
4
 
@@ -0,0 +1,302 @@
1
+ package com.securitysuite;
2
+
3
+ import android.content.Context;
4
+ import android.net.Uri;
5
+
6
+ import com.facebook.react.bridge.Arguments;
7
+ import com.facebook.react.bridge.Callback;
8
+ import com.facebook.react.bridge.ReactApplicationContext;
9
+ import com.facebook.react.bridge.ReadableArray;
10
+ import com.facebook.react.bridge.ReadableMap;
11
+ import com.facebook.react.bridge.ReadableMapKeySetIterator;
12
+ import com.facebook.react.bridge.ReadableType;
13
+ import com.facebook.react.bridge.WritableMap;
14
+
15
+ import org.json.JSONException;
16
+
17
+ import java.io.BufferedOutputStream;
18
+ import java.io.File;
19
+ import java.io.FileOutputStream;
20
+ import java.io.IOException;
21
+ import java.io.InputStream;
22
+ import java.io.OutputStream;
23
+ import java.net.URI;
24
+ import java.net.URISyntaxException;
25
+ import java.util.HashMap;
26
+ import java.util.Map;
27
+ import java.util.concurrent.TimeUnit;
28
+
29
+ import okhttp3.CertificatePinner;
30
+ import okhttp3.Headers;
31
+ import okhttp3.MediaType;
32
+ import okhttp3.MultipartBody;
33
+ import okhttp3.Request;
34
+ import okhttp3.OkHttpClient;
35
+ import okhttp3.RequestBody;
36
+ import okhttp3.Response;
37
+
38
+ public class Sslpinning {
39
+ private ReactApplicationContext context;
40
+ private static String content_type = "application/json; charset=utf-8";
41
+ public static MediaType mediaType = MediaType.parse(content_type);
42
+ String responseBodyString = "{}";
43
+
44
+ public Sslpinning(ReactApplicationContext context) {
45
+ this.context = context;
46
+ }
47
+
48
+ public void fetch(String url, final ReadableMap options, Callback callback) {
49
+ if (!isValidUrl(url)) {
50
+ callback.invoke(null, "url is invalid!");
51
+ return;
52
+ }
53
+
54
+ String hostname;
55
+ try {
56
+ hostname = getHostname(url);
57
+ } catch (URISyntaxException e) {
58
+ hostname = url;
59
+ }
60
+
61
+ if (!options.hasKey("certificates")) {
62
+ callback.invoke(null, "certificates is required!");
63
+ return;
64
+ }
65
+
66
+ ReadableArray hashes = options.getArray("certificates");
67
+ if (hashes == null || hashes.size() == 0) {
68
+ callback.invoke(null, "certificates is empty!");
69
+ return;
70
+ }
71
+
72
+ try {
73
+ CertificatePinner certificatePinner = getCertificatePinner(hostname, options);
74
+ OkHttpClient client = getClient(options, certificatePinner);
75
+
76
+ Headers header = setHeader(options);
77
+ RequestBody body = setBody(context, options);
78
+ String method = getMethod(options);
79
+
80
+ Request request = new Request.Builder()
81
+ .url(url)
82
+ .headers(header)
83
+ .method(method, body)
84
+ .build();
85
+
86
+ WritableMap output = Arguments.createMap();
87
+
88
+ try {
89
+ Response response = client.newCall(request).execute();
90
+ int responseCode = response.code();
91
+
92
+ byte[] bytes = response.body().bytes();
93
+ responseBodyString = new String(bytes, "UTF-8");
94
+
95
+ output.putInt("status", responseCode);
96
+ output.putString("url", request.url().toString());
97
+
98
+ if (!response.isSuccessful() || responseCode >= 400) {
99
+ output.putString("error", responseBodyString);
100
+ callback.invoke(null, output);
101
+ return;
102
+ }
103
+ output.putString("response", responseBodyString);
104
+ callback.invoke(output, null);
105
+ } catch (IOException e) {
106
+ output.putString("error", responseBodyString);
107
+
108
+ callback.invoke(null, output);
109
+ if (e instanceof java.net.SocketTimeoutException) {
110
+ System.err.print("Socket TimeOut");
111
+ } else {
112
+ e.printStackTrace();
113
+ }
114
+ }
115
+ } catch(JSONException e) {
116
+ callback.invoke(null, e);
117
+ }
118
+ }
119
+
120
+ private CertificatePinner getCertificatePinner(String hostname, ReadableMap options) {
121
+ CertificatePinner.Builder certificatePinner = new CertificatePinner.Builder();
122
+
123
+ ReadableArray hashes = options.getArray("certificates");
124
+ for (int i = 0; i < hashes.size(); i++) {
125
+ certificatePinner.add(hostname, "sha256/" + hashes.getString(i));
126
+ }
127
+
128
+ return certificatePinner.build();
129
+ }
130
+
131
+ private OkHttpClient getClient(ReadableMap options, CertificatePinner certificatePinner) throws JSONException {
132
+ if (options.hasKey("timeout")) {
133
+ int timeout = options.getInt("timeout");
134
+ return new OkHttpClient.Builder()
135
+ .connectTimeout(timeout, TimeUnit.MILLISECONDS)
136
+ .readTimeout(timeout, TimeUnit.MILLISECONDS)
137
+ .writeTimeout(timeout, TimeUnit.MILLISECONDS)
138
+ .certificatePinner(certificatePinner)
139
+ .build();
140
+ } else {
141
+ return new OkHttpClient.Builder()
142
+ .certificatePinner(certificatePinner)
143
+ .build();
144
+ }
145
+ }
146
+
147
+ private static String getHostname(String url) throws URISyntaxException {
148
+ URI uri = new URI(url);
149
+ String domain = uri.getHost();
150
+ return domain.startsWith("www.") ? domain.substring(4) : domain;
151
+ }
152
+
153
+ private String getMethod(ReadableMap options) {
154
+ String method = "GET";
155
+ if (options.hasKey("method")) {
156
+ method = options.getString("method");
157
+ }
158
+
159
+ return method;
160
+ }
161
+
162
+ private Headers setHeader(ReadableMap options) {
163
+ if (!options.hasKey("headers")) {
164
+ return null;
165
+ }
166
+
167
+ ReadableMap headers = options.getMap("headers");
168
+ Headers.Builder builder = new Headers.Builder();
169
+
170
+ Map<String, String> headersMap = readableMapToHashMap(headers);
171
+ for (Map.Entry<String, String> set : headersMap.entrySet()) {
172
+ builder.add(set.getKey(), set.getValue());
173
+ }
174
+
175
+ return builder.build();
176
+ }
177
+
178
+ private HashMap readableMapToHashMap(ReadableMap readableMap) {
179
+ if (readableMap == null) {
180
+ return null;
181
+ }
182
+
183
+ HashMap map = new HashMap<String, String>();
184
+ ReadableMapKeySetIterator keySetIterator = readableMap.keySetIterator();
185
+ while (keySetIterator.hasNextKey()) {
186
+ String key = keySetIterator.nextKey();
187
+ ReadableType type = readableMap.getType(key);
188
+ switch(type) {
189
+ case String:
190
+ map.put(key, readableMap.getString(key));
191
+ break;
192
+ case Map:
193
+ HashMap<String, Object> attributes = this.readableMapToHashMap(readableMap.getMap(key));
194
+ map.put(key, attributes);
195
+ break;
196
+ default:
197
+ // do nothing
198
+ }
199
+ }
200
+
201
+ return map;
202
+ }
203
+
204
+ private static boolean isFilePart(ReadableArray part) {
205
+ if (part.getType(1) != ReadableType.Map) {
206
+ return false;
207
+ }
208
+ ReadableMap value = part.getMap(1);
209
+ return value.hasKey("type") && (value.hasKey("uri") || value.hasKey("path"));
210
+ }
211
+
212
+ private RequestBody setBody(ReactApplicationContext context, ReadableMap options) {
213
+ if (!options.hasKey("body")) {
214
+ return null;
215
+ }
216
+
217
+ RequestBody body = null;
218
+ ReadableType bodyType = options.getType("body");
219
+ switch (bodyType) {
220
+ case String:
221
+ body = RequestBody.create(mediaType, options.getString("body"));
222
+ break;
223
+ case Map:
224
+ ReadableMap bodyMap = options.getMap("body");
225
+ if (bodyMap.hasKey("formData")) {
226
+ ReadableMap formData = bodyMap.getMap("formData");
227
+ body = getBody(formData);
228
+ } else if (bodyMap.hasKey("_parts")) {
229
+ body = getBody(bodyMap);
230
+ }
231
+ break;
232
+ }
233
+ return body;
234
+ }
235
+
236
+ private RequestBody getBody(ReadableMap body) {
237
+ MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
238
+ multipartBodyBuilder.setType((MediaType.parse("multipart/form-data")));
239
+ if (body.hasKey("_parts")) {
240
+ ReadableArray parts = body.getArray("_parts");
241
+ for (int i = 0; i < parts.size(); i++) {
242
+ ReadableArray part = parts.getArray(i);
243
+ String key = "";
244
+ if (part.getType(0) == ReadableType.String) {
245
+ key = part.getString(0);
246
+ } else if (part.getType(0) == ReadableType.Number) {
247
+ key = String.valueOf(part.getInt(0));
248
+ }
249
+
250
+ if (isFilePart(part)) {
251
+ ReadableMap fileData = part.getMap(1);
252
+ addFormDataPart(this.context, multipartBodyBuilder, fileData, key);
253
+ } else {
254
+ String value = part.getString(1);
255
+ multipartBodyBuilder.addFormDataPart(key, value);
256
+ }
257
+ }
258
+ }
259
+ return multipartBodyBuilder.build();
260
+ }
261
+
262
+ private static void addFormDataPart(Context context, MultipartBody.Builder multipartBodyBuilder, ReadableMap fileData, String key) {
263
+ Uri _uri = Uri.parse("");
264
+ if (fileData.hasKey("uri")) {
265
+ _uri = Uri.parse(fileData.getString("uri"));
266
+ } else if (fileData.hasKey("path")) {
267
+ _uri = Uri.parse(fileData.getString("path"));
268
+ }
269
+ String type = fileData.getString("type");
270
+ String fileName = "";
271
+ if (fileData.hasKey("fileName")) {
272
+ fileName = fileData.getString("fileName");
273
+ } else if (fileData.hasKey("name")) {
274
+ fileName = fileData.getString("name");
275
+ }
276
+
277
+ try {
278
+ File file = getTempFile(context, _uri);
279
+ multipartBodyBuilder.addFormDataPart(key, fileName, RequestBody.create(MediaType.parse(type), file));
280
+ } catch (IOException e) {
281
+ e.printStackTrace();
282
+ }
283
+ }
284
+
285
+ public static File getTempFile(Context context, Uri uri) throws IOException {
286
+ File file = File.createTempFile("media", null);
287
+ InputStream inputStream = context.getContentResolver().openInputStream(uri);
288
+ OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file));
289
+ byte[] buffer = new byte[1024];
290
+ int len;
291
+ while ((len = inputStream.read(buffer)) != -1)
292
+ outputStream.write(buffer, 0, len);
293
+ inputStream.close();
294
+ outputStream.close();
295
+ return file;
296
+ }
297
+
298
+ private boolean isValidUrl(String url) {
299
+ String regex = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
300
+ return url.matches(regex);
301
+ }
302
+ }
@@ -1,5 +1,4 @@
1
- package com.reactnativesecuritysuite;
2
-
1
+ package com.securitysuite;
3
2
 
4
3
  import android.util.Base64;
5
4
 
@@ -0,0 +1,2 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTViewManager.h>
@@ -16,6 +16,8 @@ RCT_EXTERN_METHOD(storageEncrypt:(NSString)input withSecretKey:(NSString*)secret
16
16
 
17
17
  RCT_EXTERN_METHOD(storageDecrypt:(NSString)input withSecretKey:(NSString*)secretKey withHardEncryption:(BOOL)hardEncryption withCallback:(RCTResponseSenderBlock)callback)
18
18
 
19
+ RCT_EXTERN_METHOD(fetch:(NSString)url withData:(NSDictionary)data withCallback:(RCTResponseSenderBlock)callback)
20
+
19
21
  RCT_EXTERN_METHOD(deviceHasSecurityRisk:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject)
20
22
 
21
23
  + (BOOL)requiresMainQueueSetup
@@ -1,7 +1,9 @@
1
- import IOSSecuritySuite
2
1
  import Foundation
3
2
  import CryptoKit
4
3
  import SwiftUI
4
+ import Security
5
+ import CommonCrypto
6
+ import IOSSecuritySuite
5
7
 
6
8
  @available(iOS 13.0, *)
7
9
  @objc(SecuritySuite)
@@ -123,6 +125,58 @@ class SecuritySuite: NSObject {
123
125
  func getDeviceId() -> String {
124
126
  return UIDevice.current.identifierForVendor!.uuidString.replacingOccurrences(of: "-", with: "", options: [], range: nil)
125
127
  }
128
+
129
+ @objc(fetch:withData:withCallback:)
130
+ func fetch(url: NSString, data: NSDictionary, callback: @escaping RCTResponseSenderBlock) -> Void {
131
+ let config = URLSessionConfiguration.default
132
+ config.httpShouldSetCookies = false
133
+ config.httpCookieAcceptPolicy = .never
134
+ config.networkServiceType = .responsiveData
135
+ config.shouldUseExtendedBackgroundIdleMode = true
136
+
137
+ let sslPinning = SSLPinning(data: data)
138
+
139
+ var request = URLRequest(url: URL(string: url as String)!)
140
+
141
+ if data["method"] != nil { request.httpMethod = data["method"] as! String } else { request.httpMethod = "POST" }
142
+ if data["body"] != nil { request.httpBody = (data["body"] as! String).data(using: .utf8)! } else { request.httpBody = "".data(using: .utf8)! }
143
+ if data["headers"] != nil { request.allHTTPHeaderFields = data["headers"] as! [String : String] }
144
+ if data["timeout"] != nil { request.timeoutInterval = data["timeout"] as! TimeInterval }
145
+ let session = URLSession(configuration: config, delegate: sslPinning, delegateQueue: .main)
146
+ let task = session.dataTask(with: request) { data, response, error in
147
+ let response = response as? HTTPURLResponse
148
+
149
+ if error == nil {
150
+ let responseCode = response?.statusCode
151
+ let responseString = String.init(decoding: data ?? .init(), as: UTF8.self)
152
+ let errorString = error?.localizedDescription
153
+ let responseJSON = try? JSONSerialization.jsonObject(with: data!, options: [])
154
+
155
+ var result:NSMutableDictionary = [
156
+ "status": response?.statusCode,
157
+ "url": url,
158
+ ]
159
+ if errorString == nil && responseCode! < 400 {
160
+ result["response"] = responseString
161
+ result["responseJSON"] = responseJSON
162
+ callback([result, NSNull()])
163
+ } else {
164
+ result["error"] = responseString
165
+ result["errorJSON"] = responseJSON
166
+ do {
167
+ let jsonData = try JSONSerialization.data(withJSONObject: result)
168
+ callback([NSNull(), result])
169
+ } catch {
170
+ callback([NSNull(), "JSON_PARSE_ERROR"])
171
+ }
172
+ }
173
+ } else {
174
+ callback([NSNull(), "MUST_BE_UPDATE"])
175
+ }
176
+ }
177
+
178
+ task.resume()
179
+ }
126
180
 
127
181
  @objc(deviceHasSecurityRisk:withRejecter:)
128
182
  func deviceHasSecurityRisk(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void {