@umutcansu/react-native-pinvault 2.4.1 → 2.4.2
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.
- package/CHANGELOG.md +22 -0
- package/README.md +44 -4
- package/RNPinVault.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/ConfigParser.kt +2 -0
- package/app.plugin.js +2 -0
- package/ios/Core/ConfigParser.swift +2 -0
- package/package.json +8 -3
- package/plugin/withPinVault.js +232 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.4.2 — 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Expo config plugin** (`app.plugin.js`): at `expo prebuild` it sets the
|
|
8
|
+
iOS deployment target to 16.0, writes the Info.plist keys
|
|
9
|
+
(`faceIDPermission`, `backgroundUpdates`), adds
|
|
10
|
+
`PinVaultBridge.registerBackgroundTask()` to a Swift `AppDelegate`, copies
|
|
11
|
+
the native security file into Android's assets and the iOS app bundle
|
|
12
|
+
(`nativeSecurityFile`), and writes the two native opt-outs only when asked
|
|
13
|
+
(`allowNoNativeSecurityFile`, `pinReactNativeNetworking: false`). Built and
|
|
14
|
+
run with Expo SDK 54 (React Native 0.81), Android and iOS release builds.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Native libraries 2.4.2:** release builds refuse `allowUnsigned()` /
|
|
19
|
+
`allowUnpinnedConfigApi()` unless the block also calls
|
|
20
|
+
`allowRelaxationsInRelease()`. The bridge calls it only where the native
|
|
21
|
+
security file itself allows the relaxation (`allowUnsigned` /
|
|
22
|
+
`allowUnpinned`), so the native file stays the only place that decides;
|
|
23
|
+
JS cannot turn it on.
|
|
24
|
+
|
|
3
25
|
## 2.4.1 — 2026-10-10
|
|
4
26
|
|
|
5
27
|
### Security
|
package/README.md
CHANGED
|
@@ -29,9 +29,9 @@ npm install @umutcansu/react-native-pinvault
|
|
|
29
29
|
cd ios && pod install
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
The native libraries come with it, at the package's own version: npm 2.4.
|
|
33
|
-
uses `io.github.umutcansu:pinvault:2.4.
|
|
34
|
-
package at tag `v2.4.
|
|
32
|
+
The native libraries come with it, at the package's own version: npm 2.4.2
|
|
33
|
+
uses `io.github.umutcansu:pinvault:2.4.2` from Maven Central and the Swift
|
|
34
|
+
package at tag `v2.4.2`. The New Architecture must stay on (React Native's
|
|
35
35
|
default since 0.76).
|
|
36
36
|
|
|
37
37
|
**iOS.** The pod depends on the PinVault Swift package through React Native's
|
|
@@ -65,6 +65,43 @@ hooks before `Application.onCreate` runs (see [Networking](#networking)).
|
|
|
65
65
|
**Release builds.** Put the trust anchors into the app itself, not only into
|
|
66
66
|
the JS bundle: [Native security file](#native-security-file).
|
|
67
67
|
|
|
68
|
+
### Expo
|
|
69
|
+
|
|
70
|
+
A native module: it runs in a development build or `expo prebuild`, not in
|
|
71
|
+
Expo Go. The package's config plugin does the steps above at prebuild — iOS
|
|
72
|
+
deployment target 16.0 (`Podfile.properties.json` and the Xcode project),
|
|
73
|
+
the Info.plist keys, the background task in `AppDelegate.swift`, and the
|
|
74
|
+
native security file copied into Android's assets and the iOS app bundle:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx expo install @umutcansu/react-native-pinvault
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"expo": {
|
|
83
|
+
"plugins": [
|
|
84
|
+
["@umutcansu/react-native-pinvault", {
|
|
85
|
+
"nativeSecurityFile": "./pinvault_security.json",
|
|
86
|
+
"faceIDPermission": "Unlock your protected files",
|
|
87
|
+
"backgroundUpdates": true
|
|
88
|
+
}]
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
| Option | Does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `nativeSecurityFile` | path (from the project root) of your `pinvault_security.json`; checked to be a JSON object at prebuild |
|
|
97
|
+
| `faceIDPermission` | `NSFaceIDUsageDescription`, for vault files behind the screen lock (`userAuth`) |
|
|
98
|
+
| `backgroundUpdates` | `BGTaskSchedulerPermittedIdentifiers`, `UIBackgroundModes` = `fetch` and `PinVaultBridge.registerBackgroundTask()` in a Swift `AppDelegate` (Expo SDK 53+), for `schedulePeriodicUpdates` |
|
|
99
|
+
| `allowNoNativeSecurityFile` | `true` writes the opt-out of the file (Android meta-data, Info.plist): release builds then take anchors from JS — not recommended |
|
|
100
|
+
| `pinReactNativeNetworking` | `false` removes the networking provider and writes `PinVaultPinReactNativeNetworking` = NO: RN's own fetch / WebSocket stay unpinned |
|
|
101
|
+
|
|
102
|
+
Unknown options and wrong types stop prebuild. Tested with Expo SDK 54
|
|
103
|
+
(React Native 0.81) on Android and iOS, release builds.
|
|
104
|
+
|
|
68
105
|
## Quick start
|
|
69
106
|
|
|
70
107
|
```ts
|
|
@@ -216,7 +253,10 @@ parsed as strictly; a broken file rejects `start` with `E_INVALID_CONFIG`
|
|
|
216
253
|
a block that only names itself rejects `start` — otherwise the bundle would
|
|
217
254
|
supply the pins and keys;
|
|
218
255
|
- `allowUnsigned`, `allowUnpinnedConfigApi` and `allowServerGeneratedKey` from
|
|
219
|
-
JS are refused unless the file allows them for that block;
|
|
256
|
+
JS are refused unless the file allows them for that block; a release build
|
|
257
|
+
of the native libraries (2.4.2+) refuses the first two unless the block also
|
|
258
|
+
says `allowRelaxationsInRelease()`, which the bridge adds only where the file
|
|
259
|
+
allows the relaxation;
|
|
220
260
|
- a block's `url`, `enrollmentUrl`, `renewalUrl`, `tokenHosts` and
|
|
221
261
|
`clientCertHosts` are fixed where the file gives them (where the block talks to, and who gets its token and identity),
|
|
222
262
|
and `attestation: true` / `proofOfPossession: true` there cannot be turned off from JS;
|
package/RNPinVault.podspec
CHANGED
|
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
|
4
4
|
|
|
5
5
|
# PinVault for iOS is a Swift package (Package.swift at the repository root,
|
|
6
6
|
# product "PinVault"). React Native's spm_dependency adds it to the Pods project:
|
|
7
|
-
# - consumers: the git URL at the tag of this package's version (v2.4.
|
|
7
|
+
# - consumers: the git URL at the tag of this package's version (v2.4.2);
|
|
8
8
|
# - development: PINVAULT_IOS_PACKAGE_PATH = a PinVault checkout (the sample's
|
|
9
9
|
# Podfile sets it to this repository), the counterpart of pinvault.localPath.
|
|
10
10
|
pinvault_url = "https://github.com/umutcansu/PinVault.git"
|
package/android/build.gradle
CHANGED
|
@@ -321,6 +321,8 @@ internal object ConfigParser {
|
|
|
321
321
|
if (sec.allowUnsigned) this.allowUnsigned()
|
|
322
322
|
sec.serverScope?.let { this.serverScope(it) }
|
|
323
323
|
if (sec.allowUnpinned) this.allowUnpinnedConfigApi()
|
|
324
|
+
// The native security file allowed them for this block: the library keeps them in a release build too.
|
|
325
|
+
if (native != null && (sec.allowUnsigned || sec.allowUnpinned)) this.allowRelaxationsInRelease()
|
|
324
326
|
if (sec.allowServerKey) this.allowServerGeneratedKey()
|
|
325
327
|
sec.clientCaPins?.let { this.clientCaPins(it) }
|
|
326
328
|
maxLifetime?.let { this.maxClientCertLifetimeDays(it) }
|
package/app.plugin.js
ADDED
|
@@ -346,6 +346,8 @@ public enum ConfigParser {
|
|
|
346
346
|
if sec.allowUnsigned { api.allowUnsigned() }
|
|
347
347
|
if let v = sec.serverScope { api.serverScope(v) }
|
|
348
348
|
if sec.allowUnpinned { api.allowUnpinnedConfigApi() }
|
|
349
|
+
// The native security file allowed them for this block: the library keeps them in a release build too.
|
|
350
|
+
if native != nil, sec.allowUnsigned || sec.allowUnpinned { api.allowRelaxationsInRelease() }
|
|
349
351
|
if sec.allowServerKey { api.allowServerGeneratedKey() }
|
|
350
352
|
if let v = sec.clientCaPins { api.clientCaPins(v) }
|
|
351
353
|
if let maxLifetime { api.maxClientCertLifetimeDays(maxLifetime) }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umutcansu/react-native-pinvault",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "PinVault for React Native: dynamic certificate pinning with signed remote configs, mTLS enrollment and signed vault files, as a thin bridge over the native PinVault libraries",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"types": "./lib/typescript/src/index.d.ts",
|
|
11
11
|
"default": "./lib/module/index.js"
|
|
12
12
|
},
|
|
13
|
-
"./package.json": "./package.json"
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
"./app.plugin.js": "./app.plugin.js"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"src",
|
|
@@ -23,6 +24,8 @@
|
|
|
23
24
|
"ios/*.swift",
|
|
24
25
|
"ios/Core",
|
|
25
26
|
"*.podspec",
|
|
27
|
+
"app.plugin.js",
|
|
28
|
+
"plugin",
|
|
26
29
|
"README.md",
|
|
27
30
|
"CHANGELOG.md",
|
|
28
31
|
"LICENSE",
|
|
@@ -43,7 +46,8 @@
|
|
|
43
46
|
"mtls",
|
|
44
47
|
"security",
|
|
45
48
|
"ios",
|
|
46
|
-
"android"
|
|
49
|
+
"android",
|
|
50
|
+
"expo"
|
|
47
51
|
],
|
|
48
52
|
"repository": {
|
|
49
53
|
"type": "git",
|
|
@@ -65,6 +69,7 @@
|
|
|
65
69
|
"react-native": ">=0.81.0"
|
|
66
70
|
},
|
|
67
71
|
"devDependencies": {
|
|
72
|
+
"@expo/config-plugins": "^54.0.5",
|
|
68
73
|
"@jest/globals": "^29.7.0",
|
|
69
74
|
"@react-native/babel-preset": "0.87.1",
|
|
70
75
|
"@react-native/jest-preset": "0.87.1",
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// Expo config plugin for @umutcansu/react-native-pinvault: does at
|
|
2
|
+
// `expo prebuild` what the README's Install and "Native security file"
|
|
3
|
+
// sections ask of a bare React Native app.
|
|
4
|
+
//
|
|
5
|
+
// plugins: [["@umutcansu/react-native-pinvault", {
|
|
6
|
+
// nativeSecurityFile: "./pinvault_security.json", // → Android assets + iOS app bundle
|
|
7
|
+
// faceIDPermission: "Unlock your protected files", // NSFaceIDUsageDescription (userAuth vault files)
|
|
8
|
+
// backgroundUpdates: true, // schedulePeriodicUpdates on iOS
|
|
9
|
+
// allowNoNativeSecurityFile: false, // release builds without the file (not recommended)
|
|
10
|
+
// pinReactNativeNetworking: true // false = RN's own networking stays unpinned
|
|
11
|
+
// }]]
|
|
12
|
+
//
|
|
13
|
+
// Every option is optional. Without `nativeSecurityFile` a release build
|
|
14
|
+
// refuses a config with Config APIs or static pins (README, "Native security
|
|
15
|
+
// file") unless `allowNoNativeSecurityFile` is true.
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
let configPlugins;
|
|
20
|
+
try {
|
|
21
|
+
configPlugins = require('expo/config-plugins');
|
|
22
|
+
} catch (e) {
|
|
23
|
+
configPlugins = require('@expo/config-plugins');
|
|
24
|
+
}
|
|
25
|
+
const {
|
|
26
|
+
AndroidConfig,
|
|
27
|
+
IOSConfig,
|
|
28
|
+
withAndroidManifest,
|
|
29
|
+
withAppDelegate,
|
|
30
|
+
withDangerousMod,
|
|
31
|
+
withInfoPlist,
|
|
32
|
+
withPodfileProperties,
|
|
33
|
+
withXcodeProject,
|
|
34
|
+
createRunOncePlugin,
|
|
35
|
+
} = configPlugins;
|
|
36
|
+
|
|
37
|
+
const pkg = require('../package.json');
|
|
38
|
+
|
|
39
|
+
const SECURITY_FILE = 'pinvault_security.json';
|
|
40
|
+
const BG_TASK_ID = 'io.github.umutcansu.pinvault.refresh';
|
|
41
|
+
const MIN_IOS = '16.0';
|
|
42
|
+
const ALLOW_NO_FILE_META = 'io.github.umutcansu.pinvault.ALLOW_NO_NATIVE_SECURITY_FILE';
|
|
43
|
+
const NETWORKING_PROVIDER = 'io.github.umutcansu.pinvault.reactnative.PinVaultNetworkingInitializer';
|
|
44
|
+
const KNOWN_OPTIONS = [
|
|
45
|
+
'nativeSecurityFile',
|
|
46
|
+
'faceIDPermission',
|
|
47
|
+
'backgroundUpdates',
|
|
48
|
+
'allowNoNativeSecurityFile',
|
|
49
|
+
'pinReactNativeNetworking',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function checkOptions(options) {
|
|
53
|
+
const props = options || {};
|
|
54
|
+
const unknown = Object.keys(props).filter((k) => !KNOWN_OPTIONS.includes(k));
|
|
55
|
+
if (unknown.length > 0) {
|
|
56
|
+
throw new Error(`[${pkg.name}] unknown plugin option(s): ${unknown.join(', ')} (known: ${KNOWN_OPTIONS.join(', ')})`);
|
|
57
|
+
}
|
|
58
|
+
for (const k of ['backgroundUpdates', 'allowNoNativeSecurityFile', 'pinReactNativeNetworking']) {
|
|
59
|
+
if (props[k] !== undefined && typeof props[k] !== 'boolean') throw new Error(`[${pkg.name}] ${k} must be true or false`);
|
|
60
|
+
}
|
|
61
|
+
for (const k of ['nativeSecurityFile', 'faceIDPermission']) {
|
|
62
|
+
if (props[k] !== undefined && (typeof props[k] !== 'string' || props[k].trim() === '')) {
|
|
63
|
+
throw new Error(`[${pkg.name}] ${k} must be a non-empty string`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return props;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** "15.1" < "16.0": dotted numeric versions. */
|
|
70
|
+
function versionLess(a, b) {
|
|
71
|
+
const x = String(a).split('.').map(Number);
|
|
72
|
+
const y = String(b).split('.').map(Number);
|
|
73
|
+
for (let i = 0; i < Math.max(x.length, y.length); i++) {
|
|
74
|
+
const d = (x[i] || 0) - (y[i] || 0);
|
|
75
|
+
if (d !== 0) return d < 0;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The project's security file, read and parsed (a JSON error stops prebuild, not the app at launch). */
|
|
81
|
+
function readSecurityFile(projectRoot, relative) {
|
|
82
|
+
const source = path.resolve(projectRoot, relative);
|
|
83
|
+
if (!fs.existsSync(source)) throw new Error(`[${pkg.name}] nativeSecurityFile not found: ${source}`);
|
|
84
|
+
const text = fs.readFileSync(source, 'utf8');
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(text);
|
|
87
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('not a JSON object');
|
|
88
|
+
} catch (e) {
|
|
89
|
+
throw new Error(`[${pkg.name}] nativeSecurityFile ${source} is not a JSON object: ${e.message}`);
|
|
90
|
+
}
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* AppDelegate.swift: `import RNPinVault` and the background task registration
|
|
96
|
+
* as the first line of didFinishLaunchingWithOptions. Idempotent.
|
|
97
|
+
*/
|
|
98
|
+
function addBackgroundTaskToAppDelegate(contents, language) {
|
|
99
|
+
if (language !== 'swift') {
|
|
100
|
+
throw new Error(`[${pkg.name}] backgroundUpdates needs a Swift AppDelegate (Expo SDK 53+); add the call by hand: README, "Install"`);
|
|
101
|
+
}
|
|
102
|
+
if (contents.includes('PinVaultBridge.registerBackgroundTask()')) return contents;
|
|
103
|
+
let out = contents;
|
|
104
|
+
if (!/^import RNPinVault$/m.test(out)) {
|
|
105
|
+
const imports = out.match(/^import .+$/gm);
|
|
106
|
+
if (!imports) throw new Error(`[${pkg.name}] AppDelegate.swift has no import lines`);
|
|
107
|
+
const last = imports[imports.length - 1];
|
|
108
|
+
out = out.replace(last, `${last}\nimport RNPinVault`);
|
|
109
|
+
}
|
|
110
|
+
const launch = /(didFinishLaunchingWithOptions launchOptions:[^{]*\{\n)/;
|
|
111
|
+
if (!launch.test(out)) {
|
|
112
|
+
throw new Error(`[${pkg.name}] application(_:didFinishLaunchingWithOptions:) not found in AppDelegate.swift`);
|
|
113
|
+
}
|
|
114
|
+
return out.replace(
|
|
115
|
+
launch,
|
|
116
|
+
'$1 // @umutcansu/react-native-pinvault: schedulePeriodicUpdates (before launch ends)\n' +
|
|
117
|
+
' _ = PinVaultBridge.registerBackgroundTask()\n'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Info.plist entries the options ask for. */
|
|
122
|
+
function applyInfoPlist(plist, props) {
|
|
123
|
+
const out = { ...plist };
|
|
124
|
+
if (props.faceIDPermission) out.NSFaceIDUsageDescription = props.faceIDPermission;
|
|
125
|
+
if (props.backgroundUpdates) {
|
|
126
|
+
const ids = Array.isArray(out.BGTaskSchedulerPermittedIdentifiers) ? [...out.BGTaskSchedulerPermittedIdentifiers] : [];
|
|
127
|
+
if (!ids.includes(BG_TASK_ID)) ids.push(BG_TASK_ID);
|
|
128
|
+
out.BGTaskSchedulerPermittedIdentifiers = ids;
|
|
129
|
+
const modes = Array.isArray(out.UIBackgroundModes) ? [...out.UIBackgroundModes] : [];
|
|
130
|
+
if (!modes.includes('fetch')) modes.push('fetch');
|
|
131
|
+
out.UIBackgroundModes = modes;
|
|
132
|
+
}
|
|
133
|
+
if (props.allowNoNativeSecurityFile === true) out.PinVaultAllowNoNativeSecurityFile = true;
|
|
134
|
+
else delete out.PinVaultAllowNoNativeSecurityFile;
|
|
135
|
+
if (props.pinReactNativeNetworking === false) out.PinVaultPinReactNativeNetworking = false;
|
|
136
|
+
else delete out.PinVaultPinReactNativeNetworking;
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** AndroidManifest: the allow-no-file meta-data and the networking provider opt-out. */
|
|
141
|
+
function applyAndroidManifest(manifest, props) {
|
|
142
|
+
const app = AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
|
|
143
|
+
if (props.allowNoNativeSecurityFile === true) {
|
|
144
|
+
AndroidConfig.Manifest.addMetaDataItemToMainApplication(app, ALLOW_NO_FILE_META, 'true');
|
|
145
|
+
} else {
|
|
146
|
+
AndroidConfig.Manifest.removeMetaDataItemFromMainApplication(app, ALLOW_NO_FILE_META);
|
|
147
|
+
}
|
|
148
|
+
app.provider = (app.provider || []).filter((p) => p.$['android:name'] !== NETWORKING_PROVIDER);
|
|
149
|
+
if (props.pinReactNativeNetworking === false) {
|
|
150
|
+
manifest.manifest.$['xmlns:tools'] = manifest.manifest.$['xmlns:tools'] || 'http://schemas.android.com/tools';
|
|
151
|
+
app.provider.push({
|
|
152
|
+
$: {
|
|
153
|
+
'android:name': NETWORKING_PROVIDER,
|
|
154
|
+
'android:authorities': '${applicationId}.pinvault-networking',
|
|
155
|
+
'tools:node': 'remove',
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return manifest;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function withPinVault(config, options) {
|
|
163
|
+
const props = checkOptions(options);
|
|
164
|
+
|
|
165
|
+
// iOS 16: the PinVault Swift package's minimum.
|
|
166
|
+
config = withPodfileProperties(config, (c) => {
|
|
167
|
+
const current = c.modResults['ios.deploymentTarget'];
|
|
168
|
+
if (!current || versionLess(current, MIN_IOS)) c.modResults['ios.deploymentTarget'] = MIN_IOS;
|
|
169
|
+
return c;
|
|
170
|
+
});
|
|
171
|
+
config = withXcodeProject(config, (c) => {
|
|
172
|
+
const configurations = c.modResults.pbxXCBuildConfigurationSection();
|
|
173
|
+
for (const key of Object.keys(configurations)) {
|
|
174
|
+
const settings = configurations[key].buildSettings;
|
|
175
|
+
if (!settings || settings.IPHONEOS_DEPLOYMENT_TARGET === undefined) continue;
|
|
176
|
+
if (versionLess(String(settings.IPHONEOS_DEPLOYMENT_TARGET).replace(/"/g, ''), MIN_IOS)) {
|
|
177
|
+
settings.IPHONEOS_DEPLOYMENT_TARGET = MIN_IOS;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return c;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
config = withInfoPlist(config, (c) => {
|
|
184
|
+
c.modResults = applyInfoPlist(c.modResults, props);
|
|
185
|
+
return c;
|
|
186
|
+
});
|
|
187
|
+
config = withAndroidManifest(config, (c) => {
|
|
188
|
+
c.modResults = applyAndroidManifest(c.modResults, props);
|
|
189
|
+
return c;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (props.backgroundUpdates) {
|
|
193
|
+
config = withAppDelegate(config, (c) => {
|
|
194
|
+
c.modResults.contents = addBackgroundTaskToAppDelegate(c.modResults.contents, c.modResults.language);
|
|
195
|
+
return c;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (props.nativeSecurityFile) {
|
|
200
|
+
// Android: assets (sealed by the APK signature; README, "Native security file").
|
|
201
|
+
config = withDangerousMod(config, [
|
|
202
|
+
'android',
|
|
203
|
+
async (c) => {
|
|
204
|
+
const text = readSecurityFile(c.modRequest.projectRoot, props.nativeSecurityFile);
|
|
205
|
+
const assets = path.join(c.modRequest.platformProjectRoot, 'app', 'src', 'main', 'assets');
|
|
206
|
+
fs.mkdirSync(assets, { recursive: true });
|
|
207
|
+
fs.writeFileSync(path.join(assets, SECURITY_FILE), text);
|
|
208
|
+
return c;
|
|
209
|
+
},
|
|
210
|
+
]);
|
|
211
|
+
// iOS: a resource of the app target (sealed by the code signature).
|
|
212
|
+
config = withXcodeProject(config, (c) => {
|
|
213
|
+
const text = readSecurityFile(c.modRequest.projectRoot, props.nativeSecurityFile);
|
|
214
|
+
const projectName = c.modRequest.projectName;
|
|
215
|
+
fs.writeFileSync(path.join(c.modRequest.platformProjectRoot, projectName, SECURITY_FILE), text);
|
|
216
|
+
const file = `${projectName}/${SECURITY_FILE}`;
|
|
217
|
+
if (!c.modResults.hasFile(file)) {
|
|
218
|
+
c.modResults = IOSConfig.XcodeUtils.addResourceFileToGroup({
|
|
219
|
+
filepath: file,
|
|
220
|
+
groupName: projectName,
|
|
221
|
+
project: c.modResults,
|
|
222
|
+
isBuildFile: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return c;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return config;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = createRunOncePlugin(withPinVault, pkg.name, pkg.version);
|
|
232
|
+
module.exports.internal = { addBackgroundTaskToAppDelegate, applyInfoPlist, applyAndroidManifest, checkOptions, versionLess };
|