@rozoai/checkout 0.1.7 → 0.1.10
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 +7 -2
- package/docs/QUICKSTART.es.md +2 -0
- package/docs/QUICKSTART.ja.md +2 -0
- package/docs/QUICKSTART.md +2 -0
- package/docs/QUICKSTART.zh.md +2 -0
- package/docs/README.es.md +2 -0
- package/docs/README.ja.md +2 -0
- package/docs/README.zh.md +2 -0
- package/docs/security-audit-notes.md +30 -0
- package/package.json +1 -1
- package/scripts/dist/cli.js +149 -70
- package/scripts/dist/send-evm.js +155 -76
- package/scripts/dist/send-sol.js +155 -76
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
33
33
|
| Base | `usdc-base` | `8453` | 6 decimals |
|
|
34
34
|
| Solana | `usdt-solana` `usdc-solana` | `900` | SPL; native SOL not supported |
|
|
35
35
|
| Stellar | `usdc-stellar` | `1500` | `MEMO_TEXT` memo required — shown in the deposit block |
|
|
36
|
-
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11; amounts in satoshis |
|
|
36
|
+
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11; amounts in satoshis. Any wallet that can pay an invoice works, including Cashu/ecash wallets — the mint melts your ecash into the Lightning payment. |
|
|
37
37
|
|
|
38
38
|
Native gas coins (SOL, BNB, ETH, MATIC) and on-chain BTC are not accepted.
|
|
39
39
|
|
|
@@ -52,7 +52,10 @@ already hold and pay from wherever it already lives.
|
|
|
52
52
|
address plus `receiverMemo`, so whatever you send from — exchange or wallet —
|
|
53
53
|
must let you set a memo. Omit it and the payment is lost.
|
|
54
54
|
- **Lightning pays an invoice, not an address.** Scan or paste
|
|
55
|
-
`deposit.lnInvoice`; there is no address to send to.
|
|
55
|
+
`deposit.lnInvoice`; there is no address to send to. A Cashu/ecash wallet
|
|
56
|
+
works here too — paying the invoice melts the ecash on your behalf, and this
|
|
57
|
+
route settles against ROZO's own Lightning node rather than a third-party
|
|
58
|
+
swap, so no special handling is needed on our side.
|
|
56
59
|
- **Only `--send` needs a private key**, read from `ROZO_CHECKOUT_EVM_KEY` or
|
|
57
60
|
`ROZO_CHECKOUT_SOL_KEY`, and it covers EVM chains and Solana only. Everything
|
|
58
61
|
else is keyless.
|
|
@@ -70,6 +73,8 @@ environment (or a gitignored `.env`) still works for unattended automation.
|
|
|
70
73
|
<details>
|
|
71
74
|
<summary><b>Set up a local wallet for <code>--send</code></b> — .env template and per-wallet export steps</summary>
|
|
72
75
|
|
|
76
|
+
You very likely do not need any of this: the default path needs no key, and for Stellar the `stellar-agent-wallet` skill sends with its own key handling — this `.env` setup is only for unattended EVM/Solana automation with a dedicated low-balance hot wallet.
|
|
77
|
+
|
|
73
78
|
**None of this is needed for the default path.** Paying from your own wallet
|
|
74
79
|
needs no key and no configuration, and works with wallets that can never be
|
|
75
80
|
used here — including hardware wallets and exchange accounts.
|
package/docs/QUICKSTART.es.md
CHANGED
|
@@ -224,6 +224,8 @@ Un archivo de claves o un `.env` no debe ser legible por otros usuarios
|
|
|
224
224
|
<details>
|
|
225
225
|
<summary><b>Configurar una billetera local para <code>--send</code></b> — plantilla .env y pasos de exportación por billetera</summary>
|
|
226
226
|
|
|
227
|
+
Muy probablemente no necesites nada de esto: la ruta por defecto no requiere ninguna clave, y para Stellar la skill `stellar-agent-wallet` envía con su propio manejo de claves — esta configuración `.env` es solo para automatización desatendida en EVM/Solana con una billetera caliente dedicada de saldo bajo.
|
|
228
|
+
|
|
227
229
|
**Nada de esto hace falta para la ruta por defecto.** Pagar desde la propia
|
|
228
230
|
billetera no necesita clave ni configuración, y funciona con billeteras que aquí
|
|
229
231
|
nunca se pueden usar, incluidas las de hardware y las cuentas de exchange.
|
package/docs/QUICKSTART.ja.md
CHANGED
|
@@ -202,6 +202,8 @@ ROZO_CHECKOUT_EVM_KEYSTORE=~/wallets/hot.json \
|
|
|
202
202
|
<details>
|
|
203
203
|
<summary><b><code>--send</code> 用のローカルウォレット設定</b> — .env テンプレートとウォレット別の書き出し手順</summary>
|
|
204
204
|
|
|
205
|
+
おそらくこれらは一切不要です。既定の経路に鍵は不要で、Stellar なら `stellar-agent-wallet` スキルが独自の鍵管理で送金します — この `.env` 設定は、残高を低く抑えた専用ホットウォレットによる EVM/Solana の無人自動化専用です。
|
|
206
|
+
|
|
205
207
|
**既定の経路ではここに書かれたものは一切不要です。** 自分のウォレットから支払う場合、
|
|
206
208
|
鍵も設定も要りません。しかもここでは決して使えないウォレット — ハードウェアウォレットや
|
|
207
209
|
取引所アカウントを含む — でも支払えます。
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -217,6 +217,8 @@ not be tracked by git. Both are refused rather than warned about.
|
|
|
217
217
|
<details>
|
|
218
218
|
<summary><b>Set up a local wallet for <code>--send</code></b> — .env template and per-wallet export steps</summary>
|
|
219
219
|
|
|
220
|
+
You very likely do not need any of this: the default path needs no key, and for Stellar the `stellar-agent-wallet` skill sends with its own key handling — this `.env` setup is only for unattended EVM/Solana automation with a dedicated low-balance hot wallet.
|
|
221
|
+
|
|
220
222
|
**None of this is needed for the default path.** Paying from your own wallet
|
|
221
223
|
needs no key and no configuration, and works with wallets that can never be
|
|
222
224
|
used here — including hardware wallets and exchange accounts.
|
package/docs/QUICKSTART.zh.md
CHANGED
|
@@ -185,6 +185,8 @@ ROZO_CHECKOUT_EVM_KEYSTORE=~/wallets/hot.json \
|
|
|
185
185
|
<details>
|
|
186
186
|
<summary><b>为 <code>--send</code> 配置本地钱包</b> —— .env 模板与各钱包导出步骤</summary>
|
|
187
187
|
|
|
188
|
+
你很可能完全不需要这些:默认路径不需要任何私钥;Stellar 来源可用 `stellar-agent-wallet` skill,它有自己的密钥管理来完成发送 —— 这份 `.env` 配置只用于无人值守的 EVM/Solana 自动化,且应使用余额很低的专用热钱包。
|
|
189
|
+
|
|
188
190
|
**默认路径不需要这里的任何东西。** 用你自己的钱包付款既不需要私钥也不需要任何配置,
|
|
189
191
|
而且支持那些在这里根本用不了的钱包 —— 包括硬件钱包和交易所账户。
|
|
190
192
|
|
package/docs/README.es.md
CHANGED
|
@@ -239,6 +239,8 @@ Billetera: cualquier billetera, **sin clave**. Solo `--send` la necesita: firma
|
|
|
239
239
|
<details>
|
|
240
240
|
<summary><b>Configurar una billetera local para <code>--send</code></b> — plantilla .env y pasos de exportación por billetera</summary>
|
|
241
241
|
|
|
242
|
+
Muy probablemente no necesites nada de esto: la ruta por defecto no requiere ninguna clave, y para Stellar la skill `stellar-agent-wallet` envía con su propio manejo de claves — esta configuración `.env` es solo para automatización desatendida en EVM/Solana con una billetera caliente dedicada de saldo bajo.
|
|
243
|
+
|
|
242
244
|
**Nada de esto hace falta para la ruta por defecto.** Pagar desde la propia
|
|
243
245
|
billetera no necesita clave ni configuración, y funciona con billeteras que aquí
|
|
244
246
|
nunca se pueden usar, incluidas las de hardware y las cuentas de exchange.
|
package/docs/README.ja.md
CHANGED
|
@@ -232,6 +232,8 @@ https://checkout.rozo.ai/llms.txt を自分のコンテキストに読み込み
|
|
|
232
232
|
<details>
|
|
233
233
|
<summary><b><code>--send</code> 用のローカルウォレット設定</b> — .env テンプレートとウォレット別の書き出し手順</summary>
|
|
234
234
|
|
|
235
|
+
おそらくこれらは一切不要です。既定の経路に鍵は不要で、Stellar なら `stellar-agent-wallet` スキルが独自の鍵管理で送金します — この `.env` 設定は、残高を低く抑えた専用ホットウォレットによる EVM/Solana の無人自動化専用です。
|
|
236
|
+
|
|
235
237
|
**既定の経路ではここに書かれたものは一切不要です。** 自分のウォレットから支払う場合、
|
|
236
238
|
鍵も設定も要りません。しかもここでは決して使えないウォレット — ハードウェアウォレットや
|
|
237
239
|
取引所アカウントを含む — でも支払えます。
|
package/docs/README.zh.md
CHANGED
|
@@ -214,6 +214,8 @@ node scripts/dist/status.js --rozo-payment-id <uuid> --watch
|
|
|
214
214
|
<details>
|
|
215
215
|
<summary><b>为 <code>--send</code> 配置本地钱包</b> —— .env 模板与各钱包导出步骤</summary>
|
|
216
216
|
|
|
217
|
+
你很可能完全不需要这些:默认路径不需要任何私钥;Stellar 来源可用 `stellar-agent-wallet` skill,它有自己的密钥管理来完成发送 —— 这份 `.env` 配置只用于无人值守的 EVM/Solana 自动化,且应使用余额很低的专用热钱包。
|
|
218
|
+
|
|
217
219
|
**默认路径不需要这里的任何东西。** 用你自己的钱包付款既不需要私钥也不需要任何配置,
|
|
218
220
|
而且支持那些在这里根本用不了的钱包 —— 包括硬件钱包和交易所账户。
|
|
219
221
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Security-audit notes for scanners and reviewers
|
|
2
|
+
|
|
3
|
+
What automated scans flag in this skill, and why each pattern is present.
|
|
4
|
+
|
|
5
|
+
## `env_credential_access` — environment variable read + network send
|
|
6
|
+
|
|
7
|
+
Real and by design, in exactly two places: `send-evm.js` reads
|
|
8
|
+
`ROZO_CHECKOUT_EVM_KEY` (or an encrypted keystore) and `send-sol.js` reads
|
|
9
|
+
`ROZO_CHECKOUT_SOL_KEY`, to sign a transfer the user has already confirmed
|
|
10
|
+
twice (`--confirm` on the order, `--send` on the send). That is the entire
|
|
11
|
+
optional hot-wallet mode ("Mode B" in SKILL.md); the default mode never
|
|
12
|
+
touches a key. Mitigations, all enforced in code, not prose:
|
|
13
|
+
|
|
14
|
+
- keys come from the environment or a named file only — never argv, never
|
|
15
|
+
printed, scrubbed from every output object (`forget()` in `lib/keys.mjs`);
|
|
16
|
+
- a `.env` is only read from the working directory, this tool's own
|
|
17
|
+
`~/.rozo-checkout/.env`, or a path the user names with `--env-file`; a
|
|
18
|
+
generic `~/.env` is never read;
|
|
19
|
+
- sends refuse if any `.env` here is git-tracked, refuse over the $1,100
|
|
20
|
+
per-payment cap, re-verify chain id and token decimals against the RPC, and
|
|
21
|
+
record the send locally before broadcast so the same order cannot pay twice.
|
|
22
|
+
|
|
23
|
+
## `dangerous_exec` — subprocess execution
|
|
24
|
+
|
|
25
|
+
Removed in 0.1.9. Earlier versions shelled out to `git rev-parse` /
|
|
26
|
+
`git ls-files` for one purpose: refusing to use hot-wallet keys from a
|
|
27
|
+
directory whose `.env` is git-tracked. The check now reads `.git/index`
|
|
28
|
+
directly (`lib/keys.mjs`), so the published bundle contains no
|
|
29
|
+
`child_process` usage at all. The fail-closed contract is unchanged — an
|
|
30
|
+
unparseable index refuses rather than assumes.
|
package/package.json
CHANGED
package/scripts/dist/cli.js
CHANGED
|
@@ -21270,10 +21270,10 @@ var require_dist = __commonJS({
|
|
|
21270
21270
|
});
|
|
21271
21271
|
|
|
21272
21272
|
// node_modules/uuid/dist/esm-node/rng.js
|
|
21273
|
-
import
|
|
21273
|
+
import crypto7 from "crypto";
|
|
21274
21274
|
function rng() {
|
|
21275
21275
|
if (poolPtr > rnds8Pool.length - 16) {
|
|
21276
|
-
|
|
21276
|
+
crypto7.randomFillSync(rnds8Pool);
|
|
21277
21277
|
poolPtr = 0;
|
|
21278
21278
|
}
|
|
21279
21279
|
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
@@ -21473,14 +21473,14 @@ var init_v35 = __esm({
|
|
|
21473
21473
|
});
|
|
21474
21474
|
|
|
21475
21475
|
// node_modules/uuid/dist/esm-node/md5.js
|
|
21476
|
-
import
|
|
21476
|
+
import crypto8 from "crypto";
|
|
21477
21477
|
function md5(bytes) {
|
|
21478
21478
|
if (Array.isArray(bytes)) {
|
|
21479
21479
|
bytes = Buffer.from(bytes);
|
|
21480
21480
|
} else if (typeof bytes === "string") {
|
|
21481
21481
|
bytes = Buffer.from(bytes, "utf8");
|
|
21482
21482
|
}
|
|
21483
|
-
return
|
|
21483
|
+
return crypto8.createHash("md5").update(bytes).digest();
|
|
21484
21484
|
}
|
|
21485
21485
|
var md5_default;
|
|
21486
21486
|
var init_md5 = __esm({
|
|
@@ -21525,14 +21525,14 @@ var init_v4 = __esm({
|
|
|
21525
21525
|
});
|
|
21526
21526
|
|
|
21527
21527
|
// node_modules/uuid/dist/esm-node/sha1.js
|
|
21528
|
-
import
|
|
21528
|
+
import crypto9 from "crypto";
|
|
21529
21529
|
function sha1(bytes) {
|
|
21530
21530
|
if (Array.isArray(bytes)) {
|
|
21531
21531
|
bytes = Buffer.from(bytes);
|
|
21532
21532
|
} else if (typeof bytes === "string") {
|
|
21533
21533
|
bytes = Buffer.from(bytes, "utf8");
|
|
21534
21534
|
}
|
|
21535
|
-
return
|
|
21535
|
+
return crypto9.createHash("sha1").update(bytes).digest();
|
|
21536
21536
|
}
|
|
21537
21537
|
var sha1_default;
|
|
21538
21538
|
var init_sha1 = __esm({
|
|
@@ -42729,7 +42729,7 @@ function checkExpiry({
|
|
|
42729
42729
|
import fs6 from "node:fs";
|
|
42730
42730
|
import path6 from "node:path";
|
|
42731
42731
|
import os4 from "node:os";
|
|
42732
|
-
import
|
|
42732
|
+
import crypto6 from "node:crypto";
|
|
42733
42733
|
|
|
42734
42734
|
// node_modules/viem/_esm/utils/getAction.js
|
|
42735
42735
|
function getAction(client, actionFn, name) {
|
|
@@ -53697,43 +53697,148 @@ init_keccak256();
|
|
|
53697
53697
|
// scripts/src/lib/keys.mjs
|
|
53698
53698
|
import fs4 from "node:fs";
|
|
53699
53699
|
import path4 from "node:path";
|
|
53700
|
-
import
|
|
53701
|
-
|
|
53702
|
-
|
|
53703
|
-
|
|
53700
|
+
import crypto5 from "node:crypto";
|
|
53701
|
+
function findGitRepo(dir) {
|
|
53702
|
+
for (const v of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) {
|
|
53703
|
+
if (process.env[v]) {
|
|
53704
|
+
throw new SkillError(
|
|
53705
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53706
|
+
`${v} is set, so the repository layout cannot be resolved by reading the filesystem alone. Unset it (or run from a plain checkout) before using hot-wallet keys.`
|
|
53707
|
+
);
|
|
53708
|
+
}
|
|
53709
|
+
}
|
|
53710
|
+
let cur = path4.resolve(dir);
|
|
53711
|
+
for (; ; ) {
|
|
53712
|
+
const dotGit = path4.join(cur, ".git");
|
|
53713
|
+
let st = null;
|
|
53714
|
+
try {
|
|
53715
|
+
st = fs4.statSync(dotGit);
|
|
53716
|
+
} catch (err) {
|
|
53717
|
+
if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
|
|
53718
|
+
throw new SkillError(
|
|
53719
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53720
|
+
`A .git entry here could not be inspected (${err?.code || "error"}), so tracked status cannot be proved. Refusing.`
|
|
53721
|
+
);
|
|
53722
|
+
}
|
|
53723
|
+
st = null;
|
|
53724
|
+
}
|
|
53725
|
+
if (st) {
|
|
53726
|
+
if (st.isDirectory()) return { root: cur, gitDir: dotGit };
|
|
53727
|
+
let text;
|
|
53728
|
+
try {
|
|
53729
|
+
text = fs4.readFileSync(dotGit, "utf8");
|
|
53730
|
+
} catch {
|
|
53731
|
+
throw new SkillError(
|
|
53732
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53733
|
+
"A .git entry exists here but could not be read, so tracked status cannot be proved. Refusing."
|
|
53734
|
+
);
|
|
53735
|
+
}
|
|
53736
|
+
const m = /^gitdir:\s*(.+)\s*$/m.exec(text);
|
|
53737
|
+
if (!m) {
|
|
53738
|
+
throw new SkillError(
|
|
53739
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53740
|
+
"A .git file exists here but is not a recognised gitdir pointer. Refusing."
|
|
53741
|
+
);
|
|
53742
|
+
}
|
|
53743
|
+
return { root: cur, gitDir: path4.resolve(cur, m[1]) };
|
|
53744
|
+
}
|
|
53745
|
+
const parent = path4.dirname(cur);
|
|
53746
|
+
if (parent === cur) return null;
|
|
53747
|
+
cur = parent;
|
|
53748
|
+
}
|
|
53749
|
+
}
|
|
53750
|
+
function trackedPathsFromIndex(buf, { hashLen = 20 } = {}) {
|
|
53751
|
+
const fail2 = (why) => new SkillError(
|
|
53752
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53753
|
+
`The git index could not be interpreted (${why}), so tracked status cannot be proved. Refusing.`
|
|
53754
|
+
);
|
|
53755
|
+
if (buf.length < 12 + hashLen || buf.toString("latin1", 0, 4) !== "DIRC") throw fail2("bad header");
|
|
53756
|
+
const algo = hashLen === 32 ? "sha256" : "sha1";
|
|
53757
|
+
const expected = buf.subarray(buf.length - hashLen);
|
|
53758
|
+
const actual = crypto5.createHash(algo).update(buf.subarray(0, buf.length - hashLen)).digest();
|
|
53759
|
+
if (!actual.equals(expected)) throw fail2(`${algo} checksum mismatch`);
|
|
53760
|
+
const version6 = buf.readUInt32BE(4);
|
|
53761
|
+
if (version6 !== 2 && version6 !== 3) throw fail2(`unsupported index version ${version6}`);
|
|
53762
|
+
const count = buf.readUInt32BE(8);
|
|
53763
|
+
const paths = /* @__PURE__ */ new Set();
|
|
53764
|
+
const fixed = 40 + hashLen + 2;
|
|
53765
|
+
let off = 12;
|
|
53766
|
+
for (let i = 0; i < count; i++) {
|
|
53767
|
+
const entryStart = off;
|
|
53768
|
+
if (off + fixed > buf.length - hashLen) throw fail2("truncated entry");
|
|
53769
|
+
const flags = buf.readUInt16BE(off + fixed - 2);
|
|
53770
|
+
let nameOff = off + fixed;
|
|
53771
|
+
if (flags & 16384) {
|
|
53772
|
+
if (version6 < 3) throw fail2("extended flags in v2 index");
|
|
53773
|
+
nameOff += 2;
|
|
53774
|
+
}
|
|
53775
|
+
const nameLen = flags & 4095;
|
|
53776
|
+
let end;
|
|
53777
|
+
if (nameLen < 4095) {
|
|
53778
|
+
end = nameOff + nameLen;
|
|
53779
|
+
if (end > buf.length - hashLen) throw fail2("truncated path");
|
|
53780
|
+
} else {
|
|
53781
|
+
end = buf.indexOf(0, nameOff);
|
|
53782
|
+
if (end === -1 || end > buf.length - hashLen) throw fail2("unterminated path");
|
|
53783
|
+
}
|
|
53784
|
+
paths.add(buf.toString("utf8", nameOff, end));
|
|
53785
|
+
const entryLen = end - entryStart;
|
|
53786
|
+
off = entryStart + (Math.floor(entryLen / 8) + 1) * 8;
|
|
53787
|
+
}
|
|
53788
|
+
while (off < buf.length - hashLen) {
|
|
53789
|
+
if (off + 8 > buf.length - hashLen) throw fail2("truncated extension header");
|
|
53790
|
+
const extName = buf.toString("latin1", off, off + 4);
|
|
53791
|
+
const extSize = buf.readUInt32BE(off + 4);
|
|
53792
|
+
const first = extName.charCodeAt(0);
|
|
53793
|
+
if (first >= 97 && first <= 122) {
|
|
53794
|
+
throw fail2(`mandatory index extension "${extName}" (e.g. core.splitIndex) is not supported`);
|
|
53795
|
+
}
|
|
53796
|
+
off += 8 + extSize;
|
|
53797
|
+
if (off > buf.length - hashLen) throw fail2("truncated extension");
|
|
53798
|
+
}
|
|
53799
|
+
return paths;
|
|
53800
|
+
}
|
|
53801
|
+
function repoHashLen(gitDir) {
|
|
53802
|
+
let configDir = gitDir;
|
|
53803
|
+
try {
|
|
53804
|
+
const common = fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim();
|
|
53805
|
+
if (common) configDir = path4.resolve(gitDir, common);
|
|
53806
|
+
} catch {
|
|
53807
|
+
}
|
|
53808
|
+
try {
|
|
53809
|
+
const cfg = fs4.readFileSync(path4.join(configDir, "config"), "utf8");
|
|
53810
|
+
if (/^\s*objectformat\s*=\s*sha256\s*$/im.test(cfg)) return 32;
|
|
53811
|
+
} catch {
|
|
53812
|
+
}
|
|
53813
|
+
return 20;
|
|
53814
|
+
}
|
|
53815
|
+
function trackedAmong(repo, files) {
|
|
53816
|
+
const indexPath = path4.join(repo.gitDir, "index");
|
|
53817
|
+
let buf;
|
|
53704
53818
|
try {
|
|
53705
|
-
|
|
53706
|
-
cwd: dir,
|
|
53707
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
53708
|
-
encoding: "utf8"
|
|
53709
|
-
}).trim() === "true";
|
|
53819
|
+
buf = fs4.readFileSync(indexPath);
|
|
53710
53820
|
} catch (err) {
|
|
53711
|
-
|
|
53712
|
-
if (/not a git repository|does not appear to be a git repository/i.test(stderr)) return false;
|
|
53821
|
+
if (err?.code === "ENOENT") return [];
|
|
53713
53822
|
throw new SkillError(
|
|
53714
53823
|
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53715
|
-
|
|
53824
|
+
"The git index exists but could not be read, so tracked status cannot be proved. Refusing."
|
|
53716
53825
|
);
|
|
53717
53826
|
}
|
|
53827
|
+
const tracked = trackedPathsFromIndex(buf, { hashLen: repoHashLen(repo.gitDir) });
|
|
53828
|
+
return files.filter((f) => {
|
|
53829
|
+
const rel = path4.relative(repo.root, path4.resolve(f)).split(path4.sep).join("/");
|
|
53830
|
+
return tracked.has(rel);
|
|
53831
|
+
});
|
|
53718
53832
|
}
|
|
53833
|
+
var ENV_FILE_RE = /^\.env(\..+)?$/;
|
|
53834
|
+
var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
|
|
53719
53835
|
function assertNotTrackedByGit(file) {
|
|
53720
53836
|
const dir = path4.dirname(path4.resolve(file));
|
|
53721
|
-
const
|
|
53722
|
-
if (!
|
|
53723
|
-
|
|
53724
|
-
|
|
53725
|
-
|
|
53726
|
-
cwd: dir,
|
|
53727
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
53728
|
-
encoding: "utf8"
|
|
53729
|
-
});
|
|
53730
|
-
} catch {
|
|
53731
|
-
throw new SkillError(
|
|
53732
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53733
|
-
"git could not report whether this key file is tracked. Refusing to use it."
|
|
53734
|
-
);
|
|
53735
|
-
}
|
|
53736
|
-
if (out2.split("\0").filter(Boolean).length) {
|
|
53837
|
+
const repo = findGitRepo(dir);
|
|
53838
|
+
if (!repo) return { checked: true, tracked: false };
|
|
53839
|
+
const hits = trackedAmong(repo, [file]);
|
|
53840
|
+
if (hits.length) {
|
|
53841
|
+
const base = path4.basename(file);
|
|
53737
53842
|
throw new SkillError(
|
|
53738
53843
|
"TRACKED_KEYFILE",
|
|
53739
53844
|
`${base} is tracked by git. A committed key is one push from being public \u2014 untrack it (git rm --cached ${base}) and gitignore it before using it to sign.`
|
|
@@ -53752,37 +53857,11 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
53752
53857
|
);
|
|
53753
53858
|
}
|
|
53754
53859
|
if (candidates.length === 0) return { checked: true, tracked: false, candidates: [] };
|
|
53755
|
-
|
|
53756
|
-
|
|
53757
|
-
|
|
53758
|
-
|
|
53759
|
-
|
|
53760
|
-
encoding: "utf8"
|
|
53761
|
-
}).trim() === "true";
|
|
53762
|
-
} catch (err) {
|
|
53763
|
-
const stderr = String(err?.stderr || "");
|
|
53764
|
-
const notARepo = /not a git repository|does not appear to be a git repository/i.test(stderr);
|
|
53765
|
-
if (notARepo) return { checked: true, tracked: false, candidates };
|
|
53766
|
-
throw new SkillError(
|
|
53767
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53768
|
-
`Found ${candidates.length} .env file(s) here, but git could not be consulted (${err?.code || "unknown error"}), so it cannot be proved they are untracked. Refusing to use hot-wallet keys in this directory.`
|
|
53769
|
-
);
|
|
53770
|
-
}
|
|
53771
|
-
if (!insideRepo) return { checked: true, tracked: false, candidates };
|
|
53772
|
-
let out2;
|
|
53773
|
-
try {
|
|
53774
|
-
out2 = execFileSync("git", ["ls-files", "-z", "--", ...candidates], {
|
|
53775
|
-
cwd,
|
|
53776
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
53777
|
-
encoding: "utf8"
|
|
53778
|
-
});
|
|
53779
|
-
} catch {
|
|
53780
|
-
throw new SkillError(
|
|
53781
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
53782
|
-
"git could not report whether the .env file(s) in this directory are tracked. Refusing to use hot-wallet keys rather than assuming they are safe."
|
|
53783
|
-
);
|
|
53784
|
-
}
|
|
53785
|
-
const tracked = out2.split("\0").filter(Boolean);
|
|
53860
|
+
const repo = findGitRepo(cwd);
|
|
53861
|
+
if (!repo) return { checked: true, tracked: false, candidates };
|
|
53862
|
+
const tracked = trackedAmong(repo, candidates.map((f) => path4.join(cwd, f))).map(
|
|
53863
|
+
(f) => path4.basename(f)
|
|
53864
|
+
);
|
|
53786
53865
|
if (tracked.length) {
|
|
53787
53866
|
throw new SkillError(
|
|
53788
53867
|
"TRACKED_DOTENV",
|
|
@@ -54018,7 +54097,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
54018
54097
|
if (!n || !r || !p || !dklen || !salt) {
|
|
54019
54098
|
throw new SkillError("BAD_KEYSTORE", "The keystore scrypt parameters are incomplete.");
|
|
54020
54099
|
}
|
|
54021
|
-
derived =
|
|
54100
|
+
derived = crypto6.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
|
|
54022
54101
|
N: n,
|
|
54023
54102
|
r,
|
|
54024
54103
|
p,
|
|
@@ -54034,7 +54113,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
54034
54113
|
if (!iterations || !dklen || !salt) {
|
|
54035
54114
|
throw new SkillError("BAD_KEYSTORE", "The keystore pbkdf2 parameters are incomplete.");
|
|
54036
54115
|
}
|
|
54037
|
-
derived =
|
|
54116
|
+
derived = crypto6.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
|
|
54038
54117
|
} else {
|
|
54039
54118
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore KDF "${c2.kdf}".`);
|
|
54040
54119
|
}
|
|
@@ -54049,7 +54128,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
54049
54128
|
if (c2.cipher !== "aes-128-ctr") {
|
|
54050
54129
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore cipher "${c2.cipher}".`);
|
|
54051
54130
|
}
|
|
54052
|
-
const decipher =
|
|
54131
|
+
const decipher = crypto6.createDecipheriv(
|
|
54053
54132
|
"aes-128-ctr",
|
|
54054
54133
|
derived.subarray(0, 16),
|
|
54055
54134
|
Buffer.from(c2.cipherparams.iv, "hex")
|
|
@@ -54071,7 +54150,7 @@ function timingSafeEqualHex(a, b) {
|
|
|
54071
54150
|
const ab = Buffer.from(String(a).toLowerCase(), "hex");
|
|
54072
54151
|
const bb = Buffer.from(String(b).toLowerCase(), "hex");
|
|
54073
54152
|
if (ab.length === 0 || ab.length !== bb.length) return false;
|
|
54074
|
-
return
|
|
54153
|
+
return crypto6.timingSafeEqual(ab, bb);
|
|
54075
54154
|
}
|
|
54076
54155
|
function looksLikeKeystore(text) {
|
|
54077
54156
|
const t = String(text).trimStart();
|
package/scripts/dist/send-evm.js
CHANGED
|
@@ -1759,10 +1759,10 @@ var init_u64 = __esm({
|
|
|
1759
1759
|
|
|
1760
1760
|
// node_modules/@noble/hashes/esm/cryptoNode.js
|
|
1761
1761
|
import * as nc from "node:crypto";
|
|
1762
|
-
var
|
|
1762
|
+
var crypto3;
|
|
1763
1763
|
var init_cryptoNode = __esm({
|
|
1764
1764
|
"node_modules/@noble/hashes/esm/cryptoNode.js"() {
|
|
1765
|
-
|
|
1765
|
+
crypto3 = nc && typeof nc === "object" && "webcrypto" in nc ? nc.webcrypto : nc && typeof nc === "object" && "randomBytes" in nc ? nc : void 0;
|
|
1766
1766
|
}
|
|
1767
1767
|
});
|
|
1768
1768
|
|
|
@@ -1857,11 +1857,11 @@ function createHasher(hashCons) {
|
|
|
1857
1857
|
return hashC;
|
|
1858
1858
|
}
|
|
1859
1859
|
function randomBytes(bytesLength = 32) {
|
|
1860
|
-
if (
|
|
1861
|
-
return
|
|
1860
|
+
if (crypto3 && typeof crypto3.getRandomValues === "function") {
|
|
1861
|
+
return crypto3.getRandomValues(new Uint8Array(bytesLength));
|
|
1862
1862
|
}
|
|
1863
|
-
if (
|
|
1864
|
-
return Uint8Array.from(
|
|
1863
|
+
if (crypto3 && typeof crypto3.randomBytes === "function") {
|
|
1864
|
+
return Uint8Array.from(crypto3.randomBytes(bytesLength));
|
|
1865
1865
|
}
|
|
1866
1866
|
throw new Error("crypto.getRandomValues must be defined");
|
|
1867
1867
|
}
|
|
@@ -10214,43 +10214,148 @@ function chainFamily(chainId) {
|
|
|
10214
10214
|
// scripts/src/lib/keys.mjs
|
|
10215
10215
|
import fs from "node:fs";
|
|
10216
10216
|
import path from "node:path";
|
|
10217
|
-
import
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10217
|
+
import crypto2 from "node:crypto";
|
|
10218
|
+
function findGitRepo(dir) {
|
|
10219
|
+
for (const v of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) {
|
|
10220
|
+
if (process.env[v]) {
|
|
10221
|
+
throw new SkillError(
|
|
10222
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10223
|
+
`${v} is set, so the repository layout cannot be resolved by reading the filesystem alone. Unset it (or run from a plain checkout) before using hot-wallet keys.`
|
|
10224
|
+
);
|
|
10225
|
+
}
|
|
10226
|
+
}
|
|
10227
|
+
let cur = path.resolve(dir);
|
|
10228
|
+
for (; ; ) {
|
|
10229
|
+
const dotGit = path.join(cur, ".git");
|
|
10230
|
+
let st = null;
|
|
10231
|
+
try {
|
|
10232
|
+
st = fs.statSync(dotGit);
|
|
10233
|
+
} catch (err) {
|
|
10234
|
+
if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
|
|
10235
|
+
throw new SkillError(
|
|
10236
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10237
|
+
`A .git entry here could not be inspected (${err?.code || "error"}), so tracked status cannot be proved. Refusing.`
|
|
10238
|
+
);
|
|
10239
|
+
}
|
|
10240
|
+
st = null;
|
|
10241
|
+
}
|
|
10242
|
+
if (st) {
|
|
10243
|
+
if (st.isDirectory()) return { root: cur, gitDir: dotGit };
|
|
10244
|
+
let text;
|
|
10245
|
+
try {
|
|
10246
|
+
text = fs.readFileSync(dotGit, "utf8");
|
|
10247
|
+
} catch {
|
|
10248
|
+
throw new SkillError(
|
|
10249
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10250
|
+
"A .git entry exists here but could not be read, so tracked status cannot be proved. Refusing."
|
|
10251
|
+
);
|
|
10252
|
+
}
|
|
10253
|
+
const m = /^gitdir:\s*(.+)\s*$/m.exec(text);
|
|
10254
|
+
if (!m) {
|
|
10255
|
+
throw new SkillError(
|
|
10256
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10257
|
+
"A .git file exists here but is not a recognised gitdir pointer. Refusing."
|
|
10258
|
+
);
|
|
10259
|
+
}
|
|
10260
|
+
return { root: cur, gitDir: path.resolve(cur, m[1]) };
|
|
10261
|
+
}
|
|
10262
|
+
const parent = path.dirname(cur);
|
|
10263
|
+
if (parent === cur) return null;
|
|
10264
|
+
cur = parent;
|
|
10265
|
+
}
|
|
10266
|
+
}
|
|
10267
|
+
function trackedPathsFromIndex(buf, { hashLen = 20 } = {}) {
|
|
10268
|
+
const fail2 = (why) => new SkillError(
|
|
10269
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10270
|
+
`The git index could not be interpreted (${why}), so tracked status cannot be proved. Refusing.`
|
|
10271
|
+
);
|
|
10272
|
+
if (buf.length < 12 + hashLen || buf.toString("latin1", 0, 4) !== "DIRC") throw fail2("bad header");
|
|
10273
|
+
const algo = hashLen === 32 ? "sha256" : "sha1";
|
|
10274
|
+
const expected = buf.subarray(buf.length - hashLen);
|
|
10275
|
+
const actual = crypto2.createHash(algo).update(buf.subarray(0, buf.length - hashLen)).digest();
|
|
10276
|
+
if (!actual.equals(expected)) throw fail2(`${algo} checksum mismatch`);
|
|
10277
|
+
const version4 = buf.readUInt32BE(4);
|
|
10278
|
+
if (version4 !== 2 && version4 !== 3) throw fail2(`unsupported index version ${version4}`);
|
|
10279
|
+
const count = buf.readUInt32BE(8);
|
|
10280
|
+
const paths = /* @__PURE__ */ new Set();
|
|
10281
|
+
const fixed = 40 + hashLen + 2;
|
|
10282
|
+
let off = 12;
|
|
10283
|
+
for (let i = 0; i < count; i++) {
|
|
10284
|
+
const entryStart = off;
|
|
10285
|
+
if (off + fixed > buf.length - hashLen) throw fail2("truncated entry");
|
|
10286
|
+
const flags = buf.readUInt16BE(off + fixed - 2);
|
|
10287
|
+
let nameOff = off + fixed;
|
|
10288
|
+
if (flags & 16384) {
|
|
10289
|
+
if (version4 < 3) throw fail2("extended flags in v2 index");
|
|
10290
|
+
nameOff += 2;
|
|
10291
|
+
}
|
|
10292
|
+
const nameLen = flags & 4095;
|
|
10293
|
+
let end;
|
|
10294
|
+
if (nameLen < 4095) {
|
|
10295
|
+
end = nameOff + nameLen;
|
|
10296
|
+
if (end > buf.length - hashLen) throw fail2("truncated path");
|
|
10297
|
+
} else {
|
|
10298
|
+
end = buf.indexOf(0, nameOff);
|
|
10299
|
+
if (end === -1 || end > buf.length - hashLen) throw fail2("unterminated path");
|
|
10300
|
+
}
|
|
10301
|
+
paths.add(buf.toString("utf8", nameOff, end));
|
|
10302
|
+
const entryLen = end - entryStart;
|
|
10303
|
+
off = entryStart + (Math.floor(entryLen / 8) + 1) * 8;
|
|
10304
|
+
}
|
|
10305
|
+
while (off < buf.length - hashLen) {
|
|
10306
|
+
if (off + 8 > buf.length - hashLen) throw fail2("truncated extension header");
|
|
10307
|
+
const extName = buf.toString("latin1", off, off + 4);
|
|
10308
|
+
const extSize = buf.readUInt32BE(off + 4);
|
|
10309
|
+
const first = extName.charCodeAt(0);
|
|
10310
|
+
if (first >= 97 && first <= 122) {
|
|
10311
|
+
throw fail2(`mandatory index extension "${extName}" (e.g. core.splitIndex) is not supported`);
|
|
10312
|
+
}
|
|
10313
|
+
off += 8 + extSize;
|
|
10314
|
+
if (off > buf.length - hashLen) throw fail2("truncated extension");
|
|
10315
|
+
}
|
|
10316
|
+
return paths;
|
|
10317
|
+
}
|
|
10318
|
+
function repoHashLen(gitDir) {
|
|
10319
|
+
let configDir = gitDir;
|
|
10221
10320
|
try {
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10321
|
+
const common = fs.readFileSync(path.join(gitDir, "commondir"), "utf8").trim();
|
|
10322
|
+
if (common) configDir = path.resolve(gitDir, common);
|
|
10323
|
+
} catch {
|
|
10324
|
+
}
|
|
10325
|
+
try {
|
|
10326
|
+
const cfg = fs.readFileSync(path.join(configDir, "config"), "utf8");
|
|
10327
|
+
if (/^\s*objectformat\s*=\s*sha256\s*$/im.test(cfg)) return 32;
|
|
10328
|
+
} catch {
|
|
10329
|
+
}
|
|
10330
|
+
return 20;
|
|
10331
|
+
}
|
|
10332
|
+
function trackedAmong(repo, files) {
|
|
10333
|
+
const indexPath = path.join(repo.gitDir, "index");
|
|
10334
|
+
let buf;
|
|
10335
|
+
try {
|
|
10336
|
+
buf = fs.readFileSync(indexPath);
|
|
10227
10337
|
} catch (err) {
|
|
10228
|
-
|
|
10229
|
-
if (/not a git repository|does not appear to be a git repository/i.test(stderr)) return false;
|
|
10338
|
+
if (err?.code === "ENOENT") return [];
|
|
10230
10339
|
throw new SkillError(
|
|
10231
10340
|
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10232
|
-
|
|
10341
|
+
"The git index exists but could not be read, so tracked status cannot be proved. Refusing."
|
|
10233
10342
|
);
|
|
10234
10343
|
}
|
|
10344
|
+
const tracked = trackedPathsFromIndex(buf, { hashLen: repoHashLen(repo.gitDir) });
|
|
10345
|
+
return files.filter((f) => {
|
|
10346
|
+
const rel = path.relative(repo.root, path.resolve(f)).split(path.sep).join("/");
|
|
10347
|
+
return tracked.has(rel);
|
|
10348
|
+
});
|
|
10235
10349
|
}
|
|
10350
|
+
var ENV_FILE_RE = /^\.env(\..+)?$/;
|
|
10351
|
+
var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
|
|
10236
10352
|
function assertNotTrackedByGit(file) {
|
|
10237
10353
|
const dir = path.dirname(path.resolve(file));
|
|
10238
|
-
const
|
|
10239
|
-
if (!
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
cwd: dir,
|
|
10244
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
10245
|
-
encoding: "utf8"
|
|
10246
|
-
});
|
|
10247
|
-
} catch {
|
|
10248
|
-
throw new SkillError(
|
|
10249
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10250
|
-
"git could not report whether this key file is tracked. Refusing to use it."
|
|
10251
|
-
);
|
|
10252
|
-
}
|
|
10253
|
-
if (out.split("\0").filter(Boolean).length) {
|
|
10354
|
+
const repo = findGitRepo(dir);
|
|
10355
|
+
if (!repo) return { checked: true, tracked: false };
|
|
10356
|
+
const hits = trackedAmong(repo, [file]);
|
|
10357
|
+
if (hits.length) {
|
|
10358
|
+
const base = path.basename(file);
|
|
10254
10359
|
throw new SkillError(
|
|
10255
10360
|
"TRACKED_KEYFILE",
|
|
10256
10361
|
`${base} is tracked by git. A committed key is one push from being public \u2014 untrack it (git rm --cached ${base}) and gitignore it before using it to sign.`
|
|
@@ -10269,37 +10374,11 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
10269
10374
|
);
|
|
10270
10375
|
}
|
|
10271
10376
|
if (candidates.length === 0) return { checked: true, tracked: false, candidates: [] };
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
encoding: "utf8"
|
|
10278
|
-
}).trim() === "true";
|
|
10279
|
-
} catch (err) {
|
|
10280
|
-
const stderr = String(err?.stderr || "");
|
|
10281
|
-
const notARepo = /not a git repository|does not appear to be a git repository/i.test(stderr);
|
|
10282
|
-
if (notARepo) return { checked: true, tracked: false, candidates };
|
|
10283
|
-
throw new SkillError(
|
|
10284
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10285
|
-
`Found ${candidates.length} .env file(s) here, but git could not be consulted (${err?.code || "unknown error"}), so it cannot be proved they are untracked. Refusing to use hot-wallet keys in this directory.`
|
|
10286
|
-
);
|
|
10287
|
-
}
|
|
10288
|
-
if (!insideRepo) return { checked: true, tracked: false, candidates };
|
|
10289
|
-
let out;
|
|
10290
|
-
try {
|
|
10291
|
-
out = execFileSync("git", ["ls-files", "-z", "--", ...candidates], {
|
|
10292
|
-
cwd,
|
|
10293
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
10294
|
-
encoding: "utf8"
|
|
10295
|
-
});
|
|
10296
|
-
} catch {
|
|
10297
|
-
throw new SkillError(
|
|
10298
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
10299
|
-
"git could not report whether the .env file(s) in this directory are tracked. Refusing to use hot-wallet keys rather than assuming they are safe."
|
|
10300
|
-
);
|
|
10301
|
-
}
|
|
10302
|
-
const tracked = out.split("\0").filter(Boolean);
|
|
10377
|
+
const repo = findGitRepo(cwd);
|
|
10378
|
+
if (!repo) return { checked: true, tracked: false, candidates };
|
|
10379
|
+
const tracked = trackedAmong(repo, candidates.map((f) => path.join(cwd, f))).map(
|
|
10380
|
+
(f) => path.basename(f)
|
|
10381
|
+
);
|
|
10303
10382
|
if (tracked.length) {
|
|
10304
10383
|
throw new SkillError(
|
|
10305
10384
|
"TRACKED_DOTENV",
|
|
@@ -10313,7 +10392,7 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
10313
10392
|
import fs3 from "node:fs";
|
|
10314
10393
|
import path3 from "node:path";
|
|
10315
10394
|
import os2 from "node:os";
|
|
10316
|
-
import
|
|
10395
|
+
import crypto4 from "node:crypto";
|
|
10317
10396
|
|
|
10318
10397
|
// node_modules/viem/_esm/utils/getAction.js
|
|
10319
10398
|
function getAction(client, actionFn, name) {
|
|
@@ -21504,7 +21583,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
21504
21583
|
if (!n || !r || !p || !dklen || !salt) {
|
|
21505
21584
|
throw new SkillError("BAD_KEYSTORE", "The keystore scrypt parameters are incomplete.");
|
|
21506
21585
|
}
|
|
21507
|
-
derived =
|
|
21586
|
+
derived = crypto4.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
|
|
21508
21587
|
N: n,
|
|
21509
21588
|
r,
|
|
21510
21589
|
p,
|
|
@@ -21520,7 +21599,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
21520
21599
|
if (!iterations || !dklen || !salt) {
|
|
21521
21600
|
throw new SkillError("BAD_KEYSTORE", "The keystore pbkdf2 parameters are incomplete.");
|
|
21522
21601
|
}
|
|
21523
|
-
derived =
|
|
21602
|
+
derived = crypto4.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
|
|
21524
21603
|
} else {
|
|
21525
21604
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore KDF "${c.kdf}".`);
|
|
21526
21605
|
}
|
|
@@ -21535,7 +21614,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
21535
21614
|
if (c.cipher !== "aes-128-ctr") {
|
|
21536
21615
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore cipher "${c.cipher}".`);
|
|
21537
21616
|
}
|
|
21538
|
-
const decipher =
|
|
21617
|
+
const decipher = crypto4.createDecipheriv(
|
|
21539
21618
|
"aes-128-ctr",
|
|
21540
21619
|
derived.subarray(0, 16),
|
|
21541
21620
|
Buffer.from(c.cipherparams.iv, "hex")
|
|
@@ -21557,7 +21636,7 @@ function timingSafeEqualHex(a, b) {
|
|
|
21557
21636
|
const ab = Buffer.from(String(a).toLowerCase(), "hex");
|
|
21558
21637
|
const bb = Buffer.from(String(b).toLowerCase(), "hex");
|
|
21559
21638
|
if (ab.length === 0 || ab.length !== bb.length) return false;
|
|
21560
|
-
return
|
|
21639
|
+
return crypto4.timingSafeEqual(ab, bb);
|
|
21561
21640
|
}
|
|
21562
21641
|
function looksLikeKeystore(text) {
|
|
21563
21642
|
const t = String(text).trimStart();
|
|
@@ -22107,7 +22186,7 @@ function checkExpiry({
|
|
|
22107
22186
|
// scripts/src/lib/blacklist.mjs
|
|
22108
22187
|
import fs4 from "node:fs";
|
|
22109
22188
|
import path4 from "node:path";
|
|
22110
|
-
import
|
|
22189
|
+
import crypto5 from "node:crypto";
|
|
22111
22190
|
import { fileURLToPath } from "node:url";
|
|
22112
22191
|
var BlacklistError = class extends Error {
|
|
22113
22192
|
constructor(code, message) {
|
|
@@ -22142,7 +22221,7 @@ function parseBlacklist(doc) {
|
|
|
22142
22221
|
}
|
|
22143
22222
|
addresses.push(e.address);
|
|
22144
22223
|
}
|
|
22145
|
-
const digest =
|
|
22224
|
+
const digest = crypto5.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
|
|
22146
22225
|
if (typeof provenance.addressesSha256 !== "string" || !provenance.addressesSha256) {
|
|
22147
22226
|
throw new BlacklistError("BLACKLIST_UNAVAILABLE", "Blacklist provenance digest is missing.");
|
|
22148
22227
|
}
|
|
@@ -22231,7 +22310,7 @@ function assertNotBlacklisted(targets, blacklist) {
|
|
|
22231
22310
|
import fs5 from "node:fs";
|
|
22232
22311
|
import path5 from "node:path";
|
|
22233
22312
|
import os3 from "node:os";
|
|
22234
|
-
import
|
|
22313
|
+
import crypto6 from "node:crypto";
|
|
22235
22314
|
var LOCK_STALE_MS = 6e4;
|
|
22236
22315
|
var LOCK_WAIT_MS = 1e4;
|
|
22237
22316
|
var LOCK_POLL_MS = 25;
|
|
@@ -22261,7 +22340,7 @@ function withLock(fn) {
|
|
|
22261
22340
|
try {
|
|
22262
22341
|
const age = Date.now() - fs5.statSync(file).mtimeMs;
|
|
22263
22342
|
if (age > LOCK_STALE_MS) {
|
|
22264
|
-
const stolen = `${file}.stale.${
|
|
22343
|
+
const stolen = `${file}.stale.${crypto6.randomBytes(4).toString("hex")}`;
|
|
22265
22344
|
try {
|
|
22266
22345
|
fs5.renameSync(file, stolen);
|
|
22267
22346
|
fs5.unlinkSync(stolen);
|
|
@@ -22302,7 +22381,7 @@ function statePath(rozoPaymentId) {
|
|
|
22302
22381
|
function writeAtomic(file, data) {
|
|
22303
22382
|
const dir = path5.dirname(file);
|
|
22304
22383
|
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
22305
|
-
const tmp = path5.join(dir, `.${path5.basename(file)}.${
|
|
22384
|
+
const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto6.randomBytes(6).toString("hex")}.tmp`);
|
|
22306
22385
|
const fd = fs5.openSync(tmp, "wx", 384);
|
|
22307
22386
|
try {
|
|
22308
22387
|
fs5.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
@@ -22341,7 +22420,7 @@ function depositDigest(source) {
|
|
|
22341
22420
|
amountUnit: source?.amountUnit ?? null,
|
|
22342
22421
|
lnInvoice: source?.lnInvoice ?? null
|
|
22343
22422
|
});
|
|
22344
|
-
return
|
|
22423
|
+
return crypto6.createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
22345
22424
|
}
|
|
22346
22425
|
function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
|
|
22347
22426
|
return withLock(() => {
|
package/scripts/dist/send-sol.js
CHANGED
|
@@ -11988,10 +11988,10 @@ var require_dist = __commonJS({
|
|
|
11988
11988
|
});
|
|
11989
11989
|
|
|
11990
11990
|
// node_modules/uuid/dist/esm-node/rng.js
|
|
11991
|
-
import
|
|
11991
|
+
import crypto6 from "crypto";
|
|
11992
11992
|
function rng() {
|
|
11993
11993
|
if (poolPtr > rnds8Pool.length - 16) {
|
|
11994
|
-
|
|
11994
|
+
crypto6.randomFillSync(rnds8Pool);
|
|
11995
11995
|
poolPtr = 0;
|
|
11996
11996
|
}
|
|
11997
11997
|
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
@@ -12191,14 +12191,14 @@ var init_v35 = __esm({
|
|
|
12191
12191
|
});
|
|
12192
12192
|
|
|
12193
12193
|
// node_modules/uuid/dist/esm-node/md5.js
|
|
12194
|
-
import
|
|
12194
|
+
import crypto7 from "crypto";
|
|
12195
12195
|
function md5(bytes) {
|
|
12196
12196
|
if (Array.isArray(bytes)) {
|
|
12197
12197
|
bytes = Buffer.from(bytes);
|
|
12198
12198
|
} else if (typeof bytes === "string") {
|
|
12199
12199
|
bytes = Buffer.from(bytes, "utf8");
|
|
12200
12200
|
}
|
|
12201
|
-
return
|
|
12201
|
+
return crypto7.createHash("md5").update(bytes).digest();
|
|
12202
12202
|
}
|
|
12203
12203
|
var md5_default;
|
|
12204
12204
|
var init_md5 = __esm({
|
|
@@ -12243,14 +12243,14 @@ var init_v4 = __esm({
|
|
|
12243
12243
|
});
|
|
12244
12244
|
|
|
12245
12245
|
// node_modules/uuid/dist/esm-node/sha1.js
|
|
12246
|
-
import
|
|
12246
|
+
import crypto8 from "crypto";
|
|
12247
12247
|
function sha1(bytes) {
|
|
12248
12248
|
if (Array.isArray(bytes)) {
|
|
12249
12249
|
bytes = Buffer.from(bytes);
|
|
12250
12250
|
} else if (typeof bytes === "string") {
|
|
12251
12251
|
bytes = Buffer.from(bytes, "utf8");
|
|
12252
12252
|
}
|
|
12253
|
-
return
|
|
12253
|
+
return crypto8.createHash("sha1").update(bytes).digest();
|
|
12254
12254
|
}
|
|
12255
12255
|
var sha1_default;
|
|
12256
12256
|
var init_sha1 = __esm({
|
|
@@ -32359,43 +32359,148 @@ function chainFamily(chainId) {
|
|
|
32359
32359
|
// scripts/src/lib/keys.mjs
|
|
32360
32360
|
import fs from "node:fs";
|
|
32361
32361
|
import path from "node:path";
|
|
32362
|
-
import
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
|
|
32362
|
+
import crypto2 from "node:crypto";
|
|
32363
|
+
function findGitRepo(dir) {
|
|
32364
|
+
for (const v of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) {
|
|
32365
|
+
if (process.env[v]) {
|
|
32366
|
+
throw new SkillError(
|
|
32367
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32368
|
+
`${v} is set, so the repository layout cannot be resolved by reading the filesystem alone. Unset it (or run from a plain checkout) before using hot-wallet keys.`
|
|
32369
|
+
);
|
|
32370
|
+
}
|
|
32371
|
+
}
|
|
32372
|
+
let cur = path.resolve(dir);
|
|
32373
|
+
for (; ; ) {
|
|
32374
|
+
const dotGit = path.join(cur, ".git");
|
|
32375
|
+
let st = null;
|
|
32376
|
+
try {
|
|
32377
|
+
st = fs.statSync(dotGit);
|
|
32378
|
+
} catch (err) {
|
|
32379
|
+
if (err?.code !== "ENOENT" && err?.code !== "ENOTDIR") {
|
|
32380
|
+
throw new SkillError(
|
|
32381
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32382
|
+
`A .git entry here could not be inspected (${err?.code || "error"}), so tracked status cannot be proved. Refusing.`
|
|
32383
|
+
);
|
|
32384
|
+
}
|
|
32385
|
+
st = null;
|
|
32386
|
+
}
|
|
32387
|
+
if (st) {
|
|
32388
|
+
if (st.isDirectory()) return { root: cur, gitDir: dotGit };
|
|
32389
|
+
let text;
|
|
32390
|
+
try {
|
|
32391
|
+
text = fs.readFileSync(dotGit, "utf8");
|
|
32392
|
+
} catch {
|
|
32393
|
+
throw new SkillError(
|
|
32394
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32395
|
+
"A .git entry exists here but could not be read, so tracked status cannot be proved. Refusing."
|
|
32396
|
+
);
|
|
32397
|
+
}
|
|
32398
|
+
const m = /^gitdir:\s*(.+)\s*$/m.exec(text);
|
|
32399
|
+
if (!m) {
|
|
32400
|
+
throw new SkillError(
|
|
32401
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32402
|
+
"A .git file exists here but is not a recognised gitdir pointer. Refusing."
|
|
32403
|
+
);
|
|
32404
|
+
}
|
|
32405
|
+
return { root: cur, gitDir: path.resolve(cur, m[1]) };
|
|
32406
|
+
}
|
|
32407
|
+
const parent = path.dirname(cur);
|
|
32408
|
+
if (parent === cur) return null;
|
|
32409
|
+
cur = parent;
|
|
32410
|
+
}
|
|
32411
|
+
}
|
|
32412
|
+
function trackedPathsFromIndex(buf, { hashLen = 20 } = {}) {
|
|
32413
|
+
const fail2 = (why) => new SkillError(
|
|
32414
|
+
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32415
|
+
`The git index could not be interpreted (${why}), so tracked status cannot be proved. Refusing.`
|
|
32416
|
+
);
|
|
32417
|
+
if (buf.length < 12 + hashLen || buf.toString("latin1", 0, 4) !== "DIRC") throw fail2("bad header");
|
|
32418
|
+
const algo = hashLen === 32 ? "sha256" : "sha1";
|
|
32419
|
+
const expected = buf.subarray(buf.length - hashLen);
|
|
32420
|
+
const actual = crypto2.createHash(algo).update(buf.subarray(0, buf.length - hashLen)).digest();
|
|
32421
|
+
if (!actual.equals(expected)) throw fail2(`${algo} checksum mismatch`);
|
|
32422
|
+
const version4 = buf.readUInt32BE(4);
|
|
32423
|
+
if (version4 !== 2 && version4 !== 3) throw fail2(`unsupported index version ${version4}`);
|
|
32424
|
+
const count = buf.readUInt32BE(8);
|
|
32425
|
+
const paths = /* @__PURE__ */ new Set();
|
|
32426
|
+
const fixed = 40 + hashLen + 2;
|
|
32427
|
+
let off = 12;
|
|
32428
|
+
for (let i = 0; i < count; i++) {
|
|
32429
|
+
const entryStart = off;
|
|
32430
|
+
if (off + fixed > buf.length - hashLen) throw fail2("truncated entry");
|
|
32431
|
+
const flags = buf.readUInt16BE(off + fixed - 2);
|
|
32432
|
+
let nameOff = off + fixed;
|
|
32433
|
+
if (flags & 16384) {
|
|
32434
|
+
if (version4 < 3) throw fail2("extended flags in v2 index");
|
|
32435
|
+
nameOff += 2;
|
|
32436
|
+
}
|
|
32437
|
+
const nameLen = flags & 4095;
|
|
32438
|
+
let end;
|
|
32439
|
+
if (nameLen < 4095) {
|
|
32440
|
+
end = nameOff + nameLen;
|
|
32441
|
+
if (end > buf.length - hashLen) throw fail2("truncated path");
|
|
32442
|
+
} else {
|
|
32443
|
+
end = buf.indexOf(0, nameOff);
|
|
32444
|
+
if (end === -1 || end > buf.length - hashLen) throw fail2("unterminated path");
|
|
32445
|
+
}
|
|
32446
|
+
paths.add(buf.toString("utf8", nameOff, end));
|
|
32447
|
+
const entryLen = end - entryStart;
|
|
32448
|
+
off = entryStart + (Math.floor(entryLen / 8) + 1) * 8;
|
|
32449
|
+
}
|
|
32450
|
+
while (off < buf.length - hashLen) {
|
|
32451
|
+
if (off + 8 > buf.length - hashLen) throw fail2("truncated extension header");
|
|
32452
|
+
const extName = buf.toString("latin1", off, off + 4);
|
|
32453
|
+
const extSize = buf.readUInt32BE(off + 4);
|
|
32454
|
+
const first = extName.charCodeAt(0);
|
|
32455
|
+
if (first >= 97 && first <= 122) {
|
|
32456
|
+
throw fail2(`mandatory index extension "${extName}" (e.g. core.splitIndex) is not supported`);
|
|
32457
|
+
}
|
|
32458
|
+
off += 8 + extSize;
|
|
32459
|
+
if (off > buf.length - hashLen) throw fail2("truncated extension");
|
|
32460
|
+
}
|
|
32461
|
+
return paths;
|
|
32462
|
+
}
|
|
32463
|
+
function repoHashLen(gitDir) {
|
|
32464
|
+
let configDir = gitDir;
|
|
32366
32465
|
try {
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32370
|
-
|
|
32371
|
-
|
|
32466
|
+
const common = fs.readFileSync(path.join(gitDir, "commondir"), "utf8").trim();
|
|
32467
|
+
if (common) configDir = path.resolve(gitDir, common);
|
|
32468
|
+
} catch {
|
|
32469
|
+
}
|
|
32470
|
+
try {
|
|
32471
|
+
const cfg = fs.readFileSync(path.join(configDir, "config"), "utf8");
|
|
32472
|
+
if (/^\s*objectformat\s*=\s*sha256\s*$/im.test(cfg)) return 32;
|
|
32473
|
+
} catch {
|
|
32474
|
+
}
|
|
32475
|
+
return 20;
|
|
32476
|
+
}
|
|
32477
|
+
function trackedAmong(repo, files) {
|
|
32478
|
+
const indexPath = path.join(repo.gitDir, "index");
|
|
32479
|
+
let buf;
|
|
32480
|
+
try {
|
|
32481
|
+
buf = fs.readFileSync(indexPath);
|
|
32372
32482
|
} catch (err) {
|
|
32373
|
-
|
|
32374
|
-
if (/not a git repository|does not appear to be a git repository/i.test(stderr)) return false;
|
|
32483
|
+
if (err?.code === "ENOENT") return [];
|
|
32375
32484
|
throw new SkillError(
|
|
32376
32485
|
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32377
|
-
|
|
32486
|
+
"The git index exists but could not be read, so tracked status cannot be proved. Refusing."
|
|
32378
32487
|
);
|
|
32379
32488
|
}
|
|
32489
|
+
const tracked = trackedPathsFromIndex(buf, { hashLen: repoHashLen(repo.gitDir) });
|
|
32490
|
+
return files.filter((f) => {
|
|
32491
|
+
const rel = path.relative(repo.root, path.resolve(f)).split(path.sep).join("/");
|
|
32492
|
+
return tracked.has(rel);
|
|
32493
|
+
});
|
|
32380
32494
|
}
|
|
32495
|
+
var ENV_FILE_RE = /^\.env(\..+)?$/;
|
|
32496
|
+
var PUBLIC_ENV_RE = /^\.env\.(example|sample|template)$/;
|
|
32381
32497
|
function assertNotTrackedByGit(file) {
|
|
32382
32498
|
const dir = path.dirname(path.resolve(file));
|
|
32383
|
-
const
|
|
32384
|
-
if (!
|
|
32385
|
-
|
|
32386
|
-
|
|
32387
|
-
|
|
32388
|
-
cwd: dir,
|
|
32389
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
32390
|
-
encoding: "utf8"
|
|
32391
|
-
});
|
|
32392
|
-
} catch {
|
|
32393
|
-
throw new SkillError(
|
|
32394
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32395
|
-
"git could not report whether this key file is tracked. Refusing to use it."
|
|
32396
|
-
);
|
|
32397
|
-
}
|
|
32398
|
-
if (out.split("\0").filter(Boolean).length) {
|
|
32499
|
+
const repo = findGitRepo(dir);
|
|
32500
|
+
if (!repo) return { checked: true, tracked: false };
|
|
32501
|
+
const hits = trackedAmong(repo, [file]);
|
|
32502
|
+
if (hits.length) {
|
|
32503
|
+
const base = path.basename(file);
|
|
32399
32504
|
throw new SkillError(
|
|
32400
32505
|
"TRACKED_KEYFILE",
|
|
32401
32506
|
`${base} is tracked by git. A committed key is one push from being public \u2014 untrack it (git rm --cached ${base}) and gitignore it before using it to sign.`
|
|
@@ -32414,37 +32519,11 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
32414
32519
|
);
|
|
32415
32520
|
}
|
|
32416
32521
|
if (candidates.length === 0) return { checked: true, tracked: false, candidates: [] };
|
|
32417
|
-
|
|
32418
|
-
|
|
32419
|
-
|
|
32420
|
-
|
|
32421
|
-
|
|
32422
|
-
encoding: "utf8"
|
|
32423
|
-
}).trim() === "true";
|
|
32424
|
-
} catch (err) {
|
|
32425
|
-
const stderr = String(err?.stderr || "");
|
|
32426
|
-
const notARepo = /not a git repository|does not appear to be a git repository/i.test(stderr);
|
|
32427
|
-
if (notARepo) return { checked: true, tracked: false, candidates };
|
|
32428
|
-
throw new SkillError(
|
|
32429
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32430
|
-
`Found ${candidates.length} .env file(s) here, but git could not be consulted (${err?.code || "unknown error"}), so it cannot be proved they are untracked. Refusing to use hot-wallet keys in this directory.`
|
|
32431
|
-
);
|
|
32432
|
-
}
|
|
32433
|
-
if (!insideRepo) return { checked: true, tracked: false, candidates };
|
|
32434
|
-
let out;
|
|
32435
|
-
try {
|
|
32436
|
-
out = execFileSync("git", ["ls-files", "-z", "--", ...candidates], {
|
|
32437
|
-
cwd,
|
|
32438
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
32439
|
-
encoding: "utf8"
|
|
32440
|
-
});
|
|
32441
|
-
} catch {
|
|
32442
|
-
throw new SkillError(
|
|
32443
|
-
"TRACKED_DOTENV_UNVERIFIABLE",
|
|
32444
|
-
"git could not report whether the .env file(s) in this directory are tracked. Refusing to use hot-wallet keys rather than assuming they are safe."
|
|
32445
|
-
);
|
|
32446
|
-
}
|
|
32447
|
-
const tracked = out.split("\0").filter(Boolean);
|
|
32522
|
+
const repo = findGitRepo(cwd);
|
|
32523
|
+
if (!repo) return { checked: true, tracked: false, candidates };
|
|
32524
|
+
const tracked = trackedAmong(repo, candidates.map((f) => path.join(cwd, f))).map(
|
|
32525
|
+
(f) => path.basename(f)
|
|
32526
|
+
);
|
|
32448
32527
|
if (tracked.length) {
|
|
32449
32528
|
throw new SkillError(
|
|
32450
32529
|
"TRACKED_DOTENV",
|
|
@@ -32458,7 +32537,7 @@ function assertNoTrackedDotEnv(cwd = process.cwd()) {
|
|
|
32458
32537
|
import fs3 from "node:fs";
|
|
32459
32538
|
import path3 from "node:path";
|
|
32460
32539
|
import os2 from "node:os";
|
|
32461
|
-
import
|
|
32540
|
+
import crypto3 from "node:crypto";
|
|
32462
32541
|
|
|
32463
32542
|
// node_modules/viem/_esm/index.js
|
|
32464
32543
|
init_keccak256();
|
|
@@ -32689,7 +32768,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
32689
32768
|
if (!n || !r || !p || !dklen || !salt) {
|
|
32690
32769
|
throw new SkillError("BAD_KEYSTORE", "The keystore scrypt parameters are incomplete.");
|
|
32691
32770
|
}
|
|
32692
|
-
derived =
|
|
32771
|
+
derived = crypto3.scryptSync(pass, Buffer.from(salt, "hex"), dklen, {
|
|
32693
32772
|
N: n,
|
|
32694
32773
|
r,
|
|
32695
32774
|
p,
|
|
@@ -32705,7 +32784,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
32705
32784
|
if (!iterations || !dklen || !salt) {
|
|
32706
32785
|
throw new SkillError("BAD_KEYSTORE", "The keystore pbkdf2 parameters are incomplete.");
|
|
32707
32786
|
}
|
|
32708
|
-
derived =
|
|
32787
|
+
derived = crypto3.pbkdf2Sync(pass, Buffer.from(salt, "hex"), iterations, dklen, "sha256");
|
|
32709
32788
|
} else {
|
|
32710
32789
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore KDF "${c.kdf}".`);
|
|
32711
32790
|
}
|
|
@@ -32720,7 +32799,7 @@ function decryptKeystoreV3(keystore, passphrase) {
|
|
|
32720
32799
|
if (c.cipher !== "aes-128-ctr") {
|
|
32721
32800
|
throw new SkillError("BAD_KEYSTORE", `Unsupported keystore cipher "${c.cipher}".`);
|
|
32722
32801
|
}
|
|
32723
|
-
const decipher =
|
|
32802
|
+
const decipher = crypto3.createDecipheriv(
|
|
32724
32803
|
"aes-128-ctr",
|
|
32725
32804
|
derived.subarray(0, 16),
|
|
32726
32805
|
Buffer.from(c.cipherparams.iv, "hex")
|
|
@@ -32742,7 +32821,7 @@ function timingSafeEqualHex(a, b) {
|
|
|
32742
32821
|
const ab = Buffer.from(String(a).toLowerCase(), "hex");
|
|
32743
32822
|
const bb = Buffer.from(String(b).toLowerCase(), "hex");
|
|
32744
32823
|
if (ab.length === 0 || ab.length !== bb.length) return false;
|
|
32745
|
-
return
|
|
32824
|
+
return crypto3.timingSafeEqual(ab, bb);
|
|
32746
32825
|
}
|
|
32747
32826
|
function looksLikeKeystore(text) {
|
|
32748
32827
|
const t = String(text).trimStart();
|
|
@@ -33292,7 +33371,7 @@ function checkExpiry({
|
|
|
33292
33371
|
// scripts/src/lib/blacklist.mjs
|
|
33293
33372
|
import fs4 from "node:fs";
|
|
33294
33373
|
import path4 from "node:path";
|
|
33295
|
-
import
|
|
33374
|
+
import crypto4 from "node:crypto";
|
|
33296
33375
|
import { fileURLToPath } from "node:url";
|
|
33297
33376
|
var BlacklistError = class extends Error {
|
|
33298
33377
|
constructor(code, message) {
|
|
@@ -33327,7 +33406,7 @@ function parseBlacklist(doc) {
|
|
|
33327
33406
|
}
|
|
33328
33407
|
addresses.push(e.address);
|
|
33329
33408
|
}
|
|
33330
|
-
const digest =
|
|
33409
|
+
const digest = crypto4.createHash("sha256").update(JSON.stringify(addresses), "utf8").digest("hex");
|
|
33331
33410
|
if (typeof provenance.addressesSha256 !== "string" || !provenance.addressesSha256) {
|
|
33332
33411
|
throw new BlacklistError("BLACKLIST_UNAVAILABLE", "Blacklist provenance digest is missing.");
|
|
33333
33412
|
}
|
|
@@ -33416,7 +33495,7 @@ function assertNotBlacklisted(targets, blacklist) {
|
|
|
33416
33495
|
import fs5 from "node:fs";
|
|
33417
33496
|
import path5 from "node:path";
|
|
33418
33497
|
import os3 from "node:os";
|
|
33419
|
-
import
|
|
33498
|
+
import crypto5 from "node:crypto";
|
|
33420
33499
|
var LOCK_STALE_MS = 6e4;
|
|
33421
33500
|
var LOCK_WAIT_MS = 1e4;
|
|
33422
33501
|
var LOCK_POLL_MS = 25;
|
|
@@ -33446,7 +33525,7 @@ function withLock(fn) {
|
|
|
33446
33525
|
try {
|
|
33447
33526
|
const age = Date.now() - fs5.statSync(file).mtimeMs;
|
|
33448
33527
|
if (age > LOCK_STALE_MS) {
|
|
33449
|
-
const stolen = `${file}.stale.${
|
|
33528
|
+
const stolen = `${file}.stale.${crypto5.randomBytes(4).toString("hex")}`;
|
|
33450
33529
|
try {
|
|
33451
33530
|
fs5.renameSync(file, stolen);
|
|
33452
33531
|
fs5.unlinkSync(stolen);
|
|
@@ -33487,7 +33566,7 @@ function statePath(rozoPaymentId) {
|
|
|
33487
33566
|
function writeAtomic(file, data) {
|
|
33488
33567
|
const dir = path5.dirname(file);
|
|
33489
33568
|
fs5.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
33490
|
-
const tmp = path5.join(dir, `.${path5.basename(file)}.${
|
|
33569
|
+
const tmp = path5.join(dir, `.${path5.basename(file)}.${crypto5.randomBytes(6).toString("hex")}.tmp`);
|
|
33491
33570
|
const fd = fs5.openSync(tmp, "wx", 384);
|
|
33492
33571
|
try {
|
|
33493
33572
|
fs5.writeFileSync(fd, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
@@ -33526,7 +33605,7 @@ function depositDigest(source) {
|
|
|
33526
33605
|
amountUnit: source?.amountUnit ?? null,
|
|
33527
33606
|
lnInvoice: source?.lnInvoice ?? null
|
|
33528
33607
|
});
|
|
33529
|
-
return
|
|
33608
|
+
return crypto5.createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
33530
33609
|
}
|
|
33531
33610
|
function claimSend(rozoPaymentId, intent, { skipCaps = false } = {}) {
|
|
33532
33611
|
return withLock(() => {
|