Skip to content

Commit ab3f306

Browse files
chore(express): remove leaked test wallet credentials
TICKET: WCI-1293
1 parent 606baf3 commit ab3f306

9 files changed

Lines changed: 57 additions & 79 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ coverage
2323
.claude/agents/*
2424
!.claude/agents/*.md
2525
.cursor/
26+
# Prevent committing encrypted private key fixtures generated by fetchEncryptedPrivKeys.ts
27+
encryptedPrivKeys.json
28+
modules/express/encryptedPrivKeys.json

modules/express/EXTERNAL_SIGNER.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ Here is an example json file containing two wallet IDs and their corresponding e
1818

1919
```
2020
{
21-
"61f039aad587c2000745c687373e0fa9":"{\"iv\":\"+1u1Y9cvsYuRMeyH2slnXQ==\",\"v\":1,\"iter\":10000,\"ks\":256,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"54kOXTqJ9mc=\",\"ct\":\"JF5wQ82wa1dYyFxFlbHCvK4a+A6MTHdhOqc5uXsz2icWhkY2Lin/3Ab8ZwvwDaR1JYKmC/g1gXIGwVZEOl1M/bRHY420h7sDtmTS6Ebse5NWbF0ItfUJlk6HVATGa+C6mkbaVxJ4kQW/ehnT3riqzU069ATPz8E=\"}",
22-
"61fb21819c54dd000755f8de3a18e46f":"{\"iv\":\"ULAkh1Ia2B2oJbVWRt+xMw==\",\"v\":1,\"iter\":10000,\"ks\":256,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"SVkVei5M1qU=\",\"ct\":\"NxfG1HQWGcrwCHkQh8DKeMaZrRic+SSBQHtuOSsSJzW5MDOpwqDta8PDdh52lp9eqtaY+CGN6rPhaGbeZDrEyV2PoBGeb48GicMTVAehkyoF9mr8edtsWDCxcmmde+1zv3czy2n/bgXYNGvX39D30GDRpfovSYc=\"}"
21+
"<walletId1>":"<encryptedPrivateKey1>",
22+
"<walletId2>":"<encryptedPrivateKey2>"
2323
}
2424
```
2525

@@ -71,8 +71,8 @@ Set as an environment variable in the format `WALLET_<walletId>_PASSPHRASE`. Not
7171
The environment variable can be set using `export`. For example, the wallet passphrases for the private keys above can be set with the following:
7272

7373
```
74-
export WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE=wDX058%c4plL1@pP
75-
export WALLET_61fb21819c54dd000755f8de3a18e46f_PASSPHRASE=wDX058%c4plL1@pP
74+
export WALLET_<walletId1>_PASSPHRASE=<YOUR_WALLET_PASSPHRASE>
75+
export WALLET_<walletId2>_PASSPHRASE=<YOUR_WALLET_PASSPHRASE>
7676
```
7777

7878
### External signer mode configuration values

modules/express/encryptedPrivKeys.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

modules/express/test/unit/bitgoExpress.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ proxyquire.noPreserveCache();
2727
describe('Bitgo Express', function () {
2828
describe('server initialization', function () {
2929
const validPrvJSON =
30-
'{"61f039aad587c2000745c687373e0fa9":"xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2"}';
30+
'{"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6":"xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2"}';
3131
const validLightningSignerConfigJSON = '{"fakeid":{"url": "https://127.0.0.1:8080","tlsCert":"dummy"}}';
3232

3333
it('should require NODE_ENV to be production when running against prod env', function () {

modules/express/test/unit/clientRoutes/externalSign.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,11 @@ describe('External signer', () => {
8484
let mpcEcdsa: Ecdsa;
8585
let hdTree: HDTree;
8686

87-
const walletId = '61f039aad587c2000745c687373e0fa9';
88-
const walletPassword = 'wDX058%c4plL1@pP';
87+
const walletId = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6';
88+
const walletPassword = 'test-wallet-passphrase';
8989
const secret =
9090
'xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2';
91-
const validPrv =
92-
'{"61f039aad587c2000745c687373e0fa9":"{\\"iv\\":\\"+1u1Y9cvsYuRMeyH2slnXQ==\\",\\"v\\":1,\\"iter\\":10000,\\"ks\\":256,\\"ts\\":64,\\"mode\\":\\"ccm\\",\\"adata\\":\\"\\",\\"cipher\\":\\"aes\\",\\"salt\\":\\"54kOXTqJ9mc=\\",\\"ct\\":\\"JF5wQ82wa1dYyFxFlbHCvK4a+A6MTHdhOqc5uXsz2icWhkY2Lin/3Ab8ZwvwDaR1JYKmC/g1gXIGwVZEOl1M/bRHY420h7sDtmTS6Ebse5NWbF0ItfUJlk6HVATGa+C6mkbaVxJ4kQW/ehnT3riqzU069ATPz8E=\\"}"}';
91+
let validPrv: string;
9392

9493
before(async function () {
9594
if (!nock.isActive()) {
@@ -99,6 +98,13 @@ describe('External signer', () => {
9998
bitgo = TestBitGo.decorate(BitGo, { env: 'test' });
10099
bitgo.initializeTestVars();
101100

101+
const encryptedPrv = await bitgo.encrypt({
102+
password: walletPassword,
103+
input: secret,
104+
encryptionVersion: 1,
105+
});
106+
validPrv = JSON.stringify({ [walletId]: encryptedPrv });
107+
102108
bgUrl = common.Environments[bitgo.getEnv()].uri;
103109
hdTree = await Ed25519BIP32.initialize();
104110
MPC = await Eddsa.initialize(hdTree);
@@ -122,9 +128,7 @@ describe('External signer', () => {
122128

123129
it('should read an encrypted prv from signerFileSystemPath and pass it to coin.signTransaction', async () => {
124130
const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(validPrv);
125-
const envStub = sinon
126-
.stub(process, 'env')
127-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: walletPassword });
131+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: walletPassword });
128132
const signTransactionStub = sinon
129133
.stub(Coin.Btc.prototype, 'signTransaction')
130134
.resolves({ txHex: 'signedTx', txRequestId: '' } as SignedTransaction);

modules/express/test/unit/clientRoutes/signPayload.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,11 @@ describe('Sign an arbitrary payload with trading account key', function () {
142142
describe('With the handler to sign an arbitrary payload in external signing mode', () => {
143143
let bitgo: BitGo;
144144

145-
const walletId = '61f039aad587c2000745c687373e0fa9';
146-
const walletPassword = 'wDX058%c4plL1@pP';
145+
const walletId = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6';
146+
const walletPassword = 'test-wallet-passphrase';
147147
const secret =
148148
'xprv9s21ZrQH143K3EuPWCBuqnWxydaQV6et9htQige4EswvcHKEzNmkVmwTwKoadyHzJYppuADB7Us7AbaNLToNvoFoSxuWqndQRYtnNy5DUY2';
149-
const validPrv =
150-
'{"61f039aad587c2000745c687373e0fa9":"{\\"iv\\":\\"+1u1Y9cvsYuRMeyH2slnXQ==\\",\\"v\\":1,\\"iter\\":10000,\\"ks\\":256,\\"ts\\":64,\\"mode\\":\\"ccm\\",\\"adata\\":\\"\\",\\"cipher\\":\\"aes\\",\\"salt\\":\\"54kOXTqJ9mc=\\",\\"ct\\":\\"JF5wQ82wa1dYyFxFlbHCvK4a+A6MTHdhOqc5uXsz2icWhkY2Lin/3Ab8ZwvwDaR1JYKmC/g1gXIGwVZEOl1M/bRHY420h7sDtmTS6Ebse5NWbF0ItfUJlk6HVATGa+C6mkbaVxJ4kQW/ehnT3riqzU069ATPz8E=\\"}"}';
149+
let validPrv: string;
151150

152151
const payload = {
153152
this: {
@@ -157,16 +156,20 @@ describe('With the handler to sign an arbitrary payload in external signing mode
157156
},
158157
};
159158

160-
before(() => {
159+
before(async () => {
161160
bitgo = new BitGo({ env: 'test' });
161+
const encryptedPrv = await bitgo.encrypt({
162+
password: walletPassword,
163+
input: secret,
164+
encryptionVersion: 1,
165+
});
166+
validPrv = JSON.stringify({ [walletId]: encryptedPrv });
162167
});
163168

164169
it('should return a payload signed with trading account key read from the local file system', async () => {
165170
const stubbedSignature = Buffer.from('mysign');
166171
const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(validPrv);
167-
const envStub = sinon
168-
.stub(process, 'env')
169-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: walletPassword });
172+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: walletPassword });
170173

171174
const signMessageStub = sinon.stub(Coin.Ofc.prototype, 'signMessage').resolves(stubbedSignature);
172175

@@ -248,9 +251,7 @@ describe('With the handler to sign an arbitrary payload in external signing mode
248251
it('should prioritize request body passphrase over environment variable', async () => {
249252
const stubbedSignature = Buffer.from('mysign');
250253
const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(validPrv);
251-
const envStub = sinon
252-
.stub(process, 'env')
253-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: walletPassword });
254+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: walletPassword });
254255

255256
const signMessageStub = sinon.stub(Coin.Ofc.prototype, 'signMessage').resolves(stubbedSignature);
256257

@@ -290,7 +291,7 @@ describe('With the handler to sign an arbitrary payload in external signing mode
290291
});
291292

292293
describe('With invalid setup', () => {
293-
const invalidPrv = '{"61f039aad587c2000745c687373e0fa9":"invalid"}';
294+
const invalidPrv = JSON.stringify({ [walletId]: 'invalid' });
294295

295296
it('should throw an error with missing wallet passphrase in env', async () => {
296297
const req = {
@@ -306,14 +307,12 @@ describe('With the handler to sign an arbitrary payload in external signing mode
306307
} as unknown as ExpressApiRouteRequest<'express.v2.ofc.extSignPayload', 'post'>;
307308

308309
await handleV2OFCSignPayloadInExtSigningMode(req).should.be.rejectedWith(
309-
'Could not find wallet passphrase WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE in environment'
310+
`Could not find wallet passphrase WALLET_${walletId}_PASSPHRASE in environment`
310311
);
311312
});
312313

313314
it('should throw an error with undefined signerFileSystemPath in env', async () => {
314-
const envStub = sinon
315-
.stub(process, 'env')
316-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: walletPassword });
315+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: walletPassword });
317316

318317
const req = {
319318
bitgo,
@@ -338,9 +337,7 @@ describe('With the handler to sign an arbitrary payload in external signing mode
338337

339338
it('should throw error when trying to decrypt with invalid private key', async () => {
340339
const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(invalidPrv);
341-
const envStub = sinon
342-
.stub(process, 'env')
343-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: walletPassword });
340+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: walletPassword });
344341

345342
const req = {
346343
bitgo,
@@ -367,9 +364,7 @@ describe('With the handler to sign an arbitrary payload in external signing mode
367364

368365
it('should throw error when trying to decrypt with invalid wallet passphrase key', async () => {
369366
const readFileStub = sinon.stub(fs.promises, 'readFile').resolves(validPrv);
370-
const envStub = sinon
371-
.stub(process, 'env')
372-
.value({ WALLET_61f039aad587c2000745c687373e0fa9_PASSPHRASE: 'invalidPassphrase' });
367+
const envStub = sinon.stub(process, 'env').value({ [`WALLET_${walletId}_PASSPHRASE`]: 'invalidPassphrase' });
373368

374369
const req = {
375370
bitgo,

modules/express/test/unit/typedRoutes/coinSign.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,42 +30,32 @@ describe('CoinSign codec tests (External Signer Mode)', function () {
3030
const decryptedPrivKey =
3131
'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2';
3232
const walletPassphrase = 'test_wallet_passphrase';
33-
// Use the existing encryptedPrivKeys.json file in the project root
3433
const path = require('path');
35-
const signerFilePath = path.join(__dirname, '../../../encryptedPrivKeys.json');
34+
const os = require('os');
35+
const signerFilePath = path.join(os.tmpdir(), `encryptedPrivKeys-${process.pid}-${Date.now()}.json`);
3636

3737
let fsReadFileStub: sinon.SinonStub;
3838
let agent: ReturnType<typeof setupAgent>;
39-
let originalFileContent: string;
4039

41-
// Mock encrypted private keys JSON content
4240
const mockSignerFileContent = JSON.stringify({
4341
[walletId]: encryptedPrivKey,
4442
});
4543

46-
// Setup the express app with signer mode before all tests
4744
before(function () {
48-
// Save the original content of encryptedPrivKeys.json
49-
try {
50-
originalFileContent = fsSync.readFileSync(signerFilePath, 'utf8');
51-
} catch (e) {
52-
originalFileContent = '{}';
53-
}
54-
55-
// Temporarily write mock data to the existing file
5645
fsSync.writeFileSync(signerFilePath, mockSignerFileContent);
5746

58-
// Create agent with signerMode enabled for external signing
5947
agent = setupAgent({
6048
signerMode: true,
6149
signerFileSystemPath: signerFilePath,
6250
});
6351
});
6452

65-
// Restore the original file content after all tests
6653
after(function () {
67-
// Restore original content
68-
fsSync.writeFileSync(signerFilePath, originalFileContent);
54+
try {
55+
fsSync.unlinkSync(signerFilePath);
56+
} catch (e) {
57+
// ignore
58+
}
6959
});
7060

7161
beforeEach(function () {

modules/express/test/unit/typedRoutes/generateShareTSS.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,40 +39,31 @@ describe('GenerateShareTSS codec tests (External Signer Mode)', function () {
3939
'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2';
4040
const walletPassphrase = 'test_wallet_passphrase';
4141
const path = require('path');
42-
const signerFilePath = path.join(__dirname, '../../../encryptedPrivKeys.json');
42+
const os = require('os');
43+
const signerFilePath = path.join(os.tmpdir(), `encryptedPrivKeys-${process.pid}-${Date.now()}.json`);
4344

4445
let fsReadFileStub: sinon.SinonStub;
4546
let agent: ReturnType<typeof setupAgent>;
46-
let originalFileContent: string;
4747

48-
// Mock encrypted private keys JSON content
4948
const mockSignerFileContent = JSON.stringify({
5049
[walletId]: encryptedPrivKey,
5150
});
5251

53-
// Setup the express app with signer mode before all tests
5452
before(function () {
55-
// Save the original content of encryptedPrivKeys.json
56-
try {
57-
originalFileContent = fsSync.readFileSync(signerFilePath, 'utf8');
58-
} catch (e) {
59-
originalFileContent = '{}';
60-
}
61-
62-
// Temporarily write mock data to the existing file
6353
fsSync.writeFileSync(signerFilePath, mockSignerFileContent);
6454

65-
// Create agent with signerMode enabled for external signing
6655
agent = setupAgent({
6756
signerMode: true,
6857
signerFileSystemPath: signerFilePath,
6958
});
7059
});
7160

72-
// Restore the original file content after all tests
7361
after(function () {
74-
// Restore original content
75-
fsSync.writeFileSync(signerFilePath, originalFileContent);
62+
try {
63+
fsSync.unlinkSync(signerFilePath);
64+
} catch (e) {
65+
// ignore
66+
}
7667
});
7768

7869
beforeEach(function () {

modules/express/test/unit/typedRoutes/ofcExtSignPayload.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () {
1919
const walletPassphrase = 'test_wallet_passphrase_ofc';
2020

2121
const path = require('path');
22-
const signerFilePath = path.join(__dirname, '../../../encryptedPrivKeys.json');
22+
const os = require('os');
23+
const signerFilePath = path.join(os.tmpdir(), `encryptedPrivKeys-${process.pid}-${Date.now()}.json`);
2324

2425
let fsReadFileStub: sinon.SinonStub;
2526
let agent: ReturnType<typeof setupAgent>;
26-
let originalFileContent: string;
2727

2828
const mockSignerFileContent = JSON.stringify({
2929
[walletId]: encryptedPrivKey,
@@ -36,12 +36,6 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () {
3636
};
3737

3838
before(function () {
39-
try {
40-
originalFileContent = fsSync.readFileSync(signerFilePath, 'utf8');
41-
} catch (e) {
42-
originalFileContent = '{}';
43-
}
44-
4539
fsSync.writeFileSync(signerFilePath, mockSignerFileContent);
4640

4741
agent = setupAgent({
@@ -51,7 +45,11 @@ describe('OfcExtSignPayload External Signer Mode Tests', function () {
5145
});
5246

5347
after(function () {
54-
fsSync.writeFileSync(signerFilePath, originalFileContent);
48+
try {
49+
fsSync.unlinkSync(signerFilePath);
50+
} catch (e) {
51+
// ignore
52+
}
5553
});
5654

5755
beforeEach(function () {

0 commit comments

Comments
 (0)