From 81e791fc4cbf96c992cb1b015dfc3a480cb46b48 Mon Sep 17 00:00:00 2001 From: Sikkra <159844544+Sikkra@users.noreply.github.com> Date: Tue, 19 May 2026 13:22:50 -0500 Subject: [PATCH] Remove hardcoded testnet strategy secret --- .gitignore | 3 +++ SECURITY-TODO.md | 3 +++ scripts/deploy_strategy.ts | 37 +++++++++++++++++++++++++++++++++---- scripts/package.json | 1 + scripts/test_strategy.ts | 36 +++++++++++++++++++++++++++++++++--- 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 SECURITY-TODO.md diff --git a/.gitignore b/.gitignore index 1a2758a..f2aaf22 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ src/.DS_Store # Never commit private keys *.key *.secret +.env.local +scripts/.env.local +*.env.local frontend/node_modules/ frontend/dist/ diff --git a/SECURITY-TODO.md b/SECURITY-TODO.md new file mode 100644 index 0000000..77c4123 --- /dev/null +++ b/SECURITY-TODO.md @@ -0,0 +1,3 @@ +# Security TODO + +- 2026-05-19: Removed the committed testnet deployer seed from strategy scripts and retired it from all scripted deploy/test flows. Treat the old testnet key as compromised and do not fund or reuse it. Generate and fund a fresh testnet account, then keep the replacement seed only in `TESTNET_SECRET` via a local `.env.local` file or shell environment. diff --git a/scripts/deploy_strategy.ts b/scripts/deploy_strategy.ts index 8320de4..bc0786a 100644 --- a/scripts/deploy_strategy.ts +++ b/scripts/deploy_strategy.ts @@ -1,7 +1,9 @@ /** * Deploy the BlendLeverageStrategy contract to testnet. * - * Usage: npx tsx scripts/deploy_strategy.ts + * Usage: + * TESTNET_SECRET=S... npx tsx scripts/deploy_strategy.ts + * # or put TESTNET_SECRET=S... in .env.local / scripts/.env.local */ import { BASE_FEE, @@ -14,16 +16,43 @@ import { xdr, Address, nativeToScVal, - StrKey, } from "@stellar/stellar-sdk"; import * as fs from "fs"; import * as crypto from "crypto"; const RPC_URL = "https://soroban-testnet.stellar.org"; const PASSPHRASE = Networks.TESTNET; -const SECRET = "SCX6RZDDWLKWLSUEKDROH3PCXDPAVVJ355YA4FEHQB3MJMA6K762E527"; -const keypair = Keypair.fromSecret(SECRET); +function loadEnvLocal() { + for (const file of [".env.local", "scripts/.env.local"]) { + if (!fs.existsSync(file)) continue; + const lines = fs.readFileSync(file, "utf8").split(/\r?\n/); + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed); + if (!match || process.env[match[1]]) continue; + process.env[match[1]] = match[2].replace(/^['"]|['"]$/g, ""); + } + } +} + +function getTestnetKeypair(): Keypair { + loadEnvLocal(); + const secret = process.env.TESTNET_SECRET?.trim(); + if (!secret) { + console.error("Missing TESTNET_SECRET. Add it to your shell env or local .env.local file; never commit secret keys."); + process.exit(1); + } + try { + return Keypair.fromSecret(secret); + } catch { + console.error("Invalid TESTNET_SECRET. Expected a Stellar secret seed beginning with S."); + process.exit(1); + } +} + +const keypair = getTestnetKeypair(); const account = keypair.publicKey(); const server = new SorobanRpc.Server(RPC_URL); diff --git a/scripts/package.json b/scripts/package.json index 5bf05c0..0f5bd0a 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -10,6 +10,7 @@ "@stellar/stellar-sdk": "^14.6.1" }, "devDependencies": { + "@types/node": "^25.9.1", "tsx": "^4.19.2", "typescript": "^5.7.3" } diff --git a/scripts/test_strategy.ts b/scripts/test_strategy.ts index 560cc58..903d190 100644 --- a/scripts/test_strategy.ts +++ b/scripts/test_strategy.ts @@ -8,7 +8,8 @@ * 4. Withdraw * * Usage: - * npx tsx scripts/test_strategy.ts + * TESTNET_SECRET=S... npx tsx scripts/test_strategy.ts + * # or put TESTNET_SECRET=S... in .env.local / scripts/.env.local */ import { @@ -26,6 +27,7 @@ import { nativeToScVal, scValToNative, } from "@stellar/stellar-sdk"; +import * as fs from "fs"; // ── Constants ──────────────────────────────────────────────────────────────── @@ -38,8 +40,36 @@ const RPC_URL = "https://soroban-testnet.stellar.org"; const HORIZON_URL = "https://horizon-testnet.stellar.org"; const PASSPHRASE = Networks.TESTNET; -const SECRET = "SCX6RZDDWLKWLSUEKDROH3PCXDPAVVJ355YA4FEHQB3MJMA6K762E527"; -const keypair = Keypair.fromSecret(SECRET); +function loadEnvLocal() { + for (const file of [".env.local", "scripts/.env.local"]) { + if (!fs.existsSync(file)) continue; + const lines = fs.readFileSync(file, "utf8").split(/\r?\n/); + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const match = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed); + if (!match || process.env[match[1]]) continue; + process.env[match[1]] = match[2].replace(/^['"]|['"]$/g, ""); + } + } +} + +function getTestnetKeypair(): Keypair { + loadEnvLocal(); + const secret = process.env.TESTNET_SECRET?.trim(); + if (!secret) { + console.error("Missing TESTNET_SECRET. Add it to your shell env or local .env.local file; never commit secret keys."); + process.exit(1); + } + try { + return Keypair.fromSecret(secret); + } catch { + console.error("Invalid TESTNET_SECRET. Expected a Stellar secret seed beginning with S."); + process.exit(1); + } +} + +const keypair = getTestnetKeypair(); const account = keypair.publicKey(); const server = new SorobanRpc.Server(RPC_URL);