@capgo/cli 4.3.6 → 5.0.0-alpha.7
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 +880 -604
- package/README.md +6 -6
- package/dist/index.js +93 -51
- package/package.json +1 -1
- package/src/api/crypto.ts +17 -12
- package/src/bundle/decrypt.ts +23 -12
- package/src/bundle/encrypt.ts +23 -14
- package/src/bundle/upload.ts +15 -10
- package/src/index.ts +4 -4
- package/src/key.ts +38 -11
- package/src/types/supabase.types.ts +544 -276
- package/src/utils.ts +22 -1
package/README.md
CHANGED
|
@@ -128,8 +128,8 @@ Optionally, you can give:
|
|
|
128
128
|
* `--path [/path/to/my/bundle]` to upload a specific folder.
|
|
129
129
|
* `--channel [test]` to upload to a specific channel.
|
|
130
130
|
* `--external="https://mydomain.com/myapp.zip"` to link to an external URL instead of upload to Capgo cloud, it should be a zip URL in HTTPS.
|
|
131
|
-
* `--key [/path/to/my/
|
|
132
|
-
* `--key-data [
|
|
131
|
+
* `--key [/path/to/my/public_key]` the path of your public key.
|
|
132
|
+
* `--key-data [publicKey]` the public key data, if you want to use inline.
|
|
133
133
|
* `--no-key` to ignore the signing key and send clear update.
|
|
134
134
|
* `--bundle [1.0.0]` to set the bundle version number of the file to upload.
|
|
135
135
|
* `--iv-session-key [key]` to send a custom session key to the cloud.
|
|
@@ -221,9 +221,9 @@ The command will print your `ivSessionKey`y and generate an encrypted zip, to us
|
|
|
221
221
|
|
|
222
222
|
Optionally, you can give:
|
|
223
223
|
|
|
224
|
-
`--key [/path/to/my/
|
|
224
|
+
`--key [/path/to/my/public_key]` the path of your public key.
|
|
225
225
|
|
|
226
|
-
`--key-data [
|
|
226
|
+
`--key-data [publicKey]` the public key data, if you want to use inline. This command is mainly used for test purpose, it will decrypt the zip and print the base64 decrypted session key in the console.
|
|
227
227
|
|
|
228
228
|
### **Zip**
|
|
229
229
|
|
|
@@ -308,9 +308,9 @@ Optionally, you can give: `--force` to overwrite the existing key. This command
|
|
|
308
308
|
|
|
309
309
|
Optionally, you can give:
|
|
310
310
|
|
|
311
|
-
`--key [/path/to/my/
|
|
311
|
+
`--key [/path/to/my/public_key]` the path of your public key.
|
|
312
312
|
|
|
313
|
-
`--key-data [
|
|
313
|
+
`--key-data [publicKey]` the public key data, if you want to use inline. This command is useful if you followed the recommendation and didn't commit the key in your app, and in the config.
|
|
314
314
|
## Dev contribution
|
|
315
315
|
|
|
316
316
|
1. Install development dependencies
|
package/dist/index.js
CHANGED
|
@@ -91634,7 +91634,7 @@ var {
|
|
|
91634
91634
|
// package.json
|
|
91635
91635
|
var package_default = {
|
|
91636
91636
|
name: "@capgo/cli",
|
|
91637
|
-
version: "
|
|
91637
|
+
version: "5.0.0-alpha.7",
|
|
91638
91638
|
description: "A CLI to upload to capgo servers",
|
|
91639
91639
|
main: "dist/index.js",
|
|
91640
91640
|
bin: {
|
|
@@ -93062,6 +93062,19 @@ async function findMainFile() {
|
|
|
93062
93062
|
}
|
|
93063
93063
|
return mainFile;
|
|
93064
93064
|
}
|
|
93065
|
+
async function checKOldEncryption() {
|
|
93066
|
+
const config = await getConfig();
|
|
93067
|
+
const { extConfig } = config.app;
|
|
93068
|
+
const hasPrivateKeyInConfig = !!extConfig?.plugins?.CapacitorUpdater?.privateKey;
|
|
93069
|
+
const hasPublicKeyInConfig = !!extConfig?.plugins?.CapacitorUpdater?.publicKey;
|
|
93070
|
+
if (hasPrivateKeyInConfig)
|
|
93071
|
+
f2.warning(`You still have privateKey in the capacitor config, this is deprecated, please remove it`);
|
|
93072
|
+
f2.warning(`Encryption with private will be ignored`);
|
|
93073
|
+
if (!hasPublicKeyInConfig) {
|
|
93074
|
+
f2.warning(`publicKey not found in capacitor config, please run npx @capgo/cli key save`);
|
|
93075
|
+
program.error("");
|
|
93076
|
+
}
|
|
93077
|
+
}
|
|
93065
93078
|
async function updateOrCreateVersion(supabase, update) {
|
|
93066
93079
|
return supabase.from("app_versions").upsert(update, { onConflict: "name,app_id" }).eq("app_id", update.app_id).eq("name", update.name);
|
|
93067
93080
|
}
|
|
@@ -93637,16 +93650,14 @@ var import_config2 = __toESM(require_config());
|
|
|
93637
93650
|
var import_node_crypto2 = require("node:crypto");
|
|
93638
93651
|
var import_node_buffer2 = require("node:buffer");
|
|
93639
93652
|
var algorithm = "aes-128-cbc";
|
|
93640
|
-
var oaepHash = "sha256";
|
|
93641
93653
|
var formatB64 = "base64";
|
|
93642
|
-
var padding = import_node_crypto2.constants.
|
|
93643
|
-
function decryptSource(source, ivSessionKey,
|
|
93654
|
+
var padding = import_node_crypto2.constants.RSA_PKCS1_PADDING;
|
|
93655
|
+
function decryptSource(source, ivSessionKey, key2) {
|
|
93644
93656
|
const [ivB64, sessionb64Encrypted] = ivSessionKey.split(":");
|
|
93645
|
-
const sessionKey = (0, import_node_crypto2.
|
|
93657
|
+
const sessionKey = (0, import_node_crypto2.publicDecrypt)(
|
|
93646
93658
|
{
|
|
93647
|
-
key:
|
|
93648
|
-
padding
|
|
93649
|
-
oaepHash
|
|
93659
|
+
key: key2,
|
|
93660
|
+
padding
|
|
93650
93661
|
},
|
|
93651
93662
|
import_node_buffer2.Buffer.from(sessionb64Encrypted, formatB64)
|
|
93652
93663
|
);
|
|
@@ -93656,17 +93667,16 @@ function decryptSource(source, ivSessionKey, privateKey) {
|
|
|
93656
93667
|
const decryptedData = import_node_buffer2.Buffer.concat([decipher.update(source), decipher.final()]);
|
|
93657
93668
|
return decryptedData;
|
|
93658
93669
|
}
|
|
93659
|
-
function encryptSource(source,
|
|
93670
|
+
function encryptSource(source, key2) {
|
|
93660
93671
|
const initVector = (0, import_node_crypto2.randomBytes)(16);
|
|
93661
93672
|
const sessionKey = (0, import_node_crypto2.randomBytes)(16);
|
|
93662
93673
|
const cipher = (0, import_node_crypto2.createCipheriv)(algorithm, sessionKey, initVector);
|
|
93663
93674
|
cipher.setAutoPadding(true);
|
|
93664
93675
|
const ivB64 = initVector.toString(formatB64);
|
|
93665
|
-
const sessionb64Encrypted = (0, import_node_crypto2.
|
|
93676
|
+
const sessionb64Encrypted = (0, import_node_crypto2.privateEncrypt)(
|
|
93666
93677
|
{
|
|
93667
|
-
key:
|
|
93668
|
-
padding
|
|
93669
|
-
oaepHash
|
|
93678
|
+
key: key2,
|
|
93679
|
+
padding
|
|
93670
93680
|
},
|
|
93671
93681
|
sessionKey
|
|
93672
93682
|
).toString(formatB64);
|
|
@@ -93700,18 +93710,28 @@ async function saveKey(options, log = true) {
|
|
|
93700
93710
|
oe(`Save keys \u{1F511}`);
|
|
93701
93711
|
const config = await getConfig();
|
|
93702
93712
|
const { extConfig } = config.app;
|
|
93703
|
-
const keyPath = options.key ||
|
|
93704
|
-
let
|
|
93705
|
-
if (!(0, import_node_fs7.existsSync)(keyPath) && !
|
|
93713
|
+
const keyPath = options.key || baseKeyPub;
|
|
93714
|
+
let publicKey = options.keyData || "";
|
|
93715
|
+
if (!(0, import_node_fs7.existsSync)(keyPath) && !publicKey) {
|
|
93706
93716
|
if (log) {
|
|
93707
|
-
f2.error(`Cannot find public key ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`);
|
|
93717
|
+
f2.error(`Cannot find a public key at ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`);
|
|
93708
93718
|
program.error("");
|
|
93709
93719
|
} else {
|
|
93710
93720
|
return false;
|
|
93711
93721
|
}
|
|
93712
93722
|
} else if ((0, import_node_fs7.existsSync)(keyPath)) {
|
|
93713
93723
|
const keyFile = (0, import_node_fs7.readFileSync)(keyPath);
|
|
93714
|
-
|
|
93724
|
+
publicKey = keyFile.toString();
|
|
93725
|
+
}
|
|
93726
|
+
if (publicKey) {
|
|
93727
|
+
if (!publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
93728
|
+
if (log) {
|
|
93729
|
+
f2.error(`the public key provided is not a valid RSA Public key`);
|
|
93730
|
+
program.error("");
|
|
93731
|
+
} else {
|
|
93732
|
+
return false;
|
|
93733
|
+
}
|
|
93734
|
+
}
|
|
93715
93735
|
}
|
|
93716
93736
|
if (extConfig) {
|
|
93717
93737
|
if (!extConfig.plugins) {
|
|
@@ -93722,11 +93742,13 @@ async function saveKey(options, log = true) {
|
|
|
93722
93742
|
}
|
|
93723
93743
|
if (!extConfig.plugins.CapacitorUpdater)
|
|
93724
93744
|
extConfig.plugins.CapacitorUpdater = {};
|
|
93725
|
-
extConfig.plugins.CapacitorUpdater.privateKey
|
|
93745
|
+
if (extConfig.plugins.CapacitorUpdater.privateKey)
|
|
93746
|
+
delete extConfig.plugins.CapacitorUpdater.privateKey;
|
|
93747
|
+
extConfig.plugins.CapacitorUpdater.publicKey = publicKey;
|
|
93726
93748
|
(0, import_config2.writeConfig)(extConfig, config.app.extConfigFilePath);
|
|
93727
93749
|
}
|
|
93728
93750
|
if (log) {
|
|
93729
|
-
f2.success(`
|
|
93751
|
+
f2.success(`public key saved into ${config.app.extConfigFilePath} file in local directory`);
|
|
93730
93752
|
f2.success(`your app will decode the zip archive with this key`);
|
|
93731
93753
|
}
|
|
93732
93754
|
return true;
|
|
@@ -93767,19 +93789,23 @@ async function createKey(options, log = true) {
|
|
|
93767
93789
|
CapacitorUpdater: {}
|
|
93768
93790
|
};
|
|
93769
93791
|
}
|
|
93770
|
-
extConfig.plugins.CapacitorUpdater
|
|
93792
|
+
if (!extConfig.plugins.CapacitorUpdater)
|
|
93793
|
+
extConfig.plugins.CapacitorUpdater = {};
|
|
93794
|
+
if (extConfig.plugins.CapacitorUpdater.privateKey)
|
|
93795
|
+
delete extConfig.plugins.CapacitorUpdater.privateKey;
|
|
93796
|
+
extConfig.plugins.CapacitorUpdater.publicKey = publicKey;
|
|
93771
93797
|
(0, import_config2.writeConfig)(extConfig, config.app.extConfigFilePath);
|
|
93772
93798
|
}
|
|
93773
93799
|
if (log) {
|
|
93774
93800
|
f2.success("Your RSA key has been generated");
|
|
93775
|
-
f2.success(`
|
|
93801
|
+
f2.success(`Private key saved in ${baseKey}`);
|
|
93776
93802
|
f2.success("This key will be use to encrypt your bundle before sending it to Capgo");
|
|
93777
93803
|
f2.success("Keep it safe");
|
|
93778
93804
|
f2.success("Than make it unreadable by Capgo and unmodifiable by anyone");
|
|
93779
|
-
f2.success(`
|
|
93805
|
+
f2.success(`Public key saved in ${config.app.extConfigFilePath}`);
|
|
93780
93806
|
f2.success("Your app will be the only one having it");
|
|
93781
93807
|
f2.success("Only your users can decrypt your update");
|
|
93782
|
-
f2.success("Only
|
|
93808
|
+
f2.success("Only your key can send them an update");
|
|
93783
93809
|
$e(`Done \u2705`);
|
|
93784
93810
|
}
|
|
93785
93811
|
return true;
|
|
@@ -94101,14 +94127,15 @@ Trial expires in ${isTrial} days`);
|
|
|
94101
94127
|
s.stop(`Checksum: ${checksum}`);
|
|
94102
94128
|
if (key2 === false) {
|
|
94103
94129
|
f2.info(`Encryption ignored`);
|
|
94104
|
-
} else if (key2 || (0, import_node_fs8.existsSync)(
|
|
94105
|
-
|
|
94130
|
+
} else if (key2 || (0, import_node_fs8.existsSync)(baseKey)) {
|
|
94131
|
+
await checKOldEncryption();
|
|
94132
|
+
const privateKey = typeof key2 === "string" ? key2 : baseKey;
|
|
94106
94133
|
let keyData = options.keyData || "";
|
|
94107
|
-
if (!keyData && !(0, import_node_fs8.existsSync)(
|
|
94108
|
-
f2.error(`Cannot find
|
|
94134
|
+
if (!keyData && !(0, import_node_fs8.existsSync)(privateKey)) {
|
|
94135
|
+
f2.error(`Cannot find private key ${privateKey}`);
|
|
94109
94136
|
if (import_ci_info.default.isCI)
|
|
94110
94137
|
program.error("");
|
|
94111
|
-
const res2 = await se({ message: "Do you want to use our
|
|
94138
|
+
const res2 = await se({ message: "Do you want to use our private key ?" });
|
|
94112
94139
|
if (!res2) {
|
|
94113
94140
|
f2.error(`Error: Missing public key`);
|
|
94114
94141
|
program.error("");
|
|
@@ -94126,9 +94153,13 @@ Trial expires in ${isTrial} days`);
|
|
|
94126
94153
|
notify: false
|
|
94127
94154
|
}).catch();
|
|
94128
94155
|
if (!keyData) {
|
|
94129
|
-
const keyFile = (0, import_node_fs8.readFileSync)(
|
|
94156
|
+
const keyFile = (0, import_node_fs8.readFileSync)(privateKey);
|
|
94130
94157
|
keyData = keyFile.toString();
|
|
94131
94158
|
}
|
|
94159
|
+
if (keyData && !keyData.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
|
|
94160
|
+
f2.error(`the private key provided is not a valid RSA Private key`);
|
|
94161
|
+
program.error("");
|
|
94162
|
+
}
|
|
94132
94163
|
f2.info(`Encrypting your bundle`);
|
|
94133
94164
|
const res = encryptSource(zipped, keyData);
|
|
94134
94165
|
sessionKey = res.ivSessionKey;
|
|
@@ -94999,23 +95030,29 @@ async function decryptZip(zipPath, ivsessionKey, options) {
|
|
|
94999
95030
|
}
|
|
95000
95031
|
const config = await getConfig();
|
|
95001
95032
|
const { extConfig } = config.app;
|
|
95002
|
-
|
|
95003
|
-
|
|
95033
|
+
await checKOldEncryption();
|
|
95034
|
+
if (!options.key && !(0, import_node_fs12.existsSync)(baseKeyPub) && !extConfig.plugins?.CapacitorUpdater?.publicKey) {
|
|
95035
|
+
f2.error(`Public key not found at the path ${baseKeyPub} or in ${config.app.extConfigFilePath}`);
|
|
95004
95036
|
program.error("");
|
|
95005
95037
|
}
|
|
95006
|
-
const keyPath = options.key ||
|
|
95007
|
-
let
|
|
95008
|
-
if (!(0, import_node_fs12.existsSync)(keyPath) && !
|
|
95009
|
-
f2.error(`Cannot find public key ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`);
|
|
95038
|
+
const keyPath = options.key || baseKeyPub;
|
|
95039
|
+
let publicKey = extConfig?.plugins?.CapacitorUpdater?.publicKey;
|
|
95040
|
+
if (!(0, import_node_fs12.existsSync)(keyPath) && !publicKey) {
|
|
95041
|
+
f2.error(`Cannot find a public key at ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`);
|
|
95010
95042
|
program.error("");
|
|
95011
95043
|
} else if ((0, import_node_fs12.existsSync)(keyPath)) {
|
|
95012
95044
|
const keyFile = (0, import_node_fs12.readFileSync)(keyPath);
|
|
95013
|
-
|
|
95045
|
+
publicKey = keyFile.toString();
|
|
95046
|
+
}
|
|
95047
|
+
if (publicKey && !publicKey.startsWith("-----BEGIN RSA PUBLIC KEY-----")) {
|
|
95048
|
+
f2.error(`the public key provided is not a valid RSA Public key`);
|
|
95049
|
+
program.error("");
|
|
95014
95050
|
}
|
|
95015
95051
|
const zipFile = (0, import_node_fs12.readFileSync)(zipPath);
|
|
95016
|
-
const decodedZip = decryptSource(zipFile, ivsessionKey, options.keyData ??
|
|
95052
|
+
const decodedZip = decryptSource(zipFile, ivsessionKey, options.keyData ?? publicKey ?? "");
|
|
95017
95053
|
(0, import_node_fs12.writeFileSync)(`${zipPath}_decrypted.zip`, decodedZip);
|
|
95018
|
-
|
|
95054
|
+
f2.success(`Decrypted zip file at ${zipPath}_decrypted.zip`);
|
|
95055
|
+
$e(`Done \u2705`);
|
|
95019
95056
|
import_node_process19.default.exit();
|
|
95020
95057
|
}
|
|
95021
95058
|
|
|
@@ -95027,30 +95064,35 @@ async function encryptZip(zipPath, options) {
|
|
|
95027
95064
|
oe(`Encryption`);
|
|
95028
95065
|
await checkLatest();
|
|
95029
95066
|
const localConfig = await getLocalConfig();
|
|
95067
|
+
await checKOldEncryption();
|
|
95030
95068
|
if (!(0, import_node_fs13.existsSync)(zipPath)) {
|
|
95031
95069
|
f2.error(`Error: Zip not found at the path ${zipPath}`);
|
|
95032
95070
|
program.error("");
|
|
95033
95071
|
}
|
|
95034
|
-
const keyPath = options.key ||
|
|
95035
|
-
let
|
|
95036
|
-
if (!(0, import_node_fs13.existsSync)(keyPath) && !
|
|
95037
|
-
f2.warning(`Cannot find
|
|
95072
|
+
const keyPath = options.key || baseKey;
|
|
95073
|
+
let privateKey = options.keyData || "";
|
|
95074
|
+
if (!(0, import_node_fs13.existsSync)(keyPath) && !privateKey) {
|
|
95075
|
+
f2.warning(`Cannot find a private key at ${keyPath} or as a keyData option`);
|
|
95038
95076
|
if (import_ci_info2.default.isCI) {
|
|
95039
|
-
f2.error(`Error: Missing
|
|
95077
|
+
f2.error(`Error: Missing key`);
|
|
95040
95078
|
program.error("");
|
|
95041
95079
|
}
|
|
95042
|
-
const res = await se({ message:
|
|
95080
|
+
const res = await se({ message: `Do you want to use our private key?` });
|
|
95043
95081
|
if (!res) {
|
|
95044
|
-
f2.error(`Error: Missing
|
|
95082
|
+
f2.error(`Error: Missing private key`);
|
|
95045
95083
|
program.error("");
|
|
95046
95084
|
}
|
|
95047
|
-
|
|
95085
|
+
privateKey = localConfig.signKey || "";
|
|
95048
95086
|
} else if ((0, import_node_fs13.existsSync)(keyPath)) {
|
|
95049
95087
|
const keyFile = (0, import_node_fs13.readFileSync)(keyPath);
|
|
95050
|
-
|
|
95088
|
+
privateKey = keyFile.toString();
|
|
95089
|
+
}
|
|
95090
|
+
if (privateKey && !privateKey.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
|
|
95091
|
+
f2.error(`the private key provided is not a valid RSA Private key`);
|
|
95092
|
+
program.error("");
|
|
95051
95093
|
}
|
|
95052
95094
|
const zipFile = (0, import_node_fs13.readFileSync)(zipPath);
|
|
95053
|
-
const encodedZip = encryptSource(zipFile,
|
|
95095
|
+
const encodedZip = encryptSource(zipFile, privateKey);
|
|
95054
95096
|
f2.success(`ivSessionKey: ${encodedZip.ivSessionKey}`);
|
|
95055
95097
|
(0, import_node_fs13.writeFileSync)(`${zipPath}_encrypted.zip`, encodedZip.encryptedData);
|
|
95056
95098
|
f2.success(`Encrypted zip saved at ${zipPath}_encrypted.zip`);
|
|
@@ -95736,7 +95778,7 @@ bundle.command("compatibility [appId]").action(checkCompatibilityCommand).option
|
|
|
95736
95778
|
bundle.command("delete [bundleId] [appId]").alias("d").description("Delete a bundle in Capgo Cloud").action(deleteBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
|
95737
95779
|
bundle.command("list [appId]").alias("l").description("List bundle in Capgo Cloud").action(listBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
|
95738
95780
|
bundle.command("cleanup [appId]").alias("c").action(cleanupBundle).description("Cleanup bundle in Capgo Cloud").option("-b, --bundle <bundle>", "bundle version number of the app to delete").option("-a, --apikey <apikey>", "apikey to link to your account").option("-k, --keep <keep>", "number of version to keep").option("-f, --force", "force removal");
|
|
95739
|
-
bundle.command("decrypt [zipPath] [sessionKey]").description("Decrypt a signed zip bundle").action(decryptZip).option("--key <key>", "custom path for
|
|
95781
|
+
bundle.command("decrypt [zipPath] [sessionKey]").description("Decrypt a signed zip bundle").action(decryptZip).option("--key <key>", "custom path for public signing key").option("--key-data <keyData>", "base64 public signing key");
|
|
95740
95782
|
bundle.command("encrypt [zipPath]").description("Encrypt a zip bundle").action(encryptZip).option("--key <key>", "custom path for private signing key").option("--key-data <keyData>", "base64 private signing key");
|
|
95741
95783
|
bundle.command("zip [appId]").description("Zip a bundle").action(zipBundle).option("-p, --path <path>", "path of the folder to upload").option("-b, --bundle <bundle>", "bundle version number to name the zip file").option("-n, --name <name>", "name of the zip file").option("-j, --json", "output in JSON").option("--no-code-check", "Ignore checking if notifyAppReady() is called in soure code and index present in root folder");
|
|
95742
95784
|
var channel = program.command("channel").description("Manage channel");
|
|
@@ -95746,7 +95788,7 @@ channel.command("list [appId]").alias("l").description("List channel").action(li
|
|
|
95746
95788
|
channel.command("currentBundle [channel] [appId]").description("Get current bundle for specific channel in Capgo Cloud").action(currentBundle).option("-c, --channel <channel>", "channel to get the current bundle from").option("-a, --apikey <apikey>", "apikey to link to your account").option("--quiet", "only print the bundle version");
|
|
95747
95789
|
channel.command("set [channelId] [appId]").alias("s").description("Set channel").action(setChannel).option("-a, --apikey <apikey>", "apikey to link to your account").option("-b, --bundle <bundle>", "bundle version number of the file to set").option("-s, --state <state>", "set the state of the channel, default or normal").option("--latest", "get the latest version key in the package.json to set it to the channel").option("--downgrade", "Allow to downgrade to version under native one").option("--no-downgrade", "Disable downgrade to version under native one").option("--upgrade", "Allow to upgrade to version above native one").option("--no-upgrade", "Disable upgrade to version above native one").option("--ios", "Allow sending update to ios devices").option("--no-ios", "Disable sending update to ios devices").option("--android", "Allow sending update to android devices").option("--no-android", "Disable sending update to android devices").option("--self-assign", "Allow to device to self assign to this channel").option("--no-self-assign", "Disable devices to self assign to this channel").option("--disable-auto-update <disableAutoUpdate>", "Disable auto update strategy for this channel.The possible options are: major, minor, metadata, patch, none");
|
|
95748
95790
|
var key = program.command("key").description("Manage key");
|
|
95749
|
-
key.command("save").description("Save base64 signing key in capacitor config,
|
|
95791
|
+
key.command("save").description("Save base64 signing key in capacitor config, useful for CI").action(saveKeyCommand).option("-f, --force", "force generate a new one").option("--key", "key path to save in capacitor config").option("--key-data <keyData>", "key data to save in capacitor config");
|
|
95750
95792
|
key.command("create").description("Create a new signing key").action(createKeyCommand).option("-f, --force", "force generate a new one");
|
|
95751
95793
|
program.command("upload [appId]").alias("u").description("(Deprecated) Upload a new bundle to Capgo Cloud").action(uploadDeprecatedCommand).option("-a, --apikey <apikey>", "apikey to link to your account").option("-p, --path <path>", "path of the folder to upload").option("-c, --channel <channel>", "channel to link to").option("-e, --external <url>", "link to external url intead of upload to Capgo Cloud").option("--key <key>", "custom path for public signing key").option("--key-data <keyData>", "base64 public signing key").option("--bundle-url", "prints bundle url into stdout").option("--no-key", "ignore signing key and send clear update").option("--display-iv-session", "Show in the console the iv and session key used to encrypt the update").option("-b, --bundle <bundle>", "bundle version number of the file to upload").option(
|
|
95752
95794
|
"--min-update-version <minUpdateVersion>",
|
package/package.json
CHANGED
package/src/api/crypto.ts
CHANGED
|
@@ -3,30 +3,31 @@ import {
|
|
|
3
3
|
createCipheriv,
|
|
4
4
|
createDecipheriv,
|
|
5
5
|
generateKeyPairSync,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
privateEncrypt,
|
|
7
|
+
publicDecrypt,
|
|
8
8
|
randomBytes,
|
|
9
9
|
} from 'node:crypto'
|
|
10
10
|
import { Buffer } from 'node:buffer'
|
|
11
11
|
|
|
12
12
|
const algorithm = 'aes-128-cbc'
|
|
13
|
-
const oaepHash = 'sha256'
|
|
14
13
|
const formatB64 = 'base64'
|
|
15
|
-
const padding = constants.
|
|
14
|
+
const padding = constants.RSA_PKCS1_PADDING
|
|
16
15
|
|
|
17
|
-
export function decryptSource(source: Buffer, ivSessionKey: string,
|
|
16
|
+
export function decryptSource(source: Buffer, ivSessionKey: string, key: string): Buffer {
|
|
17
|
+
// console.log('decryptKeyType - ', decryptKeyType);
|
|
18
|
+
// console.log(key);
|
|
18
19
|
// console.log('\nivSessionKey', ivSessionKey)
|
|
19
20
|
const [ivB64, sessionb64Encrypted] = ivSessionKey.split(':')
|
|
20
21
|
// console.log('\nsessionb64Encrypted', sessionb64Encrypted)
|
|
21
22
|
// console.log('\nivB64', ivB64)
|
|
22
|
-
const sessionKey =
|
|
23
|
+
const sessionKey = publicDecrypt(
|
|
23
24
|
{
|
|
24
|
-
key
|
|
25
|
+
key,
|
|
25
26
|
padding,
|
|
26
|
-
oaepHash,
|
|
27
27
|
},
|
|
28
28
|
Buffer.from(sessionb64Encrypted, formatB64),
|
|
29
29
|
)
|
|
30
|
+
|
|
30
31
|
// ivB64 to uft-8
|
|
31
32
|
const initVector = Buffer.from(ivB64, formatB64)
|
|
32
33
|
// console.log('\nSessionB64', sessionB64)
|
|
@@ -41,7 +42,10 @@ export interface Encoded {
|
|
|
41
42
|
ivSessionKey: string
|
|
42
43
|
encryptedData: Buffer
|
|
43
44
|
}
|
|
44
|
-
export function encryptSource(source: Buffer,
|
|
45
|
+
export function encryptSource(source: Buffer, key: string): Encoded {
|
|
46
|
+
// console.log('decryptKeyType - ', decryptKeyType);
|
|
47
|
+
// console.log(key);
|
|
48
|
+
|
|
45
49
|
// encrypt zip with key
|
|
46
50
|
const initVector = randomBytes(16)
|
|
47
51
|
const sessionKey = randomBytes(16)
|
|
@@ -54,14 +58,15 @@ export function encryptSource(source: Buffer, publicKey: string): Encoded {
|
|
|
54
58
|
// console.log('\nsessionB64', sessionB64)
|
|
55
59
|
const ivB64 = initVector.toString(formatB64)
|
|
56
60
|
// console.log('\nivB64', ivB64)
|
|
57
|
-
|
|
61
|
+
|
|
62
|
+
const sessionb64Encrypted = privateEncrypt(
|
|
58
63
|
{
|
|
59
|
-
key
|
|
64
|
+
key,
|
|
60
65
|
padding,
|
|
61
|
-
oaepHash,
|
|
62
66
|
},
|
|
63
67
|
sessionKey,
|
|
64
68
|
).toString(formatB64)
|
|
69
|
+
|
|
65
70
|
// console.log('\nsessionb64Encrypted', sessionb64Encrypted)
|
|
66
71
|
const ivSessionKey = `${ivB64}:${sessionb64Encrypted}`
|
|
67
72
|
// console.log('\nivSessionKey', sessionKey)
|
package/src/bundle/decrypt.ts
CHANGED
|
@@ -3,7 +3,7 @@ import process from 'node:process'
|
|
|
3
3
|
import { program } from 'commander'
|
|
4
4
|
import * as p from '@clack/prompts'
|
|
5
5
|
import { decryptSource } from '../api/crypto'
|
|
6
|
-
import {
|
|
6
|
+
import { baseKeyPub, checKOldEncryption, getConfig } from '../utils'
|
|
7
7
|
import { checkLatest } from '../api/update'
|
|
8
8
|
|
|
9
9
|
interface Options {
|
|
@@ -23,32 +23,43 @@ export async function decryptZip(zipPath: string, ivsessionKey: string, options:
|
|
|
23
23
|
|
|
24
24
|
const config = await getConfig()
|
|
25
25
|
const { extConfig } = config.app
|
|
26
|
+
// console.log('config - ', config)
|
|
27
|
+
// console.log('extConfig - ', extConfig)
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
await checKOldEncryption()
|
|
30
|
+
// console.log(`There ${hasPrivateKeyInConfig ? 'IS' : 'IS NOT'} a privateKey in the config`);
|
|
31
|
+
|
|
32
|
+
if (!options.key && !existsSync(baseKeyPub) && !extConfig.plugins?.CapacitorUpdater?.publicKey) {
|
|
33
|
+
p.log.error(`Public key not found at the path ${baseKeyPub} or in ${config.app.extConfigFilePath}`)
|
|
29
34
|
program.error('')
|
|
30
35
|
}
|
|
31
|
-
const keyPath = options.key ||
|
|
32
|
-
// check if
|
|
36
|
+
const keyPath = options.key || baseKeyPub
|
|
37
|
+
// check if private exist
|
|
33
38
|
|
|
34
|
-
let
|
|
39
|
+
let publicKey = extConfig?.plugins?.CapacitorUpdater?.publicKey
|
|
35
40
|
|
|
36
|
-
if (!existsSync(keyPath) && !
|
|
37
|
-
p.log.error(`Cannot find public key ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`)
|
|
41
|
+
if (!existsSync(keyPath) && !publicKey) {
|
|
42
|
+
p.log.error(`Cannot find a public key at ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`)
|
|
38
43
|
program.error('')
|
|
39
44
|
}
|
|
40
45
|
else if (existsSync(keyPath)) {
|
|
41
46
|
// open with fs publicKey path
|
|
42
47
|
const keyFile = readFileSync(keyPath)
|
|
43
|
-
|
|
48
|
+
publicKey = keyFile.toString()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// let's doublecheck and make sure the key we are using is the right type based on the decryption strategy
|
|
52
|
+
if (publicKey && !publicKey.startsWith('-----BEGIN RSA PUBLIC KEY-----')) {
|
|
53
|
+
p.log.error(`the public key provided is not a valid RSA Public key`)
|
|
54
|
+
program.error('')
|
|
44
55
|
}
|
|
45
|
-
// console.log('privateKey', privateKey)
|
|
46
56
|
|
|
47
57
|
const zipFile = readFileSync(zipPath)
|
|
48
58
|
|
|
49
|
-
const decodedZip = decryptSource(zipFile, ivsessionKey, options.keyData ??
|
|
59
|
+
const decodedZip = decryptSource(zipFile, ivsessionKey, options.keyData ?? publicKey ?? '')
|
|
50
60
|
// write decodedZip in a file
|
|
51
61
|
writeFileSync(`${zipPath}_decrypted.zip`, decodedZip)
|
|
52
|
-
p.
|
|
62
|
+
p.log.success(`Decrypted zip file at ${zipPath}_decrypted.zip`)
|
|
63
|
+
p.outro(`Done ✅`)
|
|
53
64
|
process.exit()
|
|
54
65
|
}
|
package/src/bundle/encrypt.ts
CHANGED
|
@@ -5,7 +5,7 @@ import ciDetect from 'ci-info'
|
|
|
5
5
|
import * as p from '@clack/prompts'
|
|
6
6
|
import { checkLatest } from '../api/update'
|
|
7
7
|
import { encryptSource } from '../api/crypto'
|
|
8
|
-
import {
|
|
8
|
+
import { baseKey, checKOldEncryption, getLocalConfig } from '../utils'
|
|
9
9
|
|
|
10
10
|
interface Options {
|
|
11
11
|
key?: string
|
|
@@ -17,40 +17,49 @@ export async function encryptZip(zipPath: string, options: Options) {
|
|
|
17
17
|
|
|
18
18
|
await checkLatest()
|
|
19
19
|
const localConfig = await getLocalConfig()
|
|
20
|
+
// console.log('localConfig - ', localConfig)
|
|
21
|
+
// console.log('config - ', config)
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
await checKOldEncryption()
|
|
22
24
|
|
|
23
25
|
if (!existsSync(zipPath)) {
|
|
24
26
|
p.log.error(`Error: Zip not found at the path ${zipPath}`)
|
|
25
27
|
program.error('')
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const keyPath = options.key ||
|
|
29
|
-
// check if
|
|
30
|
+
const keyPath = options.key || baseKey
|
|
31
|
+
// check if privateKey exist
|
|
30
32
|
|
|
31
|
-
let
|
|
33
|
+
let privateKey = options.keyData || ''
|
|
32
34
|
|
|
33
|
-
if (!existsSync(keyPath) && !
|
|
34
|
-
p.log.warning(`Cannot find
|
|
35
|
+
if (!existsSync(keyPath) && !privateKey) {
|
|
36
|
+
p.log.warning(`Cannot find a private key at ${keyPath} or as a keyData option`)
|
|
35
37
|
if (ciDetect.isCI) {
|
|
36
|
-
p.log.error(`Error: Missing
|
|
38
|
+
p.log.error(`Error: Missing key`)
|
|
37
39
|
program.error('')
|
|
38
40
|
}
|
|
39
|
-
const res = await p.confirm({ message:
|
|
41
|
+
const res = await p.confirm({ message: `Do you want to use our private key?` })
|
|
40
42
|
if (!res) {
|
|
41
|
-
p.log.error(`Error: Missing
|
|
43
|
+
p.log.error(`Error: Missing private key`)
|
|
42
44
|
program.error('')
|
|
43
45
|
}
|
|
44
|
-
|
|
46
|
+
|
|
47
|
+
privateKey = localConfig.signKey || ''
|
|
45
48
|
}
|
|
46
49
|
else if (existsSync(keyPath)) {
|
|
47
|
-
// open with fs
|
|
50
|
+
// open with fs key path
|
|
48
51
|
const keyFile = readFileSync(keyPath)
|
|
49
|
-
|
|
52
|
+
privateKey = keyFile.toString()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// let's doublecheck and make sure the key we are using is the right type based on the decryption strategy
|
|
56
|
+
if (privateKey && !privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----')) {
|
|
57
|
+
p.log.error(`the private key provided is not a valid RSA Private key`)
|
|
58
|
+
program.error('')
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
const zipFile = readFileSync(zipPath)
|
|
53
|
-
const encodedZip = encryptSource(zipFile,
|
|
62
|
+
const encodedZip = encryptSource(zipFile, privateKey)
|
|
54
63
|
p.log.success(`ivSessionKey: ${encodedZip.ivSessionKey}`)
|
|
55
64
|
// write decodedZip in a file
|
|
56
65
|
writeFileSync(`${zipPath}_encrypted.zip`, encodedZip.encryptedData)
|
package/src/bundle/upload.ts
CHANGED
|
@@ -21,7 +21,8 @@ import type {
|
|
|
21
21
|
import {
|
|
22
22
|
EMPTY_UUID,
|
|
23
23
|
OrganizationPerm,
|
|
24
|
-
|
|
24
|
+
baseKey,
|
|
25
|
+
checKOldEncryption,
|
|
25
26
|
checkCompatibility,
|
|
26
27
|
checkPlanValid,
|
|
27
28
|
convertAppName,
|
|
@@ -260,20 +261,20 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
|
|
|
260
261
|
s.start(`Calculating checksum`)
|
|
261
262
|
checksum = await getChecksum(zipped, 'crc32')
|
|
262
263
|
s.stop(`Checksum: ${checksum}`)
|
|
263
|
-
// key should be undefined or a string if false it should ingore encryption
|
|
264
264
|
if (key === false) {
|
|
265
265
|
p.log.info(`Encryption ignored`)
|
|
266
266
|
}
|
|
267
|
-
else if (key || existsSync(
|
|
268
|
-
|
|
267
|
+
else if (key || existsSync(baseKey)) {
|
|
268
|
+
await checKOldEncryption()
|
|
269
|
+
const privateKey = typeof key === 'string' ? key : baseKey
|
|
269
270
|
let keyData = options.keyData || ''
|
|
270
|
-
// check if
|
|
271
|
-
if (!keyData && !existsSync(
|
|
272
|
-
p.log.error(`Cannot find
|
|
271
|
+
// check if privateKey exist
|
|
272
|
+
if (!keyData && !existsSync(privateKey)) {
|
|
273
|
+
p.log.error(`Cannot find private key ${privateKey}`)
|
|
273
274
|
if (ciDetect.isCI)
|
|
274
275
|
program.error('')
|
|
275
276
|
|
|
276
|
-
const res = await p.confirm({ message: 'Do you want to use our
|
|
277
|
+
const res = await p.confirm({ message: 'Do you want to use our private key ?' })
|
|
277
278
|
if (!res) {
|
|
278
279
|
p.log.error(`Error: Missing public key`)
|
|
279
280
|
program.error('')
|
|
@@ -290,12 +291,16 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
|
|
|
290
291
|
},
|
|
291
292
|
notify: false,
|
|
292
293
|
}).catch()
|
|
293
|
-
// open with fs
|
|
294
|
+
// open with fs privateKey path
|
|
294
295
|
if (!keyData) {
|
|
295
|
-
const keyFile = readFileSync(
|
|
296
|
+
const keyFile = readFileSync(privateKey)
|
|
296
297
|
keyData = keyFile.toString()
|
|
297
298
|
}
|
|
298
299
|
// encrypt
|
|
300
|
+
if (keyData && !keyData.startsWith('-----BEGIN RSA PRIVATE KEY-----')) {
|
|
301
|
+
p.log.error(`the private key provided is not a valid RSA Private key`)
|
|
302
|
+
program.error('')
|
|
303
|
+
}
|
|
299
304
|
p.log.info(`Encrypting your bundle`)
|
|
300
305
|
const res = encryptSource(zipped, keyData)
|
|
301
306
|
sessionKey = res.ivSessionKey
|
package/src/index.ts
CHANGED
|
@@ -174,8 +174,8 @@ bundle
|
|
|
174
174
|
.command('decrypt [zipPath] [sessionKey]')
|
|
175
175
|
.description('Decrypt a signed zip bundle')
|
|
176
176
|
.action(decryptZip)
|
|
177
|
-
.option('--key <key>', 'custom path for
|
|
178
|
-
.option('--key-data <keyData>', 'base64
|
|
177
|
+
.option('--key <key>', 'custom path for public signing key')
|
|
178
|
+
.option('--key-data <keyData>', 'base64 public signing key')
|
|
179
179
|
|
|
180
180
|
bundle
|
|
181
181
|
.command('encrypt [zipPath]')
|
|
@@ -255,11 +255,11 @@ const key = program
|
|
|
255
255
|
|
|
256
256
|
key
|
|
257
257
|
.command('save')
|
|
258
|
-
.description('Save base64 signing key in capacitor config,
|
|
258
|
+
.description('Save base64 signing key in capacitor config, useful for CI')
|
|
259
259
|
.action(saveKeyCommand)
|
|
260
260
|
.option('-f, --force', 'force generate a new one')
|
|
261
261
|
.option('--key', 'key path to save in capacitor config')
|
|
262
|
-
.option('--key-data', 'key data to save in capacitor config')
|
|
262
|
+
.option('--key-data <keyData>', 'key data to save in capacitor config')
|
|
263
263
|
|
|
264
264
|
key
|
|
265
265
|
.command('create')
|