@scalebun/react-native 1.13.0 → 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.full.js
CHANGED
|
@@ -89,9 +89,9 @@ var init_internalLogger = __esm({
|
|
|
89
89
|
}
|
|
90
90
|
/** Set level from the public string name used in config (e.g. 'warn'). */
|
|
91
91
|
setLevelByName(name) {
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
this.level =
|
|
92
|
+
const resolved = LEVEL_BY_NAME[name];
|
|
93
|
+
if (resolved !== void 0) {
|
|
94
|
+
this.level = resolved;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
/** Current numeric level — exposed for callers that branch on dev verbosity. */
|
|
@@ -605,7 +605,7 @@ var SDK_VERSION;
|
|
|
605
605
|
var init_version = __esm({
|
|
606
606
|
"lib/module/core/constants/version.js"() {
|
|
607
607
|
"use strict";
|
|
608
|
-
SDK_VERSION = "
|
|
608
|
+
SDK_VERSION = "2.0.0";
|
|
609
609
|
}
|
|
610
610
|
});
|
|
611
611
|
|
|
@@ -11697,322 +11697,6 @@ var init_deviceAttributes = __esm({
|
|
|
11697
11697
|
}
|
|
11698
11698
|
});
|
|
11699
11699
|
|
|
11700
|
-
// lib/module/features/ota/crypto/loadEd25519.js
|
|
11701
|
-
function loadEd25519() {
|
|
11702
|
-
try {
|
|
11703
|
-
const mod = require("@noble/ed25519");
|
|
11704
|
-
const ed = mod?.default ?? mod;
|
|
11705
|
-
if (typeof ed?.verifyAsync === "function" || typeof ed?.verify === "function") {
|
|
11706
|
-
return ed;
|
|
11707
|
-
}
|
|
11708
|
-
return null;
|
|
11709
|
-
} catch {
|
|
11710
|
-
return null;
|
|
11711
|
-
}
|
|
11712
|
-
}
|
|
11713
|
-
var init_loadEd25519 = __esm({
|
|
11714
|
-
"lib/module/features/ota/crypto/loadEd25519.js"() {
|
|
11715
|
-
"use strict";
|
|
11716
|
-
}
|
|
11717
|
-
});
|
|
11718
|
-
|
|
11719
|
-
// lib/module/features/ota/crypto/loadSha512.js
|
|
11720
|
-
function loadSha512() {
|
|
11721
|
-
try {
|
|
11722
|
-
const mod = require("@noble/hashes/sha2.js");
|
|
11723
|
-
const sha512 = mod?.sha512 ?? mod?.default?.sha512;
|
|
11724
|
-
return typeof sha512 === "function" ? sha512 : null;
|
|
11725
|
-
} catch {
|
|
11726
|
-
return null;
|
|
11727
|
-
}
|
|
11728
|
-
}
|
|
11729
|
-
var init_loadSha512 = __esm({
|
|
11730
|
-
"lib/module/features/ota/crypto/loadSha512.js"() {
|
|
11731
|
-
"use strict";
|
|
11732
|
-
}
|
|
11733
|
-
});
|
|
11734
|
-
|
|
11735
|
-
// lib/module/features/ota/crypto/builtinVerifier.js
|
|
11736
|
-
var builtinVerifier_exports = {};
|
|
11737
|
-
__export(builtinVerifier_exports, {
|
|
11738
|
-
_resetBuiltinVerifier: () => _resetBuiltinVerifier,
|
|
11739
|
-
decodeSignature: () => decodeSignature,
|
|
11740
|
-
getBuiltinVerifier: () => getBuiltinVerifier
|
|
11741
|
-
});
|
|
11742
|
-
function hexToBytes(hex) {
|
|
11743
|
-
const clean2 = hex.trim().toLowerCase().replace(/^0x/, "");
|
|
11744
|
-
if (clean2.length === 0 || clean2.length % 2 !== 0 || /[^0-9a-f]/.test(clean2)) return null;
|
|
11745
|
-
const out = new Uint8Array(clean2.length / 2);
|
|
11746
|
-
for (let i = 0; i < out.length; i++) {
|
|
11747
|
-
out[i] = parseInt(clean2.slice(i * 2, i * 2 + 2), 16);
|
|
11748
|
-
}
|
|
11749
|
-
return out;
|
|
11750
|
-
}
|
|
11751
|
-
function base64ToBytes2(b64) {
|
|
11752
|
-
const s = b64.trim().replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
|
|
11753
|
-
if (s.length === 0) return null;
|
|
11754
|
-
const out = [];
|
|
11755
|
-
let buffer = 0;
|
|
11756
|
-
let bits = 0;
|
|
11757
|
-
for (const ch of s) {
|
|
11758
|
-
const idx = B64_ALPHABET2.indexOf(ch);
|
|
11759
|
-
if (idx === -1) return null;
|
|
11760
|
-
buffer = buffer << 6 | idx;
|
|
11761
|
-
bits += 6;
|
|
11762
|
-
if (bits >= 8) {
|
|
11763
|
-
bits -= 8;
|
|
11764
|
-
out.push(buffer >> bits & 255);
|
|
11765
|
-
}
|
|
11766
|
-
}
|
|
11767
|
-
return Uint8Array.from(out);
|
|
11768
|
-
}
|
|
11769
|
-
function decodeSignature(sig) {
|
|
11770
|
-
const trimmed = sig.trim();
|
|
11771
|
-
if (/^(0x)?[0-9a-fA-F]{128}$/.test(trimmed)) {
|
|
11772
|
-
const hex2 = hexToBytes(trimmed);
|
|
11773
|
-
if (hex2 && hex2.length === ED25519_SIGNATURE_BYTES) return hex2;
|
|
11774
|
-
}
|
|
11775
|
-
const b64 = base64ToBytes2(trimmed);
|
|
11776
|
-
if (b64 && b64.length === ED25519_SIGNATURE_BYTES) return b64;
|
|
11777
|
-
const hex = hexToBytes(trimmed);
|
|
11778
|
-
if (hex && hex.length === ED25519_SIGNATURE_BYTES) return hex;
|
|
11779
|
-
return null;
|
|
11780
|
-
}
|
|
11781
|
-
function wireSha512(ed, sha512) {
|
|
11782
|
-
const concat2 = (parts) => {
|
|
11783
|
-
if (parts.length === 1) return parts[0];
|
|
11784
|
-
let total = 0;
|
|
11785
|
-
for (const p of parts) total += p.length;
|
|
11786
|
-
const out = new Uint8Array(total);
|
|
11787
|
-
let off = 0;
|
|
11788
|
-
for (const p of parts) {
|
|
11789
|
-
out.set(p, off);
|
|
11790
|
-
off += p.length;
|
|
11791
|
-
}
|
|
11792
|
-
return out;
|
|
11793
|
-
};
|
|
11794
|
-
const sync = (...msgs) => sha512(concat2(msgs));
|
|
11795
|
-
const asyncFn = async (...msgs) => sha512(concat2(msgs));
|
|
11796
|
-
let wired = false;
|
|
11797
|
-
try {
|
|
11798
|
-
if (ed.hashes && typeof ed.hashes === "object") {
|
|
11799
|
-
if (typeof ed.hashes.sha512 !== "function") ed.hashes.sha512 = sync;
|
|
11800
|
-
if (typeof ed.hashes.sha512Async !== "function") ed.hashes.sha512Async = asyncFn;
|
|
11801
|
-
wired = typeof ed.hashes.sha512 === "function";
|
|
11802
|
-
}
|
|
11803
|
-
} catch {
|
|
11804
|
-
}
|
|
11805
|
-
try {
|
|
11806
|
-
if (ed.etc && typeof ed.etc === "object") {
|
|
11807
|
-
if (typeof ed.etc.sha512Sync !== "function") ed.etc.sha512Sync = sync;
|
|
11808
|
-
if (typeof ed.etc.sha512Async !== "function") ed.etc.sha512Async = asyncFn;
|
|
11809
|
-
wired = wired || typeof ed.etc.sha512Sync === "function";
|
|
11810
|
-
}
|
|
11811
|
-
} catch {
|
|
11812
|
-
}
|
|
11813
|
-
return wired;
|
|
11814
|
-
}
|
|
11815
|
-
function getBuiltinVerifier() {
|
|
11816
|
-
if (resolved !== void 0) return resolved;
|
|
11817
|
-
const ed = loadEd25519();
|
|
11818
|
-
const sha512 = loadSha512();
|
|
11819
|
-
if (!ed || !sha512) {
|
|
11820
|
-
resolved = null;
|
|
11821
|
-
return null;
|
|
11822
|
-
}
|
|
11823
|
-
if (!wireSha512(ed, sha512)) {
|
|
11824
|
-
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.");
|
|
11825
|
-
resolved = null;
|
|
11826
|
-
return null;
|
|
11827
|
-
}
|
|
11828
|
-
resolved = async ({
|
|
11829
|
-
messageHex,
|
|
11830
|
-
signature,
|
|
11831
|
-
publicKey
|
|
11832
|
-
}) => {
|
|
11833
|
-
const message = hexToBytes(messageHex);
|
|
11834
|
-
const pub = hexToBytes(publicKey);
|
|
11835
|
-
const sig = decodeSignature(signature);
|
|
11836
|
-
if (!message) {
|
|
11837
|
-
logger.error("[OTA] Bundle SHA-256 is not valid hex \u2014 refusing to stage.");
|
|
11838
|
-
return false;
|
|
11839
|
-
}
|
|
11840
|
-
if (!pub || pub.length !== ED25519_PUBLIC_KEY_BYTES) {
|
|
11841
|
-
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"}.`);
|
|
11842
|
-
return false;
|
|
11843
|
-
}
|
|
11844
|
-
if (!sig) {
|
|
11845
|
-
logger.error("[OTA] Bundle signature is not a 64-byte hex/base64 value.");
|
|
11846
|
-
return false;
|
|
11847
|
-
}
|
|
11848
|
-
try {
|
|
11849
|
-
if (typeof ed.verify === "function") return ed.verify(sig, message, pub);
|
|
11850
|
-
if (typeof ed.verifyAsync === "function") return await ed.verifyAsync(sig, message, pub);
|
|
11851
|
-
return false;
|
|
11852
|
-
} catch {
|
|
11853
|
-
return false;
|
|
11854
|
-
}
|
|
11855
|
-
};
|
|
11856
|
-
__DEV__ && logger.debug("[OTA] Using built-in ed25519 verifier (@noble/ed25519 detected).");
|
|
11857
|
-
return resolved;
|
|
11858
|
-
}
|
|
11859
|
-
function _resetBuiltinVerifier() {
|
|
11860
|
-
resolved = void 0;
|
|
11861
|
-
}
|
|
11862
|
-
var ED25519_SIGNATURE_BYTES, ED25519_PUBLIC_KEY_BYTES, B64_ALPHABET2, resolved;
|
|
11863
|
-
var init_builtinVerifier = __esm({
|
|
11864
|
-
"lib/module/features/ota/crypto/builtinVerifier.js"() {
|
|
11865
|
-
"use strict";
|
|
11866
|
-
init_loadEd25519();
|
|
11867
|
-
init_loadSha512();
|
|
11868
|
-
init_internalLogger();
|
|
11869
|
-
ED25519_SIGNATURE_BYTES = 64;
|
|
11870
|
-
ED25519_PUBLIC_KEY_BYTES = 32;
|
|
11871
|
-
B64_ALPHABET2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
11872
|
-
}
|
|
11873
|
-
});
|
|
11874
|
-
|
|
11875
|
-
// lib/module/features/ota/crypto/nativeVerifier.js
|
|
11876
|
-
function bytesToHex(bytes) {
|
|
11877
|
-
let out = "";
|
|
11878
|
-
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
11879
|
-
return out;
|
|
11880
|
-
}
|
|
11881
|
-
function normalizeHex(value, expectedChars) {
|
|
11882
|
-
const clean2 = value.trim().toLowerCase().replace(/^0x/, "");
|
|
11883
|
-
if (clean2.length !== expectedChars || /[^0-9a-f]/.test(clean2)) return null;
|
|
11884
|
-
return clean2;
|
|
11885
|
-
}
|
|
11886
|
-
function getNativeVerifier() {
|
|
11887
|
-
if (resolved2 !== void 0) return resolved2;
|
|
11888
|
-
const native = NativeScaleBunOta_default;
|
|
11889
|
-
if (!native || typeof native.verifyEd25519 !== "function") {
|
|
11890
|
-
resolved2 = null;
|
|
11891
|
-
return null;
|
|
11892
|
-
}
|
|
11893
|
-
resolved2 = async ({
|
|
11894
|
-
messageHex,
|
|
11895
|
-
signature,
|
|
11896
|
-
publicKey
|
|
11897
|
-
}) => {
|
|
11898
|
-
const message = normalizeHex(messageHex, 64);
|
|
11899
|
-
const key = normalizeHex(publicKey, ED25519_PUBLIC_KEY_HEX_CHARS);
|
|
11900
|
-
const sigBytes = decodeSignature(signature);
|
|
11901
|
-
if (!message || !key || !sigBytes) {
|
|
11902
|
-
logger.error("[OTA] Signature inputs malformed (bundle hash, signature, or public key) \u2014 refusing to stage.");
|
|
11903
|
-
return false;
|
|
11904
|
-
}
|
|
11905
|
-
let verdict;
|
|
11906
|
-
try {
|
|
11907
|
-
verdict = await native.verifyEd25519(message, bytesToHex(sigBytes), key);
|
|
11908
|
-
} catch (err) {
|
|
11909
|
-
logger.warn(`[OTA] Native ed25519 verify failed to run: ${err?.message ?? err}`);
|
|
11910
|
-
verdict = "unavailable";
|
|
11911
|
-
}
|
|
11912
|
-
if (verdict === "valid") return true;
|
|
11913
|
-
if (verdict === "invalid") return false;
|
|
11914
|
-
const builtin = getBuiltinVerifier();
|
|
11915
|
-
if (builtin) {
|
|
11916
|
-
return builtin({
|
|
11917
|
-
messageHex,
|
|
11918
|
-
signature,
|
|
11919
|
-
publicKey
|
|
11920
|
-
});
|
|
11921
|
-
}
|
|
11922
|
-
throw new Error("ed25519 unavailable: this OS has no native implementation (Android < 13) and the optional peers @noble/ed25519 + @noble/hashes are not installed.");
|
|
11923
|
-
};
|
|
11924
|
-
__DEV__ && logger.debug("[OTA] Using native ed25519 verifier (platform crypto).");
|
|
11925
|
-
return resolved2;
|
|
11926
|
-
}
|
|
11927
|
-
var ED25519_PUBLIC_KEY_HEX_CHARS, resolved2;
|
|
11928
|
-
var init_nativeVerifier = __esm({
|
|
11929
|
-
"lib/module/features/ota/crypto/nativeVerifier.js"() {
|
|
11930
|
-
"use strict";
|
|
11931
|
-
init_NativeScaleBunOta();
|
|
11932
|
-
init_internalLogger();
|
|
11933
|
-
init_builtinVerifier();
|
|
11934
|
-
ED25519_PUBLIC_KEY_HEX_CHARS = 64;
|
|
11935
|
-
}
|
|
11936
|
-
});
|
|
11937
|
-
|
|
11938
|
-
// lib/module/features/ota/signature.js
|
|
11939
|
-
async function verifyBundleSignature(bundleSha256, signature, config) {
|
|
11940
|
-
const rawKey = config?.publicKey;
|
|
11941
|
-
const keys = (Array.isArray(rawKey) ? rawKey : rawKey ? [rawKey] : []).filter((k) => typeof k === "string" && k.trim().length > 0);
|
|
11942
|
-
if (rawKey === void 0 || rawKey === "" || rawKey === null) {
|
|
11943
|
-
if (!warnedNotConfigured) {
|
|
11944
|
-
warnedNotConfigured = true;
|
|
11945
|
-
logger.warn("[OTA] No signing public key configured \u2014 bundles are accepted on SHA-256 integrity alone. Configure `ota.publicSigningKey` to enforce authenticity.");
|
|
11946
|
-
}
|
|
11947
|
-
return {
|
|
11948
|
-
ok: true,
|
|
11949
|
-
reason: "not_configured"
|
|
11950
|
-
};
|
|
11951
|
-
}
|
|
11952
|
-
if (keys.length === 0) {
|
|
11953
|
-
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.");
|
|
11954
|
-
return {
|
|
11955
|
-
ok: false,
|
|
11956
|
-
reason: "no_keys"
|
|
11957
|
-
};
|
|
11958
|
-
}
|
|
11959
|
-
if (!signature) {
|
|
11960
|
-
logger.error("[OTA] Bundle has no signature but a signing key is configured \u2014 refusing to stage.");
|
|
11961
|
-
return {
|
|
11962
|
-
ok: false,
|
|
11963
|
-
reason: "missing_signature"
|
|
11964
|
-
};
|
|
11965
|
-
}
|
|
11966
|
-
const verifier = typeof config?.verifier === "function" ? config.verifier : getNativeVerifier() ?? getBuiltinVerifier();
|
|
11967
|
-
if (!verifier) {
|
|
11968
|
-
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`.");
|
|
11969
|
-
return {
|
|
11970
|
-
ok: false,
|
|
11971
|
-
reason: "no_verifier"
|
|
11972
|
-
};
|
|
11973
|
-
}
|
|
11974
|
-
let sawThrow = false;
|
|
11975
|
-
let sawCompletion = false;
|
|
11976
|
-
for (const key of keys) {
|
|
11977
|
-
try {
|
|
11978
|
-
const valid = await verifier({
|
|
11979
|
-
messageHex: bundleSha256,
|
|
11980
|
-
signature,
|
|
11981
|
-
publicKey: key
|
|
11982
|
-
});
|
|
11983
|
-
sawCompletion = true;
|
|
11984
|
-
if (valid) return {
|
|
11985
|
-
ok: true,
|
|
11986
|
-
reason: "verified"
|
|
11987
|
-
};
|
|
11988
|
-
} catch (err) {
|
|
11989
|
-
sawThrow = true;
|
|
11990
|
-
logger.error(`[OTA] Signature verifier threw: ${err?.message ?? err}`);
|
|
11991
|
-
}
|
|
11992
|
-
}
|
|
11993
|
-
if (sawThrow && !sawCompletion) {
|
|
11994
|
-
return {
|
|
11995
|
-
ok: false,
|
|
11996
|
-
reason: "verifier_threw"
|
|
11997
|
-
};
|
|
11998
|
-
}
|
|
11999
|
-
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.");
|
|
12000
|
-
return {
|
|
12001
|
-
ok: false,
|
|
12002
|
-
reason: "invalid_signature"
|
|
12003
|
-
};
|
|
12004
|
-
}
|
|
12005
|
-
var warnedNotConfigured;
|
|
12006
|
-
var init_signature = __esm({
|
|
12007
|
-
"lib/module/features/ota/signature.js"() {
|
|
12008
|
-
"use strict";
|
|
12009
|
-
init_internalLogger();
|
|
12010
|
-
init_builtinVerifier();
|
|
12011
|
-
init_nativeVerifier();
|
|
12012
|
-
warnedNotConfigured = false;
|
|
12013
|
-
}
|
|
12014
|
-
});
|
|
12015
|
-
|
|
12016
11700
|
// lib/module/features/ota/environment.js
|
|
12017
11701
|
function compare(a, b) {
|
|
12018
11702
|
if (a.major !== b.major) return a.major - b.major;
|
|
@@ -12258,7 +11942,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
12258
11942
|
init_geoCountry();
|
|
12259
11943
|
init_deviceAttributes();
|
|
12260
11944
|
init_installationId();
|
|
12261
|
-
init_signature();
|
|
12262
11945
|
init_environment();
|
|
12263
11946
|
init_retry();
|
|
12264
11947
|
init_StorageBackend();
|
|
@@ -12286,15 +11969,15 @@ var init_OtaOrchestrator = __esm({
|
|
|
12286
11969
|
/**
|
|
12287
11970
|
* Initialize the orchestrator. Checks for boot-guard recovery state.
|
|
12288
11971
|
*
|
|
12289
|
-
*
|
|
12290
|
-
*
|
|
12291
|
-
*
|
|
11972
|
+
* Signature enforcement is no longer a JS concern: signed releases are
|
|
11973
|
+
* verified and staged in NATIVE code against RSA keys embedded in the app
|
|
11974
|
+
* package (SB-OTA-RSA-SHA256-V1), and unsigned releases keep working on
|
|
11975
|
+
* SHA-256 integrity alone. There is nothing for the host to configure here.
|
|
12292
11976
|
*/
|
|
12293
11977
|
init(config) {
|
|
12294
11978
|
noThrow(() => {
|
|
12295
11979
|
if (this.initialized) {
|
|
12296
11980
|
this.bootGuardConfig = config ?? this.bootGuardConfig;
|
|
12297
|
-
this.signatureConfig = config?.signature ?? this.signatureConfig;
|
|
12298
11981
|
return;
|
|
12299
11982
|
}
|
|
12300
11983
|
if (!NativeScaleBunOta_default) {
|
|
@@ -12316,7 +11999,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
12316
11999
|
this.initialized = true;
|
|
12317
12000
|
this.enabled = true;
|
|
12318
12001
|
this.bootGuardConfig = config ?? {};
|
|
12319
|
-
this.signatureConfig = config?.signature;
|
|
12320
12002
|
__DEV__ && logger.debug(`[OTA] Orchestrator initialized (RN ${this.environment.rnVersionString ?? "unknown"}${this.environment.bridgeless ? ", bridgeless" : ""}${this.environment.hermes ? `, Hermes HBC v${this.environment.hermesBytecodeVersion ?? "?"}` : ""})`);
|
|
12321
12003
|
const slotState = this.readSlotState();
|
|
12322
12004
|
this.hydrateCurrentBundleFromSlots(slotState);
|
|
@@ -12503,12 +12185,22 @@ var init_OtaOrchestrator = __esm({
|
|
|
12503
12185
|
const env = this.environment ?? detectOtaEnvironment();
|
|
12504
12186
|
const mobileOS = resolveMobileOS();
|
|
12505
12187
|
if (!mobileOS) throw new Error("OTA is not supported on this platform");
|
|
12188
|
+
let trustedKeyIds;
|
|
12189
|
+
try {
|
|
12190
|
+
const raw = NativeScaleBunOta_default?.getTrustedKeyIds?.();
|
|
12191
|
+
if (raw) {
|
|
12192
|
+
const arr2 = JSON.parse(raw);
|
|
12193
|
+
if (Array.isArray(arr2) && arr2.length) trustedKeyIds = arr2.slice(0, 16);
|
|
12194
|
+
}
|
|
12195
|
+
} catch {
|
|
12196
|
+
}
|
|
12506
12197
|
const payload = {
|
|
12507
12198
|
appVersion: requestParams.appVersion,
|
|
12508
12199
|
platform: mobileOS,
|
|
12509
12200
|
installationId: requestParams.installationId,
|
|
12510
12201
|
currentBundleId: this.currentBundle?.id,
|
|
12511
12202
|
currentBundleHash: this.currentBundle?.sha256,
|
|
12203
|
+
trustedKeyIds,
|
|
12512
12204
|
channelName: requestParams.channelName,
|
|
12513
12205
|
// Host-supplied country wins; otherwise the cached edge-Worker lookup
|
|
12514
12206
|
// fills in (see geoCountry.ts). Undefined when neither knows — the
|
|
@@ -12622,19 +12314,6 @@ var init_OtaOrchestrator = __esm({
|
|
|
12622
12314
|
releaseId: bundle.releaseId,
|
|
12623
12315
|
version: bundle.version
|
|
12624
12316
|
});
|
|
12625
|
-
const signatureOutcome = await verifyBundleSignature(bundle.sha256, bundle.signature, this.signatureConfig);
|
|
12626
|
-
if (!signatureOutcome.ok) {
|
|
12627
|
-
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
12628
|
-
releaseId: bundle.releaseId,
|
|
12629
|
-
error: `Signature check failed: ${signatureOutcome.reason}`,
|
|
12630
|
-
version: bundle.version
|
|
12631
|
-
});
|
|
12632
|
-
return {
|
|
12633
|
-
status: "ERROR",
|
|
12634
|
-
error: `Bundle signature check failed (${signatureOutcome.reason})`,
|
|
12635
|
-
bundle
|
|
12636
|
-
};
|
|
12637
|
-
}
|
|
12638
12317
|
otaEventEmitter.emitSimple("DOWNLOAD_STARTED", bundle.id, {
|
|
12639
12318
|
releaseId: bundle.releaseId,
|
|
12640
12319
|
version: bundle.version
|
|
@@ -12682,39 +12361,75 @@ var init_OtaOrchestrator = __esm({
|
|
|
12682
12361
|
}
|
|
12683
12362
|
});
|
|
12684
12363
|
let staged = false;
|
|
12685
|
-
if (bundle.
|
|
12686
|
-
|
|
12364
|
+
if (bundle.envelope) {
|
|
12365
|
+
const envelopeJson = JSON.stringify({
|
|
12366
|
+
...bundle.envelope,
|
|
12367
|
+
downloadUrl: bundle.url
|
|
12368
|
+
});
|
|
12369
|
+
let verdict;
|
|
12687
12370
|
try {
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
|
|
12692
|
-
|
|
12693
|
-
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
__DEV__ && logger.debug("[OTA] Patch applied successfully");
|
|
12697
|
-
} else {
|
|
12698
|
-
logger.warn("[OTA] Patch staging returned false \u2014 falling back to full bundle");
|
|
12699
|
-
}
|
|
12700
|
-
} catch (patchErr) {
|
|
12701
|
-
logger.warn(`[OTA] Patch staging failed: ${patchErr.message} \u2014 falling back to full bundle`);
|
|
12371
|
+
const verdictJson = await NativeScaleBunOta_default.verifyAndStageRelease(envelopeJson);
|
|
12372
|
+
verdict = JSON.parse(verdictJson);
|
|
12373
|
+
} catch (err) {
|
|
12374
|
+
verdict = {
|
|
12375
|
+
ok: false,
|
|
12376
|
+
code: "verify_stage_threw",
|
|
12377
|
+
message: err?.message ?? String(err)
|
|
12378
|
+
};
|
|
12702
12379
|
}
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
label: `download v${bundle.version}`,
|
|
12714
|
-
attempts: 3
|
|
12380
|
+
if (!verdict.ok) {
|
|
12381
|
+
unsubscribeProgress();
|
|
12382
|
+
postProgress(0, "FAILED");
|
|
12383
|
+
logger.error(`[OTA] Native verify/stage refused the release: ${verdict.code ?? "unknown"}${verdict.message ? ` \u2014 ${verdict.message}` : ""}`);
|
|
12384
|
+
otaEventEmitter.emitSimple("APPLY_FAILED", bundle.id, {
|
|
12385
|
+
releaseId: bundle.releaseId,
|
|
12386
|
+
// `error` is mapped onto the wire `errorCode` by deliverOtaEvents —
|
|
12387
|
+
// carry the stable native verdict code there.
|
|
12388
|
+
error: verdict.code ?? "verify_and_stage_release_failed",
|
|
12389
|
+
version: bundle.version
|
|
12715
12390
|
});
|
|
12716
|
-
|
|
12717
|
-
|
|
12391
|
+
return {
|
|
12392
|
+
status: "ERROR",
|
|
12393
|
+
error: `Release verification failed (${verdict.code ?? "unknown"})`,
|
|
12394
|
+
bundle
|
|
12395
|
+
};
|
|
12396
|
+
}
|
|
12397
|
+
staged = true;
|
|
12398
|
+
} else {
|
|
12399
|
+
if (bundle.patch && NativeScaleBunOta_default.stagePatch) {
|
|
12400
|
+
__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`);
|
|
12401
|
+
try {
|
|
12402
|
+
staged = await NativeScaleBunOta_default.stagePatch(
|
|
12403
|
+
bundle.patch.url,
|
|
12404
|
+
bundle.patch.sha256,
|
|
12405
|
+
bundle.sha256
|
|
12406
|
+
// expected full-bundle hash after patching
|
|
12407
|
+
);
|
|
12408
|
+
if (staged) {
|
|
12409
|
+
patchUsed = true;
|
|
12410
|
+
__DEV__ && logger.debug("[OTA] Patch applied successfully");
|
|
12411
|
+
} else {
|
|
12412
|
+
logger.warn("[OTA] Patch staging returned false \u2014 falling back to full bundle");
|
|
12413
|
+
}
|
|
12414
|
+
} catch (patchErr) {
|
|
12415
|
+
logger.warn(`[OTA] Patch staging failed: ${patchErr.message} \u2014 falling back to full bundle`);
|
|
12416
|
+
}
|
|
12417
|
+
}
|
|
12418
|
+
if (!staged) {
|
|
12419
|
+
try {
|
|
12420
|
+
staged = await retryWithBackoff(async () => {
|
|
12421
|
+
const ok = await NativeScaleBunOta_default.stageBundle(bundle.url, bundle.sha256);
|
|
12422
|
+
if (!ok) {
|
|
12423
|
+
throw new Error("stageBundle returned false");
|
|
12424
|
+
}
|
|
12425
|
+
return ok;
|
|
12426
|
+
}, {
|
|
12427
|
+
label: `download v${bundle.version}`,
|
|
12428
|
+
attempts: 3
|
|
12429
|
+
});
|
|
12430
|
+
} catch {
|
|
12431
|
+
staged = false;
|
|
12432
|
+
}
|
|
12718
12433
|
}
|
|
12719
12434
|
}
|
|
12720
12435
|
unsubscribeProgress();
|
|
@@ -14840,18 +14555,9 @@ var SHAPE = {
|
|
|
14840
14555
|
kind: "str",
|
|
14841
14556
|
opt: true
|
|
14842
14557
|
},
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
|
|
14846
|
-
},
|
|
14847
|
-
// Rotation support: several pinned keys, any of which may verify a
|
|
14848
|
-
// bundle. Ship a build pinning [old, new], re-sign server-side with
|
|
14849
|
-
// new, drop old next release — no emergency store submission when a
|
|
14850
|
-
// key must be replaced. Merged with publicSigningKey by the facade.
|
|
14851
|
-
publicSigningKeys: {
|
|
14852
|
-
kind: "strArr",
|
|
14853
|
-
opt: true
|
|
14854
|
-
},
|
|
14558
|
+
// No signing-key fields: signed releases are verified natively against
|
|
14559
|
+
// RSA keys embedded in the app package (SB-OTA-RSA-SHA256-V1). JS must
|
|
14560
|
+
// not supply a trusted key.
|
|
14855
14561
|
mandatoryBlocksUi: bool(false)
|
|
14856
14562
|
}
|
|
14857
14563
|
}
|
|
@@ -15654,6 +15360,23 @@ function installNetworkMonitoring(onEvent, options = {}) {
|
|
|
15654
15360
|
});
|
|
15655
15361
|
}
|
|
15656
15362
|
|
|
15363
|
+
// lib/module/features/network/thirdParty.js
|
|
15364
|
+
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";
|
|
15365
|
+
function isThirdPartyBeacon(url, hosts) {
|
|
15366
|
+
if (!url) return false;
|
|
15367
|
+
let host = "";
|
|
15368
|
+
try {
|
|
15369
|
+
host = new URL(url).hostname.toLowerCase();
|
|
15370
|
+
} catch {
|
|
15371
|
+
return false;
|
|
15372
|
+
}
|
|
15373
|
+
if (!host) return false;
|
|
15374
|
+
for (const suffix of (hosts ?? THIRD_PARTY_BEACON_HOSTS).split(",")) {
|
|
15375
|
+
if (host === suffix || host.endsWith("." + suffix)) return true;
|
|
15376
|
+
}
|
|
15377
|
+
return false;
|
|
15378
|
+
}
|
|
15379
|
+
|
|
15657
15380
|
// lib/module/features/network/NetworkFeature.js
|
|
15658
15381
|
init_networkAdapter();
|
|
15659
15382
|
init_SessionManager();
|
|
@@ -15669,10 +15392,12 @@ var NetworkFeature = class {
|
|
|
15669
15392
|
if (this.active) return;
|
|
15670
15393
|
installNetworkMonitoring((data) => {
|
|
15671
15394
|
const isInternal = isSdkInternalUrl(data.url, this.options.sdkBaseUrl);
|
|
15672
|
-
|
|
15395
|
+
const isBeaconOrDenied = this.options.captureThirdParty !== true && isThirdPartyBeacon(data.url, this.options.thirdPartyHosts) || matchesDenyUrl(data.url, this.options.denyUrls);
|
|
15396
|
+
const isExcluded = isInternal || isBeaconOrDenied;
|
|
15397
|
+
if (!isExcluded) {
|
|
15673
15398
|
this.trackFn("$network_request", data);
|
|
15674
15399
|
}
|
|
15675
|
-
if (data.type === "network") {
|
|
15400
|
+
if (data.type === "network" && !isBeaconOrDenied) {
|
|
15676
15401
|
reportNetworkRequest({
|
|
15677
15402
|
method: data.method,
|
|
15678
15403
|
url: data.url,
|
|
@@ -15688,7 +15413,7 @@ var NetworkFeature = class {
|
|
|
15688
15413
|
requestBody: data.requestBody,
|
|
15689
15414
|
responseBody: data.responseBody
|
|
15690
15415
|
});
|
|
15691
|
-
if (!
|
|
15416
|
+
if (!isExcluded) {
|
|
15692
15417
|
const sessionManager = SessionManager.getExistingInstance();
|
|
15693
15418
|
if (sessionManager) {
|
|
15694
15419
|
const backendTransport = sessionManager.getBackendTransport();
|
|
@@ -15719,6 +15444,13 @@ var NetworkFeature = class {
|
|
|
15719
15444
|
return this.active;
|
|
15720
15445
|
}
|
|
15721
15446
|
};
|
|
15447
|
+
function matchesDenyUrl(url, patterns) {
|
|
15448
|
+
if (!patterns || !patterns.length || !url) return false;
|
|
15449
|
+
for (const p of patterns) {
|
|
15450
|
+
if (typeof p === "string" ? url.includes(p) : p.test(url)) return true;
|
|
15451
|
+
}
|
|
15452
|
+
return false;
|
|
15453
|
+
}
|
|
15722
15454
|
function isSdkInternalUrl(url, sdkBaseUrl) {
|
|
15723
15455
|
if (!url) return false;
|
|
15724
15456
|
try {
|
|
@@ -20311,17 +20043,11 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
20311
20043
|
/**
|
|
20312
20044
|
* Boot the OTA orchestrator when the init config asks for it.
|
|
20313
20045
|
*
|
|
20314
|
-
*
|
|
20315
|
-
*
|
|
20316
|
-
*
|
|
20317
|
-
*
|
|
20318
|
-
*
|
|
20319
|
-
*
|
|
20320
|
-
* Verification comes from `ota.verifySignature` when supplied, else the
|
|
20321
|
-
* built-in @noble-based verifier (optional peers). Pinning keys with
|
|
20322
|
-
* NEITHER available is fail-CLOSED by design (signature.ts) — an update
|
|
20323
|
-
* that cannot be verified is not installed. We say that out loud rather
|
|
20324
|
-
* than letting the app discover it as a silent no-update condition.
|
|
20046
|
+
* Signature enforcement is no longer configured from JS. Signed releases are
|
|
20047
|
+
* verified and staged in NATIVE code against RSA keys embedded in the app
|
|
20048
|
+
* package (SB-OTA-RSA-SHA256-V1); unsigned releases keep working on SHA-256
|
|
20049
|
+
* integrity. There are no signing-key or `verifySignature` options to pass
|
|
20050
|
+
* through — the host embeds its trusted keys natively.
|
|
20325
20051
|
*/
|
|
20326
20052
|
_maybeStartOta(rawConfig) {
|
|
20327
20053
|
const ota = rawConfig?.ota;
|
|
@@ -20330,29 +20056,8 @@ var ScaleBunFacade = class _ScaleBunFacade {
|
|
|
20330
20056
|
const {
|
|
20331
20057
|
otaOrchestrator: otaOrchestrator2
|
|
20332
20058
|
} = (init_OtaOrchestrator(), __toCommonJS(OtaOrchestrator_exports));
|
|
20333
|
-
const rawSingle = ota.publicSigningKey;
|
|
20334
|
-
const rawList = ota.publicSigningKeys;
|
|
20335
|
-
const publicKeys = Array.from(new Set([...typeof rawSingle === "string" && rawSingle ? [rawSingle] : [], ...Array.isArray(rawList) ? rawList : []].filter((k) => typeof k === "string" && k.trim().length > 0)));
|
|
20336
|
-
const signingRequested = publicKeys.length > 0 || Array.isArray(rawList);
|
|
20337
|
-
const verifier = ota.verifySignature;
|
|
20338
|
-
if (signingRequested && typeof verifier !== "function") {
|
|
20339
|
-
const {
|
|
20340
|
-
getBuiltinVerifier: getBuiltinVerifier2
|
|
20341
|
-
} = (init_builtinVerifier(), __toCommonJS(builtinVerifier_exports));
|
|
20342
|
-
if (!getBuiltinVerifier2()) {
|
|
20343
|
-
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`.");
|
|
20344
|
-
}
|
|
20345
|
-
}
|
|
20346
20059
|
const healthyAfterMs = typeof ota.healthyAfterMs === "number" ? ota.healthyAfterMs : typeof ota.healthyTimeoutMs === "number" ? ota.healthyTimeoutMs : void 0;
|
|
20347
20060
|
otaOrchestrator2.init({
|
|
20348
|
-
...signingRequested ? {
|
|
20349
|
-
signature: {
|
|
20350
|
-
// Single key stays a plain string — the shape every
|
|
20351
|
-
// existing consumer and test already handles.
|
|
20352
|
-
publicKey: publicKeys.length === 1 ? publicKeys[0] : publicKeys,
|
|
20353
|
-
verifier
|
|
20354
|
-
}
|
|
20355
|
-
} : {},
|
|
20356
20061
|
...healthyAfterMs !== void 0 ? {
|
|
20357
20062
|
healthyAfterMs
|
|
20358
20063
|
} : {}
|
|
@@ -24310,11 +24015,11 @@ var normalizedWheelValue = (value) => value.toLowerCase().replace(/\s+/g, " ").t
|
|
|
24310
24015
|
function resolveWheelOutcome(segments, source) {
|
|
24311
24016
|
const identity = wheelOutcomeIdentity(source);
|
|
24312
24017
|
const index = findWheelOutcomeIndex(segments, identity);
|
|
24313
|
-
const
|
|
24018
|
+
const resolved = index >= 0;
|
|
24314
24019
|
return {
|
|
24315
|
-
index:
|
|
24316
|
-
label: identity.resultLabel || (
|
|
24317
|
-
resolved
|
|
24020
|
+
index: resolved ? index : 0,
|
|
24021
|
+
label: identity.resultLabel || (resolved ? segments[index]?.fullLabel ?? "" : ""),
|
|
24022
|
+
resolved
|
|
24318
24023
|
};
|
|
24319
24024
|
}
|
|
24320
24025
|
function SpinWheelInApp(props) {
|
|
@@ -33173,8 +32878,8 @@ function EngagePromptProvider({
|
|
|
33173
32878
|
__DEV__ && logger.debug("[Engage] no eligible in-app message to render");
|
|
33174
32879
|
return false;
|
|
33175
32880
|
}
|
|
33176
|
-
const
|
|
33177
|
-
activeInAppVariantRef.current = message.experiment?.enabled ?
|
|
32881
|
+
const resolved = selectInAppVariant(message, getDeviceId());
|
|
32882
|
+
activeInAppVariantRef.current = message.experiment?.enabled ? resolved : null;
|
|
33178
32883
|
store.recordImpression(message.id, Date.now(), message.revision);
|
|
33179
32884
|
setActiveInApp(message);
|
|
33180
32885
|
return true;
|