privateer-agent 0.15.4 → 0.15.6
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/README.md +11 -0
- package/bin/boot-bundle.d.mts +2 -0
- package/bin/boot-bundle.mjs +44 -0
- package/bin/fresh-supervisor.d.mts +5 -1
- package/bin/fresh-supervisor.mjs +47 -10
- package/bin/privateer-launch.mjs +27 -3
- package/bin/privateer-subagent.mjs +5 -2
- package/bin/privateer-verify.mjs +12 -0
- package/bin/session-recovery.d.mts +53 -0
- package/bin/session-recovery.mjs +240 -0
- package/bin/session-seal-preload.mjs +17 -0
- package/bin/session-seal.d.mts +25 -0
- package/bin/session-seal.mjs +242 -0
- package/dist-boot/boot.mjs +3 -0
- package/dist-boot/c-27JKSMWL.mjs +1460 -0
- package/dist-boot/c-2BMRFV24.mjs +269 -0
- package/dist-boot/c-2CTF6JA3.mjs +166 -0
- package/dist-boot/c-2G3BBHRJ.mjs +84 -0
- package/dist-boot/c-2IDLKXX6.mjs +250 -0
- package/dist-boot/c-2NJD5VYM.mjs +8 -0
- package/dist-boot/c-2S5EEQPW.mjs +1602 -0
- package/dist-boot/c-2WRDCUVI.mjs +25 -0
- package/dist-boot/c-2YN3M256.mjs +14 -0
- package/dist-boot/c-37GM3GMU.mjs +187 -0
- package/dist-boot/c-3STKRSVO.mjs +2480 -0
- package/dist-boot/c-3TPV6TQQ.mjs +2320 -0
- package/dist-boot/c-3UVHLQDJ.mjs +225 -0
- package/dist-boot/c-42PUUZYC.mjs +697 -0
- package/dist-boot/c-4D5UMWVW.mjs +683 -0
- package/dist-boot/c-4ETZRMTA.mjs +46 -0
- package/dist-boot/c-4QT25BD5.mjs +1482 -0
- package/dist-boot/c-4WVR32AP.mjs +2579 -0
- package/dist-boot/c-5EMM567G.mjs +152 -0
- package/dist-boot/c-5FA4CV63.mjs +145 -0
- package/dist-boot/c-5GV3X3RB.mjs +14 -0
- package/dist-boot/c-5JCEPRIY.mjs +52 -0
- package/dist-boot/c-5NDKIIWT.mjs +16 -0
- package/dist-boot/c-5QL3AR26.mjs +4665 -0
- package/dist-boot/c-5XY6MFKN.mjs +5142 -0
- package/dist-boot/c-65UZIZC7.mjs +2897 -0
- package/dist-boot/c-6FUXKHOK.mjs +1969 -0
- package/dist-boot/c-6JHWQE4V.mjs +18242 -0
- package/dist-boot/c-6LOBR4I6.mjs +5280 -0
- package/dist-boot/c-6QHOZXKQ.mjs +14394 -0
- package/dist-boot/c-6ZO2SADQ.mjs +178 -0
- package/dist-boot/c-6ZORVVZP.mjs +1449 -0
- package/dist-boot/c-76QH4UCP.mjs +2441 -0
- package/dist-boot/c-77GO5L5Y.mjs +20982 -0
- package/dist-boot/c-7M23XFTN.mjs +5052 -0
- package/dist-boot/c-7TC367E2.mjs +32 -0
- package/dist-boot/c-7UOG7POX.mjs +1524 -0
- package/dist-boot/c-7VHP3FLN.mjs +376 -0
- package/dist-boot/c-A73JHZP2.mjs +13207 -0
- package/dist-boot/c-ABXULAFL.mjs +71 -0
- package/dist-boot/c-ANDD2AQO.mjs +74 -0
- package/dist-boot/c-AOY77AZL.mjs +1430 -0
- package/dist-boot/c-AXEPZ22I.mjs +108 -0
- package/dist-boot/c-B2Y7VNHU.mjs +427 -0
- package/dist-boot/c-B7VJUH73.mjs +77 -0
- package/dist-boot/c-BFW6CDVL.mjs +24 -0
- package/dist-boot/c-BHNIF2AR.mjs +188 -0
- package/dist-boot/c-BKF2O6BL.mjs +23 -0
- package/dist-boot/c-BOZXFTZN.mjs +8 -0
- package/dist-boot/c-BRNYJWZL.mjs +28 -0
- package/dist-boot/c-C3N4Z2HO.mjs +93 -0
- package/dist-boot/c-C4IV7ZFG.mjs +717 -0
- package/dist-boot/c-C5NK6UYV.mjs +793 -0
- package/dist-boot/c-C67644MB.mjs +1659 -0
- package/dist-boot/c-C6S2YB53.mjs +620 -0
- package/dist-boot/c-CDBEJSBF.mjs +37 -0
- package/dist-boot/c-CG47HBKU.mjs +435 -0
- package/dist-boot/c-CKCTWCT5.mjs +51 -0
- package/dist-boot/c-CN77DCGF.mjs +60 -0
- package/dist-boot/c-CRBXNKFO.mjs +1795 -0
- package/dist-boot/c-CRIHASQ6.mjs +973 -0
- package/dist-boot/c-CU4NGAUO.mjs +3786 -0
- package/dist-boot/c-CWRFLGQU.mjs +1883 -0
- package/dist-boot/c-CYQB2USO.mjs +32 -0
- package/dist-boot/c-DEE67DJO.mjs +379 -0
- package/dist-boot/c-DIFDBYOP.mjs +1258 -0
- package/dist-boot/c-DOSGVXDZ.mjs +34879 -0
- package/dist-boot/c-DR7XQI32.mjs +381 -0
- package/dist-boot/c-DTGPSB46.mjs +14666 -0
- package/dist-boot/c-DTHDNA4P.mjs +320 -0
- package/dist-boot/c-E4Z3Y5B5.mjs +3864 -0
- package/dist-boot/c-E66Z2TI7.mjs +203 -0
- package/dist-boot/c-EBU6RLRV.mjs +1527 -0
- package/dist-boot/c-EBZZNGSF.mjs +432 -0
- package/dist-boot/c-EC3LJ2KF.mjs +1032 -0
- package/dist-boot/c-EQBKXWNX.mjs +15829 -0
- package/dist-boot/c-EUEPIW4S.mjs +574 -0
- package/dist-boot/c-EX4YVWAT.mjs +378 -0
- package/dist-boot/c-EYZGSAJU.mjs +98 -0
- package/dist-boot/c-F75VRKY4.mjs +172 -0
- package/dist-boot/c-FH4NXKHV.mjs +177 -0
- package/dist-boot/c-FHNUCTEM.mjs +140 -0
- package/dist-boot/c-FJ5T42DS.mjs +5349 -0
- package/dist-boot/c-FPCNRL7G.mjs +1728 -0
- package/dist-boot/c-G4DLN7Q4.mjs +8 -0
- package/dist-boot/c-G56GYQFZ.mjs +2507 -0
- package/dist-boot/c-GLIOERDQ.mjs +1200 -0
- package/dist-boot/c-GMJ5HUBM.mjs +14 -0
- package/dist-boot/c-H3HSGMAQ.mjs +461 -0
- package/dist-boot/c-HIKTO6P7.mjs +275 -0
- package/dist-boot/c-I2MCJ34O.mjs +464 -0
- package/dist-boot/c-I3C6LBW4.mjs +765 -0
- package/dist-boot/c-I54OKQHW.mjs +387 -0
- package/dist-boot/c-IFSP3R4D.mjs +80 -0
- package/dist-boot/c-IZK77VB7.mjs +171 -0
- package/dist-boot/c-J3Y25UGE.mjs +54 -0
- package/dist-boot/c-J5IESS7W.mjs +27750 -0
- package/dist-boot/c-JJWZBY7L.mjs +254 -0
- package/dist-boot/c-JKTFXTGF.mjs +463 -0
- package/dist-boot/c-JN57AXQ4.mjs +77 -0
- package/dist-boot/c-KFIO6LUW.mjs +1096 -0
- package/dist-boot/c-KIEMEFWW.mjs +478 -0
- package/dist-boot/c-KIVXWBYD.mjs +28 -0
- package/dist-boot/c-KP6CRIP4.mjs +580 -0
- package/dist-boot/c-L26VOJX4.mjs +11558 -0
- package/dist-boot/c-L5GHWG5G.mjs +5988 -0
- package/dist-boot/c-M7NSQAKK.mjs +353 -0
- package/dist-boot/c-MCEWOPBW.mjs +56 -0
- package/dist-boot/c-MGPIEG7I.mjs +20 -0
- package/dist-boot/c-NREXJV3M.mjs +393 -0
- package/dist-boot/c-O5PP6CTX.mjs +162 -0
- package/dist-boot/c-OBQDHSXM.mjs +1774 -0
- package/dist-boot/c-OLQHHDD4.mjs +21 -0
- package/dist-boot/c-OXW4JQQH.mjs +319 -0
- package/dist-boot/c-P3D7ZUWN.mjs +1288 -0
- package/dist-boot/c-P4PQZVV5.mjs +948 -0
- package/dist-boot/c-P63T3XL2.mjs +26373 -0
- package/dist-boot/c-P6N5DQGZ.mjs +194 -0
- package/dist-boot/c-P7MLKQFC.mjs +58 -0
- package/dist-boot/c-PB3PZJL5.mjs +1276 -0
- package/dist-boot/c-PTN4S6G2.mjs +1259 -0
- package/dist-boot/c-PYCE5J7U.mjs +1083 -0
- package/dist-boot/c-Q4HYCRLB.mjs +19410 -0
- package/dist-boot/c-QUXMETGG.mjs +68 -0
- package/dist-boot/c-RDMJZCOX.mjs +356 -0
- package/dist-boot/c-ROGJK76M.mjs +17 -0
- package/dist-boot/c-RQJJ26T4.mjs +708 -0
- package/dist-boot/c-RSKCUPJF.mjs +7559 -0
- package/dist-boot/c-RVJU4O6P.mjs +37 -0
- package/dist-boot/c-S2RVHR3M.mjs +165 -0
- package/dist-boot/c-SKMK3PEE.mjs +91120 -0
- package/dist-boot/c-TI7SM4EG.mjs +20 -0
- package/dist-boot/c-TILJ6GXD.mjs +1235 -0
- package/dist-boot/c-TT6BCUJD.mjs +114 -0
- package/dist-boot/c-U4ITOXAN.mjs +2107 -0
- package/dist-boot/c-UGERXVO2.mjs +7120 -0
- package/dist-boot/c-UPBH5AXY.mjs +1363 -0
- package/dist-boot/c-US2PUNZL.mjs +597 -0
- package/dist-boot/c-UU4WZPPE.mjs +20 -0
- package/dist-boot/c-UWVIYPQS.mjs +39 -0
- package/dist-boot/c-V4RHQBPQ.mjs +17683 -0
- package/dist-boot/c-V5MRVK47.mjs +351 -0
- package/dist-boot/c-V6CL266R.mjs +1755 -0
- package/dist-boot/c-VJTMFKYE.mjs +14975 -0
- package/dist-boot/c-VJTRUKIB.mjs +773 -0
- package/dist-boot/c-W4IFSHPW.mjs +43 -0
- package/dist-boot/c-WETGB2CF.mjs +113 -0
- package/dist-boot/c-WO3QSZUU.mjs +1337 -0
- package/dist-boot/c-WQLH73TY.mjs +242 -0
- package/dist-boot/c-XL735JW5.mjs +500 -0
- package/dist-boot/c-Y7HADL5B.mjs +1107 -0
- package/dist-boot/c-YGQHSVSK.mjs +41 -0
- package/dist-boot/c-YMP2AMEB.mjs +10350 -0
- package/dist-boot/c-YRDO2WEF.mjs +12012 -0
- package/dist-boot/c-YUSTRATJ.mjs +8 -0
- package/dist-boot/c-Z2262R22.mjs +2697 -0
- package/dist-boot/c-Z4ALQMJC.mjs +709 -0
- package/dist-boot/c-Z5ZUQVUY.mjs +62 -0
- package/dist-boot/c-ZDZUKXF7.mjs +874 -0
- package/dist-boot/c-ZJYLGIC3.mjs +32 -0
- package/dist-boot/c-ZNA5CDMI.mjs +77 -0
- package/dist-boot/c-ZSO6DI3M.mjs +204 -0
- package/dist-boot/c-ZWUVHKMK.mjs +100 -0
- package/dist-boot/c-ZY4MESEW.mjs +832 -0
- package/dist-boot/c-ZZ5REI4L.mjs +329 -0
- package/dist-boot/cli.mjs +51 -0
- package/dist-boot/ext-anthropic-attribution.mjs +207 -0
- package/dist-boot/ext-pi-background-tasks.mjs +7234 -0
- package/dist-boot/ext-pi-subagents.mjs +37 -0
- package/dist-boot/ext-privateer-account.mjs +4426 -0
- package/dist-boot/ext-privateer-brand.mjs +5274 -0
- package/dist-boot/ext-privateer-codemode.mjs +37 -0
- package/dist-boot/ext-privateer-computer.mjs +626 -0
- package/dist-boot/ext-privateer-connect.mjs +1862 -0
- package/dist-boot/ext-privateer-context.mjs +323 -0
- package/dist-boot/ext-privateer-desktop.mjs +92 -0
- package/dist-boot/ext-privateer-final-audit.mjs +48 -0
- package/dist-boot/ext-privateer-fresh.mjs +81 -0
- package/dist-boot/ext-privateer-gate.mjs +5959 -0
- package/dist-boot/ext-privateer-hints.mjs +236 -0
- package/dist-boot/ext-privateer-media.mjs +2626 -0
- package/dist-boot/ext-privateer-models.mjs +3397 -0
- package/dist-boot/ext-privateer-posture.mjs +3006 -0
- package/dist-boot/ext-privateer-privacy.mjs +4578 -0
- package/dist-boot/ext-privateer-recovery.mjs +287 -0
- package/dist-boot/ext-privateer-speak.mjs +4754 -0
- package/dist-boot/ext-privateer-terminals.mjs +725 -0
- package/dist-boot/ext-privateer-tools.mjs +528 -0
- package/dist-boot/ext-privateer-truncation.mjs +74 -0
- package/dist-boot/ext-privateer-update.mjs +210 -0
- package/dist-boot/ext-privateer-vision.mjs +148 -0
- package/dist-boot/ext-privateer-web.mjs +543 -0
- package/dist-boot/ext-rpiv-ask-user-question.mjs +710 -0
- package/dist-boot/manifest.json +9 -0
- package/extensions/privateer-brand.ts +3 -1
- package/extensions/privateer-context.ts +4 -2
- package/extensions/privateer-final-audit.ts +17 -0
- package/extensions/privateer-gate.ts +55 -9
- package/extensions/privateer-recovery.ts +175 -0
- package/extensions/privateer-web.ts +4 -4
- package/package.json +3 -1
- package/patches/@earendil-works+pi-coding-agent+1.3.0.patch +199 -2
- package/patches/pi-background-tasks+2.6.10.patch +91 -7
- package/src/cli/chat.ts +7 -1
- package/src/config/moatManifest.json +2 -0
- package/src/config/whatsNew.json +4 -4
- package/src/context.ts +33 -5
- package/src/engine/finalAudit.ts +38 -0
- package/src/engine/stallWatchdog.ts +8 -1
- package/src/providers/account.ts +34 -3
- package/src/remote/attentionPush.ts +40 -0
- package/src/remote/liveTaskSession.ts +7 -1
- package/src/remote/relayClient.ts +38 -4
- package/src/remote/remoteBridge.ts +17 -0
- package/src/remote/transcriptSnapshot.ts +104 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import{createRequire as __pvCR}from"node:module";import{fileURLToPath as __pvF}from"node:url";import{dirname as __pvD}from"node:path";const require=__pvCR(import.meta.url);const __pvUrl=(r)=>new URL(r,import.meta.url).href;const __pvFile=(r)=>__pvF(new URL(r,import.meta.url));
|
|
2
|
+
import {
|
|
3
|
+
HashAlgorithms,
|
|
4
|
+
Uint8ArrayToBase64,
|
|
5
|
+
Uint8ArrayToHex,
|
|
6
|
+
Uint8ArrayToString,
|
|
7
|
+
base64ToUint8Array,
|
|
8
|
+
canonicalize,
|
|
9
|
+
hexToUint8Array,
|
|
10
|
+
importKey,
|
|
11
|
+
stringToUint8Array,
|
|
12
|
+
verifySignature
|
|
13
|
+
} from "./c-U4ITOXAN.mjs";
|
|
14
|
+
import "./c-4WVR32AP.mjs";
|
|
15
|
+
import {
|
|
16
|
+
__name
|
|
17
|
+
} from "./c-CN77DCGF.mjs";
|
|
18
|
+
|
|
19
|
+
// node_modules/@freedomofpress/tuf-browser/dist/crypto.js
|
|
20
|
+
function getRoleKeys(keys, keyids) {
|
|
21
|
+
const roleKeys = new Map(keys);
|
|
22
|
+
for (const key of keys.keys()) {
|
|
23
|
+
if (!keyids.includes(key)) {
|
|
24
|
+
roleKeys.delete(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return roleKeys;
|
|
28
|
+
}
|
|
29
|
+
__name(getRoleKeys, "getRoleKeys");
|
|
30
|
+
async function loadKeys(keys) {
|
|
31
|
+
const importedKeys = /* @__PURE__ */ new Map();
|
|
32
|
+
for (const keyId in keys) {
|
|
33
|
+
const key = keys[keyId];
|
|
34
|
+
const canonicalBytes = stringToUint8Array(canonicalize(key));
|
|
35
|
+
const verified_keyId = Uint8ArrayToHex(new Uint8Array(await crypto.subtle.digest(HashAlgorithms.SHA256, canonicalBytes)));
|
|
36
|
+
if (importedKeys.has(verified_keyId)) {
|
|
37
|
+
throw new Error("Duplicate keyId found!");
|
|
38
|
+
}
|
|
39
|
+
if (verified_keyId !== keyId) {
|
|
40
|
+
console.warn(`KeyId ${keyId} does not match the expected ${verified_keyId}, importing anyway the provided one for proper referencing.`);
|
|
41
|
+
}
|
|
42
|
+
importedKeys.set(keyId, await importKey(key.keytype, key.scheme, key.keyval.public));
|
|
43
|
+
}
|
|
44
|
+
return importedKeys;
|
|
45
|
+
}
|
|
46
|
+
__name(loadKeys, "loadKeys");
|
|
47
|
+
async function checkSignatures(keys, roleKeys, signed, signatures, threshold) {
|
|
48
|
+
if (threshold < 1) {
|
|
49
|
+
throw new Error("Threshold must be at least 1");
|
|
50
|
+
}
|
|
51
|
+
if (threshold > keys.size) {
|
|
52
|
+
throw new Error("Threshold is bigger than the number of keys provided, something is wrong.");
|
|
53
|
+
}
|
|
54
|
+
const keyIds = new Set(roleKeys);
|
|
55
|
+
const signed_canon = canonicalize(signed);
|
|
56
|
+
let valid_signatures = 0;
|
|
57
|
+
for (const signature of signatures) {
|
|
58
|
+
if (!keyIds.has(signature.keyid)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
keyIds.delete(signature.keyid);
|
|
62
|
+
const key = keys.get(signature.keyid);
|
|
63
|
+
const sig = hexToUint8Array(signature.sig);
|
|
64
|
+
if (!key) {
|
|
65
|
+
throw new Error("Keyid was empty.");
|
|
66
|
+
}
|
|
67
|
+
if (await verifySignature(key, stringToUint8Array(signed_canon), sig) === true) {
|
|
68
|
+
valid_signatures++;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (valid_signatures >= threshold) {
|
|
72
|
+
return true;
|
|
73
|
+
} else {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
__name(checkSignatures, "checkSignatures");
|
|
78
|
+
|
|
79
|
+
// node_modules/@freedomofpress/tuf-browser/dist/storage/encoding.js
|
|
80
|
+
function isRawBytesWrapper(value) {
|
|
81
|
+
return value != null && typeof value === "object" && "__raw_bytes__" in value && // eslint-disable-next-line
|
|
82
|
+
typeof value.__raw_bytes__ === "string";
|
|
83
|
+
}
|
|
84
|
+
__name(isRawBytesWrapper, "isRawBytesWrapper");
|
|
85
|
+
function decodeRawBytesWrapper(wrapper) {
|
|
86
|
+
const bytes = base64ToUint8Array(wrapper.__raw_bytes__);
|
|
87
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
88
|
+
}
|
|
89
|
+
__name(decodeRawBytesWrapper, "decodeRawBytesWrapper");
|
|
90
|
+
function createRawBytesWrapper(value) {
|
|
91
|
+
return { __raw_bytes__: Uint8ArrayToBase64(value) };
|
|
92
|
+
}
|
|
93
|
+
__name(createRawBytesWrapper, "createRawBytesWrapper");
|
|
94
|
+
|
|
95
|
+
// node_modules/@freedomofpress/tuf-browser/dist/storage/browser.js
|
|
96
|
+
var ExtensionStorageBackend = class {
|
|
97
|
+
static {
|
|
98
|
+
__name(this, "ExtensionStorageBackend");
|
|
99
|
+
}
|
|
100
|
+
async read(key) {
|
|
101
|
+
const result = await browser.storage.local.get(key);
|
|
102
|
+
const value = result[key];
|
|
103
|
+
if (isRawBytesWrapper(value)) {
|
|
104
|
+
return decodeRawBytesWrapper(value);
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
async write(key, value) {
|
|
109
|
+
await browser.storage.local.set({ [key]: value });
|
|
110
|
+
}
|
|
111
|
+
async writeRaw(key, value) {
|
|
112
|
+
await browser.storage.local.set({ [key]: createRawBytesWrapper(value) });
|
|
113
|
+
}
|
|
114
|
+
async delete(key) {
|
|
115
|
+
await browser.storage.local.remove(key);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// node_modules/@freedomofpress/tuf-browser/dist/storage/localstorage.js
|
|
120
|
+
var LocalStorageBackend = class {
|
|
121
|
+
static {
|
|
122
|
+
__name(this, "LocalStorageBackend");
|
|
123
|
+
}
|
|
124
|
+
async read(key) {
|
|
125
|
+
const value = localStorage.getItem(key);
|
|
126
|
+
if (value) {
|
|
127
|
+
const parsed = JSON.parse(value);
|
|
128
|
+
if (isRawBytesWrapper(parsed)) {
|
|
129
|
+
return decodeRawBytesWrapper(parsed);
|
|
130
|
+
}
|
|
131
|
+
return parsed;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async write(key, value) {
|
|
135
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
136
|
+
}
|
|
137
|
+
async writeRaw(key, value) {
|
|
138
|
+
localStorage.setItem(key, JSON.stringify(createRawBytesWrapper(value)));
|
|
139
|
+
}
|
|
140
|
+
async delete(key) {
|
|
141
|
+
localStorage.removeItem(key);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// node_modules/@freedomofpress/tuf-browser/dist/storage/memory.js
|
|
146
|
+
var MemoryBackend = class {
|
|
147
|
+
static {
|
|
148
|
+
__name(this, "MemoryBackend");
|
|
149
|
+
}
|
|
150
|
+
constructor() {
|
|
151
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
152
|
+
}
|
|
153
|
+
async read(key) {
|
|
154
|
+
const value = this.cache.get(key);
|
|
155
|
+
if (!value)
|
|
156
|
+
return void 0;
|
|
157
|
+
if (isRawBytesWrapper(value)) {
|
|
158
|
+
return decodeRawBytesWrapper(value);
|
|
159
|
+
}
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
async write(key, value) {
|
|
163
|
+
this.cache.set(key, value);
|
|
164
|
+
}
|
|
165
|
+
async writeRaw(key, value) {
|
|
166
|
+
this.cache.set(key, createRawBytesWrapper(value));
|
|
167
|
+
}
|
|
168
|
+
async delete(key) {
|
|
169
|
+
this.cache.delete(key);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// node_modules/@freedomofpress/tuf-browser/dist/types.js
|
|
174
|
+
var Roles;
|
|
175
|
+
(function(Roles2) {
|
|
176
|
+
Roles2["Root"] = "root";
|
|
177
|
+
Roles2["Timestamp"] = "timestamp";
|
|
178
|
+
Roles2["Snapshot"] = "snapshot";
|
|
179
|
+
Roles2["Targets"] = "targets";
|
|
180
|
+
})(Roles || (Roles = {}));
|
|
181
|
+
var TOP_LEVEL_ROLE_NAMES = [
|
|
182
|
+
Roles.Root,
|
|
183
|
+
Roles.Targets,
|
|
184
|
+
Roles.Snapshot,
|
|
185
|
+
Roles.Timestamp
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
// node_modules/@freedomofpress/tuf-browser/dist/tuf.js
|
|
189
|
+
var TUFClient = class {
|
|
190
|
+
static {
|
|
191
|
+
__name(this, "TUFClient");
|
|
192
|
+
}
|
|
193
|
+
constructor(repositoryUrl, startingRoot, namespace, targetBaseUrl, options) {
|
|
194
|
+
this.repositoryUrl = repositoryUrl;
|
|
195
|
+
this.targetBaseUrl = targetBaseUrl || repositoryUrl;
|
|
196
|
+
this.startingRoot = startingRoot;
|
|
197
|
+
this.namespace = namespace;
|
|
198
|
+
if (options?.backend) {
|
|
199
|
+
this.backend = options.backend;
|
|
200
|
+
} else if (options?.disableCache) {
|
|
201
|
+
this.backend = new MemoryBackend();
|
|
202
|
+
} else if (typeof browser !== "undefined" && browser.storage?.local) {
|
|
203
|
+
this.backend = new ExtensionStorageBackend();
|
|
204
|
+
} else if (typeof localStorage !== "undefined") {
|
|
205
|
+
this.backend = new LocalStorageBackend();
|
|
206
|
+
} else {
|
|
207
|
+
this.backend = new MemoryBackend();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
getCacheKey(key) {
|
|
211
|
+
if (key.startsWith("/") || key.startsWith("./") || key.includes("..") || key.includes("\\")) {
|
|
212
|
+
throw new Error(`key contains an invalid pattern (${key})`);
|
|
213
|
+
}
|
|
214
|
+
return `${this.namespace}/${key}.json`;
|
|
215
|
+
}
|
|
216
|
+
async getFromCache(key) {
|
|
217
|
+
const namespacedKey = this.getCacheKey(key);
|
|
218
|
+
return await this.backend.read(namespacedKey);
|
|
219
|
+
}
|
|
220
|
+
async setInCache(key, value) {
|
|
221
|
+
const namespacedKey = this.getCacheKey(key);
|
|
222
|
+
await this.backend.write(namespacedKey, value);
|
|
223
|
+
}
|
|
224
|
+
async fetchMetafileBase(role, version, target = false) {
|
|
225
|
+
let url;
|
|
226
|
+
role = encodeURIComponent(role);
|
|
227
|
+
if (!target) {
|
|
228
|
+
url = version !== -1 ? `${this.repositoryUrl}${version}.${role}.json` : `${this.repositoryUrl}${role}.json`;
|
|
229
|
+
} else {
|
|
230
|
+
url = `${this.repositoryUrl}${version}.${role}`;
|
|
231
|
+
}
|
|
232
|
+
const response = await fetch(url);
|
|
233
|
+
if (!response.ok) {
|
|
234
|
+
throw new Error(`Failed to fetch file: ${response.status} ${response.statusText}`);
|
|
235
|
+
}
|
|
236
|
+
return response;
|
|
237
|
+
}
|
|
238
|
+
validateMetadata(metadata) {
|
|
239
|
+
const seenKeyIds = /* @__PURE__ */ new Set();
|
|
240
|
+
for (const sig of metadata.signatures) {
|
|
241
|
+
if (seenKeyIds.has(sig.keyid)) {
|
|
242
|
+
throw new Error(`Duplicate signature found for keyid: ${sig.keyid}`);
|
|
243
|
+
}
|
|
244
|
+
seenKeyIds.add(sig.keyid);
|
|
245
|
+
}
|
|
246
|
+
const specVersion = metadata.signed.spec_version;
|
|
247
|
+
if (!specVersion) {
|
|
248
|
+
throw new Error("spec_version is required");
|
|
249
|
+
}
|
|
250
|
+
const parts = specVersion.split(".");
|
|
251
|
+
if (parts.length < 2 || parts.length > 3) {
|
|
252
|
+
throw new Error(`Invalid spec_version format: ${specVersion}`);
|
|
253
|
+
}
|
|
254
|
+
if (!parts.every((p) => /^\d+$/.test(p))) {
|
|
255
|
+
throw new Error(`spec_version parts must be numeric: ${specVersion}`);
|
|
256
|
+
}
|
|
257
|
+
if (parts[0] !== "1") {
|
|
258
|
+
throw new Error(`Unsupported spec_version major version: ${parts[0]} (expected 1)`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async fetchMetafileJson(role, version = -1) {
|
|
262
|
+
const response = await this.fetchMetafileBase(role, version);
|
|
263
|
+
const metadata = await response.json();
|
|
264
|
+
this.validateMetadata(metadata);
|
|
265
|
+
return metadata;
|
|
266
|
+
}
|
|
267
|
+
async fetchMetafileBinary(role, version = -1, target = false) {
|
|
268
|
+
const response = await this.fetchMetafileBase(role, version, target);
|
|
269
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
270
|
+
}
|
|
271
|
+
bootstrapRoot(file) {
|
|
272
|
+
try {
|
|
273
|
+
const metadata = JSON.parse(file);
|
|
274
|
+
this.validateMetadata(metadata);
|
|
275
|
+
return metadata;
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw new Error(`Failed to load the JSON file: ${error}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async verifyHashes(data, hashes, context) {
|
|
281
|
+
if (!hashes)
|
|
282
|
+
return;
|
|
283
|
+
const hashAlgoMap = {
|
|
284
|
+
sha256: HashAlgorithms.SHA256,
|
|
285
|
+
sha384: HashAlgorithms.SHA384,
|
|
286
|
+
sha512: HashAlgorithms.SHA512
|
|
287
|
+
};
|
|
288
|
+
for (const [algo, expectedHash] of Object.entries(hashes)) {
|
|
289
|
+
const cryptoAlgo = hashAlgoMap[algo];
|
|
290
|
+
if (!cryptoAlgo) {
|
|
291
|
+
throw new Error(`${context}: unsupported hash algorithm '${algo}'`);
|
|
292
|
+
}
|
|
293
|
+
const computedHash = Uint8ArrayToHex(new Uint8Array(await crypto.subtle.digest(cryptoAlgo, data)));
|
|
294
|
+
if (expectedHash !== computedHash) {
|
|
295
|
+
throw new Error(`${context}: ${algo} hash mismatch`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// This function supports ECDSA (256, 385, 521), Ed25519 in Hex or PEM format
|
|
300
|
+
// it is possible to support certain cases of RSA, but it is not really useful for now
|
|
301
|
+
// Returns a mapping keyid (hexstring) -> CryptoKey object
|
|
302
|
+
async loadRoot(json, oldroot) {
|
|
303
|
+
if (json.signed._type !== Roles.Root) {
|
|
304
|
+
throw new Error("Loading the wrong metafile as root.");
|
|
305
|
+
}
|
|
306
|
+
let keys;
|
|
307
|
+
let threshold;
|
|
308
|
+
let roleKeys;
|
|
309
|
+
if (oldroot == void 0) {
|
|
310
|
+
keys = await loadKeys(json.signed.keys);
|
|
311
|
+
roleKeys = json.signed.roles.root.keyids;
|
|
312
|
+
threshold = json.signed.roles.root.threshold;
|
|
313
|
+
} else {
|
|
314
|
+
keys = oldroot.keys;
|
|
315
|
+
roleKeys = oldroot.roles["root"].keyids;
|
|
316
|
+
threshold = oldroot.threshold;
|
|
317
|
+
}
|
|
318
|
+
if (await checkSignatures(keys, roleKeys, json.signed, json.signatures, threshold) !== true) {
|
|
319
|
+
throw new Error("Failed to verify metafile.");
|
|
320
|
+
}
|
|
321
|
+
keys = await loadKeys(json.signed.keys);
|
|
322
|
+
if (!Number.isSafeInteger(json.signed.version) || json.signed.version < 1) {
|
|
323
|
+
throw new Error("There is something wrong with the root version number.");
|
|
324
|
+
}
|
|
325
|
+
for (const role of TOP_LEVEL_ROLE_NAMES) {
|
|
326
|
+
if (!json.signed.roles[role]) {
|
|
327
|
+
throw new Error(`Missing required top-level role: ${role}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
for (const [roleName, role] of Object.entries(json.signed.roles)) {
|
|
331
|
+
const keyidSet = new Set(role.keyids);
|
|
332
|
+
if (keyidSet.size !== role.keyids.length) {
|
|
333
|
+
throw new Error(`Duplicate key IDs found in role: ${roleName}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
keys,
|
|
338
|
+
version: json.signed.version,
|
|
339
|
+
expires: new Date(json.signed.expires),
|
|
340
|
+
threshold: json.signed.roles.root.threshold,
|
|
341
|
+
consistent_snapshot: json.signed.consistent_snapshot,
|
|
342
|
+
roles: json.signed.roles
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
async updateRoot(frozenTimestamp) {
|
|
346
|
+
let rootJson = await this.getFromCache(Roles.Root);
|
|
347
|
+
if (!rootJson) {
|
|
348
|
+
rootJson = await this.bootstrapRoot(this.startingRoot);
|
|
349
|
+
}
|
|
350
|
+
let root = await this.loadRoot(rootJson);
|
|
351
|
+
const oldRoot = root;
|
|
352
|
+
let newroot;
|
|
353
|
+
let newrootJson;
|
|
354
|
+
for (let new_version = root.version + 1; new_version < Number.MAX_SAFE_INTEGER; new_version++) {
|
|
355
|
+
try {
|
|
356
|
+
newrootJson = await this.fetchMetafileJson(Roles.Root, new_version);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
if (e instanceof Error && e.message.includes("Failed to fetch")) {
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
throw e;
|
|
362
|
+
}
|
|
363
|
+
if (newrootJson.signed.version !== new_version) {
|
|
364
|
+
throw new Error(`Version mismatch: URL version ${new_version} but file contains version ${newrootJson.signed.version}`);
|
|
365
|
+
}
|
|
366
|
+
if (newrootJson.signed?._type !== Roles.Root) {
|
|
367
|
+
throw new Error("Incorrect metadata type for root.");
|
|
368
|
+
}
|
|
369
|
+
newroot = await this.loadRoot(newrootJson, root);
|
|
370
|
+
if (newroot.version !== root.version + 1) {
|
|
371
|
+
throw new Error(`Root version must be exactly ${root.version + 1}, got ${newroot.version}. Probable rollback attack.`);
|
|
372
|
+
}
|
|
373
|
+
newroot = await this.loadRoot(newrootJson);
|
|
374
|
+
root = newroot;
|
|
375
|
+
await this.setInCache(Roles.Root, newrootJson);
|
|
376
|
+
}
|
|
377
|
+
if (root.expires <= frozenTimestamp) {
|
|
378
|
+
throw new Error("Freeze attack on the root metafile.");
|
|
379
|
+
}
|
|
380
|
+
if (root.version > oldRoot.version) {
|
|
381
|
+
const timestampKeysChanged = JSON.stringify(root.roles.timestamp.keyids.sort()) !== JSON.stringify(oldRoot.roles.timestamp.keyids.sort());
|
|
382
|
+
const snapshotKeysChanged = JSON.stringify(root.roles.snapshot.keyids.sort()) !== JSON.stringify(oldRoot.roles.snapshot.keyids.sort());
|
|
383
|
+
const targetsKeysChanged = JSON.stringify(root.roles.targets.keyids.sort()) !== JSON.stringify(oldRoot.roles.targets.keyids.sort());
|
|
384
|
+
if (timestampKeysChanged) {
|
|
385
|
+
await this.backend.delete(this.getCacheKey(Roles.Timestamp));
|
|
386
|
+
await this.backend.delete(this.getCacheKey(Roles.Snapshot));
|
|
387
|
+
await this.backend.delete(this.getCacheKey(Roles.Targets));
|
|
388
|
+
}
|
|
389
|
+
if (snapshotKeysChanged) {
|
|
390
|
+
await this.backend.delete(this.getCacheKey(Roles.Snapshot));
|
|
391
|
+
await this.backend.delete(this.getCacheKey(Roles.Targets));
|
|
392
|
+
}
|
|
393
|
+
if (targetsKeysChanged) {
|
|
394
|
+
await this.backend.delete(this.getCacheKey(Roles.Targets));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return root;
|
|
398
|
+
}
|
|
399
|
+
async updateTimestamp(root, frozenTimestamp) {
|
|
400
|
+
const keys = getRoleKeys(root.keys, root.roles.timestamp.keyids);
|
|
401
|
+
if (keys.size < 1) {
|
|
402
|
+
throw new Error("No valid keys found for the timestamp role.");
|
|
403
|
+
}
|
|
404
|
+
const cachedTimestamp = await this.getFromCache(Roles.Timestamp);
|
|
405
|
+
const newTimestampRaw = await this.fetchMetafileBinary(Roles.Timestamp, -1);
|
|
406
|
+
const newTimestamp = JSON.parse(Uint8ArrayToString(newTimestampRaw));
|
|
407
|
+
this.validateMetadata(newTimestamp);
|
|
408
|
+
if (newTimestamp.signed._type !== Roles.Timestamp) {
|
|
409
|
+
throw new Error(`Invalid metadata type: expected ${Roles.Timestamp}, got ${newTimestamp.signed._type}`);
|
|
410
|
+
}
|
|
411
|
+
if (!newTimestamp.signed.meta || !newTimestamp.signed.meta["snapshot.json"]) {
|
|
412
|
+
throw new Error("Timestamp metadata missing required meta['snapshot.json']");
|
|
413
|
+
}
|
|
414
|
+
if (await checkSignatures(keys, root.roles["timestamp"].keyids, newTimestamp.signed, newTimestamp.signatures, root.roles.timestamp.threshold) !== true) {
|
|
415
|
+
throw new Error("Failed verifying timestamp role signature(s).");
|
|
416
|
+
}
|
|
417
|
+
if (cachedTimestamp !== void 0) {
|
|
418
|
+
if (newTimestamp.signed.version < cachedTimestamp.signed.version) {
|
|
419
|
+
throw new Error("New timestamp file has a lower version that the currently cached one.");
|
|
420
|
+
}
|
|
421
|
+
if (newTimestamp.signed.version == cachedTimestamp.signed.version) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
if (newTimestamp.signed.meta["snapshot.json"].version < cachedTimestamp.signed.meta["snapshot.json"].version) {
|
|
425
|
+
throw new Error("Timestamp has been updated, but snapshot version has been rolled back.");
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (new Date(newTimestamp.signed.expires) <= frozenTimestamp) {
|
|
429
|
+
throw new Error("Freeze attack on the timestamp metafile.");
|
|
430
|
+
}
|
|
431
|
+
await this.backend.writeRaw(this.getCacheKey(Roles.Timestamp), newTimestampRaw);
|
|
432
|
+
return newTimestamp;
|
|
433
|
+
}
|
|
434
|
+
async updateSnapshot(root, frozenTimestamp, timestampMeta) {
|
|
435
|
+
const version = timestampMeta.signed.meta["snapshot.json"].version;
|
|
436
|
+
const keys = getRoleKeys(root.keys, root.roles.snapshot.keyids);
|
|
437
|
+
const cachedSnapshot = await this.getFromCache(Roles.Snapshot);
|
|
438
|
+
let newSnapshotRaw;
|
|
439
|
+
if (root.consistent_snapshot) {
|
|
440
|
+
newSnapshotRaw = await this.fetchMetafileBinary(Roles.Snapshot, version);
|
|
441
|
+
} else {
|
|
442
|
+
newSnapshotRaw = await this.fetchMetafileBinary(Roles.Snapshot, -1);
|
|
443
|
+
}
|
|
444
|
+
const snapshotMeta = timestampMeta.signed.meta["snapshot.json"];
|
|
445
|
+
if (snapshotMeta.length !== void 0) {
|
|
446
|
+
if (newSnapshotRaw.length !== snapshotMeta.length) {
|
|
447
|
+
throw new Error(`Snapshot length mismatch: expected ${snapshotMeta.length}, got ${newSnapshotRaw.length}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
await this.verifyHashes(newSnapshotRaw, snapshotMeta.hashes, "Snapshot");
|
|
451
|
+
const newSnapshot = JSON.parse(Uint8ArrayToString(newSnapshotRaw));
|
|
452
|
+
this.validateMetadata(newSnapshot);
|
|
453
|
+
if (newSnapshot.signed._type !== Roles.Snapshot) {
|
|
454
|
+
throw new Error(`Invalid metadata type: expected ${Roles.Snapshot}, got ${newSnapshot.signed._type}`);
|
|
455
|
+
}
|
|
456
|
+
if (!newSnapshot.signed.meta || !newSnapshot.signed.meta["targets.json"]) {
|
|
457
|
+
throw new Error("Snapshot metadata missing required meta['targets.json']");
|
|
458
|
+
}
|
|
459
|
+
if (await checkSignatures(keys, root.roles["snapshot"].keyids, newSnapshot.signed, newSnapshot.signatures, root.roles.snapshot.threshold) !== true) {
|
|
460
|
+
throw new Error("Failed verifying snapshot role signature(s).");
|
|
461
|
+
}
|
|
462
|
+
if (newSnapshot.signed.version !== version) {
|
|
463
|
+
throw new Error(`Snapshot version mismatch: URL version ${version} but file contains version ${newSnapshot.signed.version}`);
|
|
464
|
+
}
|
|
465
|
+
if (cachedSnapshot !== void 0) {
|
|
466
|
+
for (const [target] of Object.entries(cachedSnapshot.signed.meta)) {
|
|
467
|
+
if (target in newSnapshot.signed.meta !== true) {
|
|
468
|
+
throw new Error("Target that was listed in an older snapshot was dropped in a newer one.");
|
|
469
|
+
}
|
|
470
|
+
if (newSnapshot.signed.meta[target].version < cachedSnapshot.signed.meta[target].version) {
|
|
471
|
+
throw new Error("Target version in newer snapshot is lower than the cached one. Probable rollback attack.");
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (new Date(newSnapshot.signed.expires) <= frozenTimestamp) {
|
|
476
|
+
throw new Error("Freeze attack on the snapshot metafile.");
|
|
477
|
+
}
|
|
478
|
+
await this.backend.writeRaw(this.getCacheKey(Roles.Snapshot), newSnapshotRaw);
|
|
479
|
+
return newSnapshot.signed.meta;
|
|
480
|
+
}
|
|
481
|
+
async updateTargets(root, frozenTimestamp, snapshot) {
|
|
482
|
+
const keys = getRoleKeys(root.keys, root.roles.targets.keyids);
|
|
483
|
+
const cachedTargets = await this.getFromCache(Roles.Targets);
|
|
484
|
+
let newTargetsRaw;
|
|
485
|
+
if (root.consistent_snapshot) {
|
|
486
|
+
newTargetsRaw = await this.fetchMetafileBinary(Roles.Targets, snapshot[`${Roles.Targets}.json`].version);
|
|
487
|
+
} else {
|
|
488
|
+
newTargetsRaw = await this.fetchMetafileBinary(Roles.Targets, -1);
|
|
489
|
+
}
|
|
490
|
+
const targetsMeta = snapshot[`${Roles.Targets}.json`];
|
|
491
|
+
if (targetsMeta.length !== void 0) {
|
|
492
|
+
if (newTargetsRaw.length !== targetsMeta.length) {
|
|
493
|
+
throw new Error(`Targets length mismatch: expected ${targetsMeta.length}, got ${newTargetsRaw.length}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
await this.verifyHashes(newTargetsRaw, targetsMeta.hashes, "Targets");
|
|
497
|
+
const newTargets = JSON.parse(Uint8ArrayToString(newTargetsRaw));
|
|
498
|
+
this.validateMetadata(newTargets);
|
|
499
|
+
if (newTargets.signed._type !== Roles.Targets) {
|
|
500
|
+
throw new Error(`Invalid metadata type: expected ${Roles.Targets}, got ${newTargets.signed._type}`);
|
|
501
|
+
}
|
|
502
|
+
if (await checkSignatures(keys, root.roles["targets"].keyids, newTargets.signed, newTargets.signatures, root.roles.targets.threshold) !== true) {
|
|
503
|
+
throw new Error(`Failed verifying targets role.`);
|
|
504
|
+
}
|
|
505
|
+
const expectedVersion = snapshot[`${Roles.Targets}.json`].version;
|
|
506
|
+
if (newTargets.signed.version !== expectedVersion) {
|
|
507
|
+
throw new Error(`Targets version mismatch: URL version ${expectedVersion} but file contains version ${newTargets.signed.version}`);
|
|
508
|
+
}
|
|
509
|
+
if (cachedTargets !== void 0 && newTargets.signed.version < cachedTargets.signed.version) {
|
|
510
|
+
throw new Error("Targets version is lower than the cached one. Probable rollback attack.");
|
|
511
|
+
}
|
|
512
|
+
if (new Date(newTargets.signed.expires) <= frozenTimestamp) {
|
|
513
|
+
throw new Error("Freeze attack on the targets metafile.");
|
|
514
|
+
}
|
|
515
|
+
await this.backend.writeRaw(this.getCacheKey(Roles.Targets), newTargetsRaw);
|
|
516
|
+
}
|
|
517
|
+
async listSignedTargets() {
|
|
518
|
+
const cachedTargets = await this.getFromCache(Roles.Targets);
|
|
519
|
+
const filenames = [];
|
|
520
|
+
if (cachedTargets) {
|
|
521
|
+
for (const filename of Object.keys(cachedTargets.signed.targets)) {
|
|
522
|
+
filenames.push(filename);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return filenames;
|
|
526
|
+
}
|
|
527
|
+
async fetchTarget(name) {
|
|
528
|
+
const cachedTargets = await this.getFromCache(Roles.Targets);
|
|
529
|
+
if (cachedTargets === void 0) {
|
|
530
|
+
throw new Error("Failed to find the targets metafile when it should have existed.");
|
|
531
|
+
}
|
|
532
|
+
if (!(name in cachedTargets.signed.targets)) {
|
|
533
|
+
throw new Error(`${name} not present in the targets role.`);
|
|
534
|
+
}
|
|
535
|
+
const targetInfo = cachedTargets.signed.targets[name];
|
|
536
|
+
const targetHashes = targetInfo.hashes;
|
|
537
|
+
let hashForUrl;
|
|
538
|
+
if (targetHashes.sha256) {
|
|
539
|
+
hashForUrl = targetHashes.sha256;
|
|
540
|
+
} else if (targetHashes.sha512) {
|
|
541
|
+
hashForUrl = targetHashes.sha512;
|
|
542
|
+
} else {
|
|
543
|
+
throw new Error(`No supported hash algorithm found for ${name}. Available: ${Object.keys(targetHashes).join(", ")}`);
|
|
544
|
+
}
|
|
545
|
+
const lastSlash = name.lastIndexOf("/");
|
|
546
|
+
const targetUrl = lastSlash === -1 ? `${this.targetBaseUrl}${hashForUrl}.${name}` : `${this.targetBaseUrl}${name.substring(0, lastSlash + 1)}${hashForUrl}.${name.substring(lastSlash + 1)}`;
|
|
547
|
+
const response = await fetch(targetUrl);
|
|
548
|
+
if (!response.ok) {
|
|
549
|
+
throw new Error(`Failed to fetch target: ${response.status} ${response.statusText}`);
|
|
550
|
+
}
|
|
551
|
+
const raw_file = new Uint8Array(await response.arrayBuffer());
|
|
552
|
+
if (raw_file.byteLength !== targetInfo.length) {
|
|
553
|
+
throw new Error(`${name} length mismatch: expected ${targetInfo.length}, got ${raw_file.byteLength}`);
|
|
554
|
+
}
|
|
555
|
+
await this.verifyHashes(raw_file, targetHashes, `Target '${name}'`);
|
|
556
|
+
return raw_file.buffer;
|
|
557
|
+
}
|
|
558
|
+
async updateTUF() {
|
|
559
|
+
const frozenTimestamp = /* @__PURE__ */ new Date();
|
|
560
|
+
const root = await this.updateRoot(frozenTimestamp);
|
|
561
|
+
const timestampMeta = await this.updateTimestamp(root, frozenTimestamp);
|
|
562
|
+
if (timestampMeta === null) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const snapshot = await this.updateSnapshot(root, frozenTimestamp, timestampMeta);
|
|
566
|
+
await this.updateTargets(root, frozenTimestamp, snapshot);
|
|
567
|
+
}
|
|
568
|
+
async getTarget(name) {
|
|
569
|
+
return await this.fetchTarget(name);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
export {
|
|
573
|
+
TUFClient
|
|
574
|
+
};
|