@scalebun/react-native 1.11.1 → 2.0.0
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/android/build.gradle +8 -0
- package/android/src/androidTest/java/com/scalebun/rn/ota/ScaleBunOtaVerifierInstrumentedTest.kt +112 -0
- package/android/src/main/java/com/scalebun/rn/ota/OtaProtocol.kt +244 -0
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaKeyRegistry.kt +100 -0
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +115 -60
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaReleaseVerifier.kt +129 -0
- package/android/src/main/java/com/scalebun/rn/ota/SlotManager.kt +18 -0
- package/android/src/oldarch/java/com/scalebun/rn/ota/ScaleBunOtaSpec.kt +4 -8
- package/dist/scalebun.full.js +127 -422
- package/dist/scalebun.slim.js +127 -422
- package/ios/Ota/OtaProtocol.swift +242 -0
- package/ios/Ota/OtaSlotManager.swift +26 -0
- package/ios/Ota/ScaleBunOtaBridge.mm +6 -3
- package/ios/Ota/ScaleBunOtaKeyRegistry.swift +147 -0
- package/ios/Ota/ScaleBunOtaModule.swift +125 -53
- package/ios/Ota/ScaleBunOtaReleaseVerifier.swift +115 -0
- package/ios/Ota/ScaleBunOtaVerifierTests.swift +92 -0
- package/lib/commonjs/core/config/schema.js +3 -12
- package/lib/commonjs/core/constants/version.js +1 -1
- package/lib/commonjs/features/network/NetworkFeature.js +39 -5
- package/lib/commonjs/features/network/thirdParty.js +90 -0
- package/lib/commonjs/features/ota/OtaOrchestrator.js +108 -60
- package/lib/commonjs/public/ScaleBunFacade.js +5 -41
- package/lib/module/core/config/schema.js +3 -12
- package/lib/module/core/constants/version.js +1 -1
- package/lib/module/features/network/NetworkFeature.js +38 -4
- package/lib/module/features/network/thirdParty.js +83 -0
- package/lib/module/features/ota/OtaOrchestrator.js +108 -60
- package/lib/module/public/ScaleBunFacade.js +5 -41
- package/lib/typescript/core/config/schema.d.ts +0 -3
- package/lib/typescript/core/constants/version.d.ts +1 -1
- package/lib/typescript/features/network/index.d.ts +18 -0
- package/lib/typescript/features/network/thirdParty.d.ts +65 -0
- package/lib/typescript/features/ota/OtaOrchestrator.d.ts +5 -8
- package/lib/typescript/features/ota/OtaTypes.d.ts +22 -0
- package/lib/typescript/public/ScaleBunFacade.d.ts +5 -11
- package/lib/typescript/specs/NativeScaleBunOta.d.ts +30 -20
- package/package.json +3 -13
- package/scalebun-react-native.podspec +4 -0
- package/src/core/config/schema.ts +3 -9
- package/src/core/constants/version.ts +1 -1
- package/src/features/network/NetworkFeature.ts +41 -4
- package/src/features/network/index.ts +18 -0
- package/src/features/network/thirdParty.ts +92 -0
- package/src/features/ota/OtaOrchestrator.ts +116 -70
- package/src/features/ota/OtaTypes.ts +23 -1
- package/src/public/ScaleBunFacade.ts +5 -53
- package/src/specs/NativeScaleBunOta.ts +32 -20
- package/lib/commonjs/features/ota/crypto/builtinVerifier.js +0 -248
- package/lib/commonjs/features/ota/crypto/loadEd25519.js +0 -40
- package/lib/commonjs/features/ota/crypto/loadSha512.js +0 -40
- package/lib/commonjs/features/ota/crypto/nativeVerifier.js +0 -121
- package/lib/commonjs/features/ota/signature.js +0 -175
- package/lib/module/features/ota/crypto/builtinVerifier.js +0 -240
- package/lib/module/features/ota/crypto/loadEd25519.js +0 -34
- package/lib/module/features/ota/crypto/loadSha512.js +0 -34
- package/lib/module/features/ota/crypto/nativeVerifier.js +0 -113
- package/lib/module/features/ota/signature.js +0 -169
- package/lib/typescript/features/ota/crypto/builtinVerifier.d.ts +0 -53
- package/lib/typescript/features/ota/crypto/loadEd25519.d.ts +0 -30
- package/lib/typescript/features/ota/crypto/loadSha512.d.ts +0 -15
- package/lib/typescript/features/ota/crypto/nativeVerifier.d.ts +0 -35
- package/lib/typescript/features/ota/signature.d.ts +0 -81
- package/src/features/ota/crypto/builtinVerifier.ts +0 -257
- package/src/features/ota/crypto/loadEd25519.ts +0 -41
- package/src/features/ota/crypto/loadSha512.ts +0 -35
- package/src/features/ota/crypto/nativeVerifier.ts +0 -117
- package/src/features/ota/signature.ts +0 -206
package/dist/scalebun.slim.js
CHANGED
|
@@ -92,9 +92,9 @@ var init_internalLogger = __esm({
|
|
|
92
92
|
}
|
|
93
93
|
/** Set level from the public string name used in config (e.g. 'warn'). */
|
|
94
94
|
setLevelByName(name) {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
97
|
-
this.level =
|
|
95
|
+
const resolved = LEVEL_BY_NAME[name];
|
|
96
|
+
if (resolved !== void 0) {
|
|
97
|
+
this.level = resolved;
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
/** Current numeric level — exposed for callers that branch on dev verbosity. */
|
|
@@ -600,7 +600,7 @@ var SDK_VERSION;
|
|
|
600
600
|
var init_version = __esm({
|
|
601
601
|
"lib/module/core/constants/version.js"() {
|
|
602
602
|
"use strict";
|
|
603
|
-
SDK_VERSION = "
|
|
603
|
+
SDK_VERSION = "2.0.0";
|
|
604
604
|
}
|
|
605
605
|
});
|
|
606
606
|
|
|
@@ -6993,322 +6993,6 @@ var init_deviceAttributes = __esm({
|
|
|
6993
6993
|
}
|
|
6994
6994
|
});
|
|
6995
6995
|
|
|
6996
|
-
// lib/module/features/ota/crypto/loadEd25519.js
|
|
6997
|
-
function loadEd25519() {
|
|
6998
|
-
try {
|
|
6999
|
-
const mod = require("@noble/ed25519");
|
|
7000
|
-
const ed = mod?.default ?? mod;
|
|
7001
|
-
if (typeof ed?.verifyAsync === "function" || typeof ed?.verify === "function") {
|
|
7002
|
-
return ed;
|
|
7003
|
-
}
|
|
7004
|
-
return null;
|
|
7005
|
-
} catch {
|
|
7006
|
-
return null;
|
|
7007
|
-
}
|
|
7008
|
-
}
|
|
7009
|
-
var init_loadEd25519 = __esm({
|
|
7010
|
-
"lib/module/features/ota/crypto/loadEd25519.js"() {
|
|
7011
|
-
"use strict";
|
|
7012
|
-
}
|
|
7013
|
-
});
|
|
7014
|
-
|
|
7015
|
-
// lib/module/features/ota/crypto/loadSha512.js
|
|
7016
|
-
function loadSha512() {
|
|
7017
|
-
try {
|
|
7018
|
-
const mod = require("@noble/hashes/sha2.js");
|
|
7019
|
-
const sha512 = mod?.sha512 ?? mod?.default?.sha512;
|
|
7020
|
-
return typeof sha512 === "function" ? sha512 : null;
|
|
7021
|
-
} catch {
|
|
7022
|
-
return null;
|
|
7023
|
-
}
|
|
7024
|
-
}
|
|
7025
|
-
var init_loadSha512 = __esm({
|
|
7026
|
-
"lib/module/features/ota/crypto/loadSha512.js"() {
|
|
7027
|
-
"use strict";
|
|
7028
|
-
}
|
|
7029
|
-
});
|
|
7030
|
-
|
|
7031
|
-
// lib/module/features/ota/crypto/builtinVerifier.js
|
|
7032
|
-
var builtinVerifier_exports = {};
|
|
7033
|
-
__export(builtinVerifier_exports, {
|
|
7034
|
-
_resetBuiltinVerifier: () => _resetBuiltinVerifier,
|
|
7035
|
-
decodeSignature: () => decodeSignature,
|
|
7036
|
-
getBuiltinVerifier: () => getBuiltinVerifier
|
|
7037
|
-
});
|
|
7038
|
-
function hexToBytes(hex) {
|
|
7039
|
-
const clean2 = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
7040
|
-
if (clean2.length === 0 || clean2.length % 2 !== 0 || /[^0-9a-f]/.test(clean2)) return null;
|
|
7041
|
-
const out = new Uint8Array(clean2.length / 2);
|
|
7042
|
-
for (let i = 0; i < out.length; i++) {
|
|
7043
|
-
out[i] = parseInt(clean2.slice(i * 2, i * 2 + 2), 16);
|
|
7044
|
-
}
|
|
7045
|
-
return out;
|
|
7046
|
-
}
|
|
7047
|
-
function base64ToBytes2(b64) {
|
|
7048
|
-
const s = b64.trim().replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
7049
|
-
if (s.length === 0) return null;
|
|
7050
|
-
const out = [];
|
|
7051
|
-
let buffer = 0;
|
|
7052
|
-
let bits = 0;
|
|
7053
|
-
for (const ch of s) {
|
|
7054
|
-
const idx = B64_ALPHABET2.indexOf(ch);
|
|
7055
|
-
if (idx === -1) return null;
|
|
7056
|
-
buffer = buffer << 6 | idx;
|
|
7057
|
-
bits += 6;
|
|
7058
|
-
if (bits >= 8) {
|
|
7059
|
-
bits -= 8;
|
|
7060
|
-
out.push(buffer >> bits & 255);
|
|
7061
|
-
}
|
|
7062
|
-
}
|
|
7063
|
-
return Uint8Array.from(out);
|
|
7064
|
-
}
|
|
7065
|
-
function decodeSignature(sig) {
|
|
7066
|
-
const trimmed = sig.trim();
|
|
7067
|
-
if (/^(0x)?[0-9a-fA-F]{128}$/.test(trimmed)) {
|
|
7068
|
-
const hex2 = hexToBytes(trimmed);
|
|
7069
|
-
if (hex2 && hex2.length === ED25519_SIGNATURE_BYTES) return hex2;
|
|
7070
|
-
}
|
|
7071
|
-
const b64 = base64ToBytes2(trimmed);
|
|
7072
|
-
if (b64 && b64.length === ED25519_SIGNATURE_BYTES) return b64;
|
|
7073
|
-
const hex = hexToBytes(trimmed);
|
|
7074
|
-
if (hex && hex.length === ED25519_SIGNATURE_BYTES) return hex;
|
|
7075
|
-
return null;
|
|
7076
|
-
}
|
|
7077
|
-
function wireSha512(ed, sha512) {
|
|
7078
|
-
const concat2 = (parts) => {
|
|
7079
|
-
if (parts.length === 1) return parts[0];
|
|
7080
|
-
let total = 0;
|
|
7081
|
-
for (const p of parts) total += p.length;
|
|
7082
|
-
const out = new Uint8Array(total);
|
|
7083
|
-
let off = 0;
|
|
7084
|
-
for (const p of parts) {
|
|
7085
|
-
out.set(p, off);
|
|
7086
|
-
off += p.length;
|
|
7087
|
-
}
|
|
7088
|
-
return out;
|
|
7089
|
-
};
|
|
7090
|
-
const sync = (...msgs) => sha512(concat2(msgs));
|
|
7091
|
-
const asyncFn = async (...msgs) => sha512(concat2(msgs));
|
|
7092
|
-
let wired = false;
|
|
7093
|
-
try {
|
|
7094
|
-
if (ed.hashes && typeof ed.hashes === "object") {
|
|
7095
|
-
if (typeof ed.hashes.sha512 !== "function") ed.hashes.sha512 = sync;
|
|
7096
|
-
if (typeof ed.hashes.sha512Async !== "function") ed.hashes.sha512Async = asyncFn;
|
|
7097
|
-
wired = typeof ed.hashes.sha512 === "function";
|
|
7098
|
-
}
|
|
7099
|
-
} catch {
|
|
7100
|
-
}
|
|
7101
|
-
try {
|
|
7102
|
-
if (ed.etc && typeof ed.etc === "object") {
|
|
7103
|
-
if (typeof ed.etc.sha512Sync !== "function") ed.etc.sha512Sync = sync;
|
|
7104
|
-
if (typeof ed.etc.sha512Async !== "function") ed.etc.sha512Async = asyncFn;
|
|
7105
|
-
wired = wired || typeof ed.etc.sha512Sync === "function";
|
|
7106
|
-
}
|
|
7107
|
-
} catch {
|
|
7108
|
-
}
|
|
7109
|
-
return wired;
|
|
7110
|
-
}
|
|
7111
|
-
function getBuiltinVerifier() {
|
|
7112
|
-
if (resolved !== void 0) return resolved;
|
|
7113
|
-
const ed = loadEd25519();
|
|
7114
|
-
const sha512 = loadSha512();
|
|
7115
|
-
if (!ed || !sha512) {
|
|
7116
|
-
resolved = null;
|
|
7117
|
-
return null;
|
|
7118
|
-
}
|
|
7119
|
-
if (!wireSha512(ed, sha512)) {
|
|
7120
|
-
logger.error("[OTA] @noble/ed25519 is installed but its SHA-512 provider could not be wired (unrecognised version shape). Supply `ota.verifySignature` yourself, or pin a @noble/ed25519 version this SDK supports.");
|
|
7121
|
-
resolved = null;
|
|
7122
|
-
return null;
|
|
7123
|
-
}
|
|
7124
|
-
resolved = async ({
|
|
7125
|
-
messageHex,
|
|
7126
|
-
signature,
|
|
7127
|
-
publicKey
|
|
7128
|
-
}) => {
|
|
7129
|
-
const message = hexToBytes(messageHex);
|
|
7130
|
-
const pub = hexToBytes(publicKey);
|
|
7131
|
-
const sig = decodeSignature(signature);
|
|
7132
|
-
if (!message) {
|
|
7133
|
-
logger.error("[OTA] Bundle SHA-256 is not valid hex \u2014 refusing to stage.");
|
|
7134
|
-
return false;
|
|
7135
|
-
}
|
|
7136
|
-
if (!pub || pub.length !== ED25519_PUBLIC_KEY_BYTES) {
|
|
7137
|
-
logger.error(`[OTA] ota.publicSigningKey must be ${ED25519_PUBLIC_KEY_BYTES} bytes of hex (${ED25519_PUBLIC_KEY_BYTES * 2} characters) \u2014 got ${pub ? `${pub.length} bytes` : "a value that is not hex"}.`);
|
|
7138
|
-
return false;
|
|
7139
|
-
}
|
|
7140
|
-
if (!sig) {
|
|
7141
|
-
logger.error("[OTA] Bundle signature is not a 64-byte hex/base64 value.");
|
|
7142
|
-
return false;
|
|
7143
|
-
}
|
|
7144
|
-
try {
|
|
7145
|
-
if (typeof ed.verify === "function") return ed.verify(sig, message, pub);
|
|
7146
|
-
if (typeof ed.verifyAsync === "function") return await ed.verifyAsync(sig, message, pub);
|
|
7147
|
-
return false;
|
|
7148
|
-
} catch {
|
|
7149
|
-
return false;
|
|
7150
|
-
}
|
|
7151
|
-
};
|
|
7152
|
-
__DEV__ && logger.debug("[OTA] Using built-in ed25519 verifier (@noble/ed25519 detected).");
|
|
7153
|
-
return resolved;
|
|
7154
|
-
}
|
|
7155
|
-
function _resetBuiltinVerifier() {
|
|
7156
|
-
resolved = void 0;
|
|
7157
|
-
}
|
|
7158
|
-
var ED25519_SIGNATURE_BYTES, ED25519_PUBLIC_KEY_BYTES, B64_ALPHABET2, resolved;
|
|
7159
|
-
var init_builtinVerifier = __esm({
|
|
7160
|
-
"lib/module/features/ota/crypto/builtinVerifier.js"() {
|
|
7161
|
-
"use strict";
|
|
7162
|
-
init_loadEd25519();
|
|
7163
|
-
init_loadSha512();
|
|
7164
|
-
init_internalLogger();
|
|
7165
|
-
ED25519_SIGNATURE_BYTES = 64;
|
|
7166
|
-
ED25519_PUBLIC_KEY_BYTES = 32;
|
|
7167
|
-
B64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
7168
|
-
}
|
|
7169
|
-
});
|
|
7170
|
-
|
|
7171
|
-
// lib/module/features/ota/crypto/nativeVerifier.js
|
|
7172
|
-
function bytesToHex(bytes) {
|
|
7173
|
-
let out = "";
|
|
7174
|
-
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
7175
|
-
return out;
|
|
7176
|
-
}
|
|
7177
|
-
function normalizeHex(value, expectedChars) {
|
|
7178
|
-
const clean2 = value.trim().toLowerCase().replace(/^0x/, "");
|
|
7179
|
-
if (clean2.length !== expectedChars || /[^0-9a-f]/.test(clean2)) return null;
|
|
7180
|
-
return clean2;
|
|
7181
|
-
}
|
|
7182
|
-
function getNativeVerifier() {
|
|
7183
|
-
if (resolved2 !== void 0) return resolved2;
|
|
7184
|
-
const native = NativeScaleBunOta_default;
|
|
7185
|
-
if (!native || typeof native.verifyEd25519 !== "function") {
|
|
7186
|
-
resolved2 = null;
|
|
7187
|
-
return null;
|
|
7188
|
-
}
|
|
7189
|
-
resolved2 = async ({
|
|
7190
|
-
messageHex,
|
|
7191
|
-
signature,
|
|
7192
|
-
publicKey
|
|
7193
|
-
}) => {
|
|
7194
|
-
const message = normalizeHex(messageHex, 64);
|
|
7195
|
-
const key = normalizeHex(publicKey, ED25519_PUBLIC_KEY_HEX_CHARS);
|
|
7196
|
-
const sigBytes = decodeSignature(signature);
|
|
7197
|
-
if (!message || !key || !sigBytes) {
|
|
7198
|
-
logger.error("[OTA] Signature inputs malformed (bundle hash, signature, or public key) \u2014 refusing to stage.");
|
|
7199
|
-
return false;
|
|
7200
|
-
}
|
|
7201
|
-
let verdict;
|
|
7202
|
-
try {
|
|
7203
|
-
verdict = await native.verifyEd25519(message, bytesToHex(sigBytes), key);
|
|
7204
|
-
} catch (err) {
|
|
7205
|
-
logger.warn(`[OTA] Native ed25519 verify failed to run: ${err?.message ?? err}`);
|
|
7206
|
-
verdict = "unavailable";
|
|
7207
|
-
}
|
|
7208
|
-
if (verdict === "valid") return true;
|
|
7209
|
-
if (verdict === "invalid") return false;
|
|
7210
|
-
const builtin = getBuiltinVerifier();
|
|
7211
|
-
if (builtin) {
|
|
7212
|
-
return builtin({
|
|
7213
|
-
messageHex,
|
|
7214
|
-
signature,
|
|
7215
|
-
publicKey
|
|
7216
|
-
});
|
|
7217
|
-
}
|
|
7218
|
-
throw new Error("ed25519 unavailable: this OS has no native implementation (Android < 13) and the optional peers @noble/ed25519 + @noble/hashes are not installed.");
|
|
7219
|
-
};
|
|
7220
|
-
__DEV__ && logger.debug("[OTA] Using native ed25519 verifier (platform crypto).");
|
|
7221
|
-
return resolved2;
|
|
7222
|
-
}
|
|
7223
|
-
var ED25519_PUBLIC_KEY_HEX_CHARS, resolved2;
|
|
7224
|
-
var init_nativeVerifier = __esm({
|
|
7225
|
-
"lib/module/features/ota/crypto/nativeVerifier.js"() {
|
|
7226
|
-
"use strict";
|
|
7227
|
-
init_NativeScaleBunOta();
|
|
7228
|
-
init_internalLogger();
|
|
7229
|
-
init_builtinVerifier();
|
|
7230
|
-
ED25519_PUBLIC_KEY_HEX_CHARS = 64;
|
|
7231
|
-
}
|
|
7232
|
-
});
|
|
7233
|
-
|
|
7234
|
-
// lib/module/features/ota/signature.js
|
|
7235
|
-
async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
7236
|
-
const rawKey = config?.publicKey;
|
|
7237
|
-
const keys = (Array.isArray(rawKey) ? rawKey : rawKey ? [rawKey] : []).filter((k) => typeof k === "string" && k.trim().length > 0);
|
|
7238
|
-
if (rawKey === void 0 || rawKey === "" || rawKey === null) {
|
|
7239
|
-
if (!warnedNotConfigured) {
|
|
7240
|
-
warnedNotConfigured = true;
|
|
7241
|
-
logger.warn("[OTA] No signing public key configured \u2014 bundles are accepted on SHA-256 integrity alone. Configure `ota.publicSigningKey` to enforce authenticity.");
|
|
7242
|
-
}
|
|
7243
|
-
return {
|
|
7244
|
-
ok: true,
|
|
7245
|
-
reason: "not_configured"
|
|
7246
|
-
};
|
|
7247
|
-
}
|
|
7248
|
-
if (keys.length === 0) {
|
|
7249
|
-
logger.error("[OTA] Signing key config resolves to zero usable keys (empty array or blank strings) \u2014 refusing to stage. Remove the config to disable signing, or pin at least one real key.");
|
|
7250
|
-
return {
|
|
7251
|
-
ok: false,
|
|
7252
|
-
reason: "no_keys"
|
|
7253
|
-
};
|
|
7254
|
-
}
|
|
7255
|
-
if (!signature) {
|
|
7256
|
-
logger.error("[OTA] Bundle has no signature but a signing key is configured \u2014 refusing to stage.");
|
|
7257
|
-
return {
|
|
7258
|
-
ok: false,
|
|
7259
|
-
reason: "missing_signature"
|
|
7260
|
-
};
|
|
7261
|
-
}
|
|
7262
|
-
const verifier = typeof config?.verifier === "function" ? config.verifier : getNativeVerifier() ?? getBuiltinVerifier();
|
|
7263
|
-
if (!verifier) {
|
|
7264
|
-
logger.error("[OTA] A signing key is configured but no signature verifier is available \u2014 refusing to stage. Either install the optional peers `@noble/ed25519` and `@noble/hashes` (the SDK then verifies with no further code), or provide your own `ota.verifySignature`.");
|
|
7265
|
-
return {
|
|
7266
|
-
ok: false,
|
|
7267
|
-
reason: "no_verifier"
|
|
7268
|
-
};
|
|
7269
|
-
}
|
|
7270
|
-
let sawThrow = false;
|
|
7271
|
-
let sawCompletion = false;
|
|
7272
|
-
for (const key of keys) {
|
|
7273
|
-
try {
|
|
7274
|
-
const valid = await verifier({
|
|
7275
|
-
messageHex: bundleSha256,
|
|
7276
|
-
signature,
|
|
7277
|
-
publicKey: key
|
|
7278
|
-
});
|
|
7279
|
-
sawCompletion = true;
|
|
7280
|
-
if (valid) return {
|
|
7281
|
-
ok: true,
|
|
7282
|
-
reason: "verified"
|
|
7283
|
-
};
|
|
7284
|
-
} catch (err) {
|
|
7285
|
-
sawThrow = true;
|
|
7286
|
-
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
7287
|
-
}
|
|
7288
|
-
}
|
|
7289
|
-
if (sawThrow && !sawCompletion) {
|
|
7290
|
-
return {
|
|
7291
|
-
ok: false,
|
|
7292
|
-
reason: "verifier_threw"
|
|
7293
|
-
};
|
|
7294
|
-
}
|
|
7295
|
-
logger.error(keys.length > 1 ? `[OTA] Bundle signature is INVALID under all ${keys.length} pinned keys \u2014 refusing to stage.` : "[OTA] Bundle signature is INVALID \u2014 refusing to stage.");
|
|
7296
|
-
return {
|
|
7297
|
-
ok: false,
|
|
7298
|
-
reason: "invalid_signature"
|
|
7299
|
-
};
|
|
7300
|
-
}
|
|
7301
|
-
var warnedNotConfigured;
|
|
7302
|
-
var init_signature = __esm({
|
|
7303
|
-
"lib/module/features/ota/signature.js"() {
|
|
7304
|
-
"use strict";
|
|
7305
|
-
init_internalLogger();
|
|
7306
|
-
init_builtinVerifier();
|
|
7307
|
-
init_nativeVerifier();
|
|
7308
|
-
warnedNotConfigured = false;
|
|
7309
|
-
}
|
|
7310
|
-
});
|
|
7311
|
-
|
|
7312
6996
|
// lib/module/features/ota/environment.js
|
|
7313
6997
|
function compare(a, b) {
|
|
7314
6998
|
if (a.major !== b.major) return a.major - b.major;
|
|
@@ -7554,7 +7238,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
7554
7238
|
init_geoCountry();
|
|
7555
7239
|
init_deviceAttributes();
|
|
7556
7240
|
init_installationId();
|
|
7557
|
-
init_signature();
|
|
7558
7241
|
init_environment();
|
|
7559
7242
|
init_retry();
|
|
7560
7243
|
init_StorageBackend();
|
|
@@ -7582,15 +7265,15 @@ var init_OtaOrchestrator = __esm({
|
|
|
7582
7265
|
/**
|
|
7583
7266
|
* Initialize the orchestrator. Checks for boot-guard recovery state.
|
|
7584
7267
|
*
|
|
7585
|
-
*
|
|
7586
|
-
*
|
|
7587
|
-
*
|
|
7268
|
+
* Signature enforcement is no longer a JS concern: signed releases are
|
|
7269
|
+
* verified and staged in NATIVE code against RSA keys embedded in the app
|
|
7270
|
+
* package (SB-OTA-RSA-SHA256-V1), and unsigned releases keep working on
|
|
7271
|
+
* SHA-256 integrity alone. There is nothing for the host to configure here.
|
|
7588
7272
|
*/
|
|
7589
7273
|
init(config) {
|
|
7590
7274
|
noThrow(() => {
|
|
7591
7275
|
if (this.initialized) {
|
|
7592
7276
|
this.bootGuardConfig = config ?? this.bootGuardConfig;
|
|
7593
|
-
this.signatureConfig = config?.signature ?? this.signatureConfig;
|
|
7594
7277
|
return;
|
|
7595
7278
|
}
|
|
7596
7279
|
if (!NativeScaleBunOta_default) {
|
|
@@ -7612,7 +7295,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
7612
7295
|
this.initialized = true;
|
|
7613
7296
|
this.enabled = true;
|
|
7614
7297
|
this.bootGuardConfig = config ?? {};
|
|
7615
|
-
this.signatureConfig = config?.signature;
|
|
7616
7298
|
__DEV__ && logger.debug(`[OTA] Orchestrator initialized (RN ${this.environment.rnVersionString ?? "unknown"}${this.environment.bridgeless ? ", bridgeless" : ""}${this.environment.hermes ? `, Hermes HBC v${this.environment.hermesBytecodeVersion ?? "?"}` : ""})`);
|
|
7617
7299
|
const slotState = this.readSlotState();
|
|
7618
7300
|
this.hydrateCurrentBundleFromSlots(slotState);
|
|
@@ -7799,12 +7481,22 @@ var init_OtaOrchestrator = __esm({
|
|
|
7799
7481
|
const env = this.environment ?? detectOtaEnvironment();
|
|
7800
7482
|
const mobileOS = resolveMobileOS();
|
|
7801
7483
|
if (!mobileOS) throw new Error("OTA is not supported on this platform");
|
|
7484
|
+
let trustedKeyIds;
|
|
7485
|
+
try {
|
|
7486
|
+
const raw = NativeScaleBunOta_default?.getTrustedKeyIds?.();
|
|
7487
|
+
if (raw) {
|
|
7488
|
+
const arr2 = JSON.parse(raw);
|
|
7489
|
+
if (Array.isArray(arr2) && arr2.length) trustedKeyIds = arr2.slice(0, 16);
|
|
7490
|
+
}
|
|
7491
|
+
} catch {
|
|
7492
|
+
}
|
|
7802
7493
|
const payload = {
|
|
7803
7494
|
appVersion: requestParams.appVersion,
|
|
7804
7495
|
platform: mobileOS,
|
|
7805
7496
|
installationId: requestParams.installationId,
|
|
7806
7497
|
currentBundleId: this.currentBundle?.id,
|
|
7807
7498
|
currentBundleHash: this.currentBundle?.sha256,
|
|
7499
|
+
trustedKeyIds,
|
|
7808
7500
|
channelName: requestParams.channelName,
|
|
7809
7501
|
// Host-supplied country wins; otherwise the cached edge-Worker lookup
|
|
7810
7502
|
// fills in (see geoCountry.ts). Undefined when neither knows — the
|
|
@@ -7918,19 +7610,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
7918
7610
|
releaseId: bundle.releaseId,
|
|
7919
7611
|
version: bundle.version
|
|
7920
7612
|
});
|
|
7921
|
-
const signatureOutcome = await verifyBundleSignature(bundle.sha256, bundle.signature, this.signatureConfig);
|
|
7922
|
-
if (!signatureOutcome.ok) {
|
|
7923
|
-
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
7924
|
-
releaseId: bundle.releaseId,
|
|
7925
|
-
error: `Signature check failed: ${signatureOutcome.reason}`,
|
|
7926
|
-
version: bundle.version
|
|
7927
|
-
});
|
|
7928
|
-
return {
|
|
7929
|
-
status: "ERROR",
|
|
7930
|
-
error: `Bundle signature check failed (${signatureOutcome.reason})`,
|
|
7931
|
-
bundle
|
|
7932
|
-
};
|
|
7933
|
-
}
|
|
7934
7613
|
otaEventEmitter.emitSimple("DOWNLOAD_STARTED", bundle.id, {
|
|
7935
7614
|
releaseId: bundle.releaseId,
|
|
7936
7615
|
version: bundle.version
|
|
@@ -7978,39 +7657,75 @@ var init_OtaOrchestrator = __esm({
|
|
|
7978
7657
|
}
|
|
7979
7658
|
});
|
|
7980
7659
|
let staged = false;
|
|
7981
|
-
if (bundle.
|
|
7982
|
-
|
|
7660
|
+
if (bundle.envelope) {
|
|
7661
|
+
const envelopeJson = JSON.stringify({
|
|
7662
|
+
...bundle.envelope,
|
|
7663
|
+
downloadUrl: bundle.url
|
|
7664
|
+
});
|
|
7665
|
+
let verdict;
|
|
7983
7666
|
try {
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
__DEV__ && logger.debug("[OTA] Patch applied successfully");
|
|
7993
|
-
} else {
|
|
7994
|
-
logger.warn("[OTA] Patch staging returned false \u2014 falling back to full bundle");
|
|
7995
|
-
}
|
|
7996
|
-
} catch (patchErr) {
|
|
7997
|
-
logger.warn(`[OTA] Patch staging failed: ${patchErr.message} \u2014 falling back to full bundle`);
|
|
7667
|
+
const verdictJson = await NativeScaleBunOta_default.verifyAndStageRelease(envelopeJson);
|
|
7668
|
+
verdict = JSON.parse(verdictJson);
|
|
7669
|
+
} catch (err) {
|
|
7670
|
+
verdict = {
|
|
7671
|
+
ok: false,
|
|
7672
|
+
code: "verify_stage_threw",
|
|
7673
|
+
message: err?.message ?? String(err)
|
|
7674
|
+
};
|
|
7998
7675
|
}
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
label: `download v${bundle.version}`,
|
|
8010
|
-
attempts: 3
|
|
7676
|
+
if (!verdict.ok) {
|
|
7677
|
+
unsubscribeProgress();
|
|
7678
|
+
postProgress(0, "FAILED");
|
|
7679
|
+
logger.error(`[OTA] Native verify/stage refused the release: ${verdict.code ?? "unknown"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}`);
|
|
7680
|
+
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
7681
|
+
releaseId: bundle.releaseId,
|
|
7682
|
+
// `error` is mapped onto the wire `errorCode` by deliverOtaEvents —
|
|
7683
|
+
// carry the stable native verdict code there.
|
|
7684
|
+
error: verdict.code ?? "verify_and_stage_release_failed",
|
|
7685
|
+
version: bundle.version
|
|
8011
7686
|
});
|
|
8012
|
-
|
|
8013
|
-
|
|
7687
|
+
return {
|
|
7688
|
+
status: "ERROR",
|
|
7689
|
+
error: `Release verification failed (${verdict.code ?? "unknown"})`,
|
|
7690
|
+
bundle
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7693
|
+
staged = true;
|
|
7694
|
+
} else {
|
|
7695
|
+
if (bundle.patch && NativeScaleBunOta_default.stagePatch) {
|
|
7696
|
+
__DEV__ && logger.debug(`[OTA] Patch available (${(bundle.patch.size / 1024).toFixed(0)} KB vs ${(bundle.size / 1024).toFixed(0)} KB full) \u2014 trying patch\u2026`);
|
|
7697
|
+
try {
|
|
7698
|
+
staged = await NativeScaleBunOta_default.stagePatch(
|
|
7699
|
+
bundle.patch.url,
|
|
7700
|
+
bundle.patch.sha256,
|
|
7701
|
+
bundle.sha256
|
|
7702
|
+
// expected full-bundle hash after patching
|
|
7703
|
+
);
|
|
7704
|
+
if (staged) {
|
|
7705
|
+
patchUsed = true;
|
|
7706
|
+
__DEV__ && logger.debug("[OTA] Patch applied successfully");
|
|
7707
|
+
} else {
|
|
7708
|
+
logger.warn("[OTA] Patch staging returned false \u2014 falling back to full bundle");
|
|
7709
|
+
}
|
|
7710
|
+
} catch (patchErr) {
|
|
7711
|
+
logger.warn(`[OTA] Patch staging failed: ${patchErr.message} \u2014 falling back to full bundle`);
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7714
|
+
if (!staged) {
|
|
7715
|
+
try {
|
|
7716
|
+
staged = await retryWithBackoff(async () => {
|
|
7717
|
+
const ok = await NativeScaleBunOta_default.stageBundle(bundle.url, bundle.sha256);
|
|
7718
|
+
if (!ok) {
|
|
7719
|
+
throw new Error("stageBundle returned false");
|
|
7720
|
+
}
|
|
7721
|
+
return ok;
|
|
7722
|
+
}, {
|
|
7723
|
+
label: `download v${bundle.version}`,
|
|
7724
|
+
attempts: 3
|
|
7725
|
+
});
|
|
7726
|
+
} catch {
|
|
7727
|
+
staged = false;
|
|
7728
|
+
}
|
|
8014
7729
|
}
|
|
8015
7730
|
}
|
|
8016
7731
|
unsubscribeProgress();
|
|
@@ -10186,18 +9901,9 @@ var SHAPE = {
|
|
|
10186
9901
|
kind: "str",
|
|
10187
9902
|
opt: true
|
|
10188
9903
|
},
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
},
|
|
10193
|
-
// Rotation support: several pinned keys, any of which may verify a
|
|
10194
|
-
// bundle. Ship a build pinning [old, new], re-sign server-side with
|
|
10195
|
-
// new, drop old next release — no emergency store submission when a
|
|
10196
|
-
// key must be replaced. Merged with publicSigningKey by the facade.
|
|
10197
|
-
publicSigningKeys: {
|
|
10198
|
-
kind: "strArr",
|
|
10199
|
-
opt: true
|
|
10200
|
-
},
|
|
9904
|
+
// No signing-key fields: signed releases are verified natively against
|
|
9905
|
+
// RSA keys embedded in the app package (SB-OTA-RSA-SHA256-V1). JS must
|
|
9906
|
+
// not supply a trusted key.
|
|
10201
9907
|
mandatoryBlocksUi: bool(false)
|
|
10202
9908
|
}
|
|
10203
9909
|
}
|
|
@@ -10999,6 +10705,23 @@ function installNetworkMonitoring(onEvent, options = {}) {
|
|
|
10999
10705
|
});
|
|
11000
10706
|
}
|
|
11001
10707
|
|
|
10708
|
+
// lib/module/features/network/thirdParty.js
|
|
10709
|
+
var THIRD_PARTY_BEACON_HOSTS = "google-analytics.com,googletagmanager.com,analytics.google.com,doubleclick.net,googlesyndication.com,googleadservices.com,app-measurement.com,facebook.net,analytics.tiktok.com,ads-twitter.com,analytics.twitter.com,ct.pinterest.com,bat.bing.com,px.ads.linkedin.com,tr.snapchat.com,criteo.com,criteo.net,taboola.com,outbrain.com,adroll.com,adsrvr.org,quantserve.com,scorecardresearch.com,hotjar.com,hotjar.io,clarity.ms,fullstory.com,mouseflow.com,crazyegg.com,luckyorange.com,inspectlet.com,segment.io,mixpanel.com,amplitude.com,heap.io,heapanalytics.com,rudderstack.com,posthog.com,matomo.cloud,plausible.io,simpleanalytics.com,statcounter.com,appsflyer.com,adjust.com,branch.io,ipify.org,ipinfo.io,ipapi.co,ip-api.com,geolocation-db.com,cloudflareinsights.com,nr-data.net,browser-intake-datadoghq.com,sentry.io,bugsnag.com,rollbar.com";
|
|
10710
|
+
function isThirdPartyBeacon(url, hosts) {
|
|
10711
|
+
if (!url) return false;
|
|
10712
|
+
let host = "";
|
|
10713
|
+
try {
|
|
10714
|
+
host = new URL(url).hostname.toLowerCase();
|
|
10715
|
+
} catch {
|
|
10716
|
+
return false;
|
|
10717
|
+
}
|
|
10718
|
+
if (!host) return false;
|
|
10719
|
+
for (const suffix of (hosts ?? THIRD_PARTY_BEACON_HOSTS).split(",")) {
|
|
10720
|
+
if (host === suffix || host.endsWith("." + suffix)) return true;
|
|
10721
|
+
}
|
|
10722
|
+
return false;
|
|
10723
|
+
}
|
|
10724
|
+
|
|
11002
10725
|
// lib/module/features/network/NetworkFeature.js
|
|
11003
10726
|
init_networkAdapter();
|
|
11004
10727
|
init_SessionManager();
|
|
@@ -11014,10 +10737,12 @@ var NetworkFeature = class {
|
|
|
11014
10737
|
if (this.active) return;
|
|
11015
10738
|
installNetworkMonitoring((data) => {
|
|
11016
10739
|
const isInternal = isSdkInternalUrl(data.url, this.options.sdkBaseUrl);
|
|
11017
|
-
|
|
10740
|
+
const isBeaconOrDenied = this.options.captureThirdParty !== true && isThirdPartyBeacon(data.url, this.options.thirdPartyHosts) || matchesDenyUrl(data.url, this.options.denyUrls);
|
|
10741
|
+
const isExcluded = isInternal || isBeaconOrDenied;
|
|
10742
|
+
if (!isExcluded) {
|
|
11018
10743
|
this.trackFn("$network_request", data);
|
|
11019
10744
|
}
|
|
11020
|
-
if (data.type === "network") {
|
|
10745
|
+
if (data.type === "network" && !isBeaconOrDenied) {
|
|
11021
10746
|
reportNetworkRequest({
|
|
11022
10747
|
method: data.method,
|
|
11023
10748
|
url: data.url,
|
|
@@ -11033,7 +10758,7 @@ var NetworkFeature = class {
|
|
|
11033
10758
|
requestBody: data.requestBody,
|
|
11034
10759
|
responseBody: data.responseBody
|
|
11035
10760
|
});
|
|
11036
|
-
if (!
|
|
10761
|
+
if (!isExcluded) {
|
|
11037
10762
|
const sessionManager = SessionManager.getExistingInstance();
|
|
11038
10763
|
if (sessionManager) {
|
|
11039
10764
|
const backendTransport = sessionManager.getBackendTransport();
|
|
@@ -11064,6 +10789,13 @@ var NetworkFeature = class {
|
|
|
11064
10789
|
return this.active;
|
|
11065
10790
|
}
|
|
11066
10791
|
};
|
|
10792
|
+
function matchesDenyUrl(url, patterns) {
|
|
10793
|
+
if (!patterns || !patterns.length || !url) return false;
|
|
10794
|
+
for (const p of patterns) {
|
|
10795
|
+
if (typeof p === "string" ? url.includes(p) : p.test(url)) return true;
|
|
10796
|
+
}
|
|
10797
|
+
return false;
|
|
10798
|
+
}
|
|
11067
10799
|
function isSdkInternalUrl(url, sdkBaseUrl) {
|
|
11068
10800
|
if (!url) return false;
|
|
11069
10801
|
try {
|
|
@@ -17100,17 +16832,11 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
17100
16832
|
/**
|
|
17101
16833
|
* Boot the OTA orchestrator when the init config asks for it.
|
|
17102
16834
|
*
|
|
17103
|
-
*
|
|
17104
|
-
*
|
|
17105
|
-
*
|
|
17106
|
-
*
|
|
17107
|
-
*
|
|
17108
|
-
*
|
|
17109
|
-
* Verification comes from `ota.verifySignature` when supplied, else the
|
|
17110
|
-
* built-in @noble-based verifier (optional peers). Pinning keys with
|
|
17111
|
-
* NEITHER available is fail-CLOSED by design (signature.ts) — an update
|
|
17112
|
-
* that cannot be verified is not installed. We say that out loud rather
|
|
17113
|
-
* than letting the app discover it as a silent no-update condition.
|
|
16835
|
+
* Signature enforcement is no longer configured from JS. Signed releases are
|
|
16836
|
+
* verified and staged in NATIVE code against RSA keys embedded in the app
|
|
16837
|
+
* package (SB-OTA-RSA-SHA256-V1); unsigned releases keep working on SHA-256
|
|
16838
|
+
* integrity. There are no signing-key or `verifySignature` options to pass
|
|
16839
|
+
* through — the host embeds its trusted keys natively.
|
|
17114
16840
|
*/
|
|
17115
16841
|
_maybeStartOta(rawConfig) {
|
|
17116
16842
|
const ota = rawConfig?.ota;
|
|
@@ -17119,29 +16845,8 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
17119
16845
|
const {
|
|
17120
16846
|
otaOrchestrator: otaOrchestrator2
|
|
17121
16847
|
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
17122
|
-
const rawSingle = ota.publicSigningKey;
|
|
17123
|
-
const rawList = ota.publicSigningKeys;
|
|
17124
|
-
const publicKeys = Array.from(new Set([...typeof rawSingle === "string" && rawSingle ? [rawSingle] : [], ...Array.isArray(rawList) ? rawList : []].filter((k) => typeof k === "string" && k.trim().length > 0)));
|
|
17125
|
-
const signingRequested = publicKeys.length > 0 || Array.isArray(rawList);
|
|
17126
|
-
const verifier = ota.verifySignature;
|
|
17127
|
-
if (signingRequested && typeof verifier !== "function") {
|
|
17128
|
-
const {
|
|
17129
|
-
getBuiltinVerifier: getBuiltinVerifier2
|
|
17130
|
-
} = (init_builtinVerifier(), __toCommonJS(builtinVerifier_exports));
|
|
17131
|
-
if (!getBuiltinVerifier2()) {
|
|
17132
|
-
logger.warn("[ScaleBun] A signing key is pinned but no signature verifier is available. Signature checking is fail-closed: updates will be REJECTED until one exists. Install the optional peers `@noble/ed25519` + `@noble/hashes` (no further code needed), or supply `ota.verifySignature`.");
|
|
17133
|
-
}
|
|
17134
|
-
}
|
|
17135
16848
|
const healthyAfterMs = typeof ota.healthyAfterMs === "number" ? ota.healthyAfterMs : typeof ota.healthyTimeoutMs === "number" ? ota.healthyTimeoutMs : void 0;
|
|
17136
16849
|
otaOrchestrator2.init({
|
|
17137
|
-
...signingRequested ? {
|
|
17138
|
-
signature: {
|
|
17139
|
-
// Single key stays a plain string — the shape every
|
|
17140
|
-
// existing consumer and test already handles.
|
|
17141
|
-
publicKey: publicKeys.length === 1 ? publicKeys[0] : publicKeys,
|
|
17142
|
-
verifier
|
|
17143
|
-
}
|
|
17144
|
-
} : {},
|
|
17145
16850
|
...healthyAfterMs !== void 0 ? {
|
|
17146
16851
|
healthyAfterMs
|
|
17147
16852
|
} : {}
|
|
@@ -21099,11 +20804,11 @@ var normalizedWheelValue = (value) => value.toLowerCase().replace(/\s+/g, " ").t
|
|
|
21099
20804
|
function resolveWheelOutcome(segments, source) {
|
|
21100
20805
|
const identity = wheelOutcomeIdentity(source);
|
|
21101
20806
|
const index = findWheelOutcomeIndex(segments, identity);
|
|
21102
|
-
const
|
|
20807
|
+
const resolved = index >= 0;
|
|
21103
20808
|
return {
|
|
21104
|
-
index:
|
|
21105
|
-
label: identity.resultLabel || (
|
|
21106
|
-
resolved
|
|
20809
|
+
index: resolved ? index : 0,
|
|
20810
|
+
label: identity.resultLabel || (resolved ? segments[index]?.fullLabel ?? "" : ""),
|
|
20811
|
+
resolved
|
|
21107
20812
|
};
|
|
21108
20813
|
}
|
|
21109
20814
|
function SpinWheelInApp(props) {
|
|
@@ -29962,8 +29667,8 @@ function EngagePromptProvider({
|
|
|
29962
29667
|
__DEV__ && logger.debug("[Engage] no eligible in-app message to render");
|
|
29963
29668
|
return false;
|
|
29964
29669
|
}
|
|
29965
|
-
const
|
|
29966
|
-
activeInAppVariantRef.current = message.experiment?.enabled ?
|
|
29670
|
+
const resolved = selectInAppVariant(message, getDeviceId());
|
|
29671
|
+
activeInAppVariantRef.current = message.experiment?.enabled ? resolved : null;
|
|
29967
29672
|
store.recordImpression(message.id, Date.now(), message.revision);
|
|
29968
29673
|
setActiveInApp(message);
|
|
29969
29674
|
return true;
|