Skip to content

Commit 2e67bdd

Browse files
authored
Merge pull request #91 from BitGo/WP-5319/hide-recovery-api-ebe-v2
feat(ebe): hide recovery api under non recovery mode
2 parents 4d04e20 + 2f53b04 commit 2e67bdd

File tree

7 files changed

+90
-3
lines changed

7 files changed

+90
-3
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'should';
2+
import * as request from 'supertest';
3+
import nock from 'nock';
4+
import { app as expressApp } from '../../../enclavedApp';
5+
import { AppMode, EnclavedConfig, TlsMode } from '../../../shared/types';
6+
import sinon from 'sinon';
7+
import * as middleware from '../../../shared/middleware';
8+
import { BitGoRequest } from '../../../types/request';
9+
import { BitGoAPI as BitGo } from '@bitgo-beta/sdk-api';
10+
11+
describe('Non Recovery', () => {
12+
let agent: request.SuperAgentTest;
13+
const coin = 'tbtc';
14+
const config: EnclavedConfig = {
15+
appMode: AppMode.ENCLAVED,
16+
port: 0,
17+
bind: 'localhost',
18+
timeout: 60000,
19+
tlsMode: TlsMode.DISABLED,
20+
httpLoggerFile: '',
21+
allowSelfSigned: true,
22+
kmsUrl: 'kms.example.com',
23+
};
24+
25+
beforeEach(() => {
26+
nock.disableNetConnect();
27+
nock.enableNetConnect('127.0.0.1');
28+
29+
// Initialize BitGo with test environment
30+
const bitgo = new BitGo({
31+
env: 'test',
32+
accessToken: 'test_token',
33+
});
34+
35+
// Setup middleware stubs before creating app
36+
sinon.stub(middleware, 'prepareBitGo').callsFake(() => (req, res, next) => {
37+
(req as BitGoRequest<EnclavedConfig>).bitgo = bitgo;
38+
(req as BitGoRequest<EnclavedConfig>).config = config;
39+
next();
40+
});
41+
42+
// Create app after middleware is stubbed
43+
const app = expressApp(config);
44+
agent = request.agent(app);
45+
});
46+
47+
afterEach(() => {
48+
nock.cleanAll();
49+
sinon.restore();
50+
});
51+
52+
it('should fail to run ebe recovery if not in recovery mode', async () => {
53+
const userPub = 'xpub_user';
54+
const backupPub = 'xpub_backup';
55+
const bitgoPub = 'xpub_bitgo';
56+
57+
const response = await agent.post(`/api/${coin}/multisig/recovery`).send({
58+
userPub,
59+
backupPub,
60+
bitgoPub,
61+
unsignedSweepPrebuildTx: {},
62+
walletContractAddress: '',
63+
coin,
64+
});
65+
response.status.should.equal(500);
66+
response.body.should.have.property('error');
67+
response.body.should.have.property('details');
68+
response.body.details.should.containEql(
69+
'Recovery operations are not enabled. The server must be in recovery mode to perform this action.',
70+
);
71+
});
72+
});

src/__tests__/api/enclaved/recoveryMultisigTransaction.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('UTXO recovery', () => {
2222
tlsMode: TlsMode.DISABLED,
2323
allowSelfSigned: true,
2424
kmsUrl: 'kms.example.com',
25+
recoveryMode: true,
2526
};
2627

2728
beforeEach(() => {

src/__tests__/api/enclaved/recoveryMusigEth.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ describe('recoveryMultisigTransaction', () => {
4040
kmsUrl: kmsUrl,
4141
tlsMode: TlsMode.DISABLED,
4242
allowSelfSigned: true,
43+
recoveryMode: true,
4344
};
4445

4546
configStub = sinon.stub(configModule, 'initConfig').returns(cfg);

src/__tests__/api/enclaved/signMpcRecoveryTransaction.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('EdDSA Recovery Signing', () => {
1818
httpLoggerFile: '',
1919
tlsMode: TlsMode.DISABLED,
2020
allowSelfSigned: true,
21+
recoveryMode: true,
2122
};
2223

2324
const commonKeychain =

src/api/enclaved/handlers/recoveryMultisigTransaction.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
TransactionRecipient,
77
} from '@bitgo-beta/sdk-core';
88
import { EnclavedApiSpecRouteRequest } from '../../../enclavedBitgoExpress/routers/enclavedApiSpec';
9-
import { EnvironmentName } from '../../../initConfig';
9+
import { EnclavedConfig, EnvironmentName } from '../../../initConfig';
1010
import logger from '../../../logger';
1111
import {
1212
isEthLikeCoin,
@@ -19,12 +19,14 @@ import {
1919
getReplayProtectionOptions,
2020
} from '../../../shared/recoveryUtils';
2121
import { SignedEthLikeRecoveryTx } from '../../../types/transaction';
22-
import { retrieveKmsPrvKey } from '../utils';
22+
import { checkRecoveryMode, retrieveKmsPrvKey } from '../utils';
2323
import coinFactory from '../../../shared/coinFactory';
2424

2525
export async function recoveryMultisigTransaction(
2626
req: EnclavedApiSpecRouteRequest<'v1.multisig.recovery', 'post'>,
2727
): Promise<any> {
28+
checkRecoveryMode(req.config as EnclavedConfig);
29+
2830
const { userPub, backupPub, bitgoPub, unsignedSweepPrebuildTx, walletContractAddress, coin } =
2931
req.decoded;
3032

src/api/enclaved/handlers/signEddsaRecoveryTransaction.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { Ed25519Bip32HdTree } from '@bitgo-beta/sdk-lib-mpc';
1111
import { CoinFamily, coins } from '@bitgo-beta/statics';
1212
import { type KeyPair as SolKeyPair } from '@bitgo-beta/sdk-coin-sol';
13-
import { retrieveKmsPrvKey } from '../utils';
13+
import { checkRecoveryMode, retrieveKmsPrvKey } from '../utils';
1414
import { EnclavedConfig } from '../../../shared/types';
1515
import logger from '../../../logger';
1616

@@ -88,6 +88,8 @@ export async function signEddsaRecoveryTransaction({
8888
let publicKey = '';
8989
logger.info(`Received request ${JSON.stringify(request)}`);
9090

91+
checkRecoveryMode(cfg);
92+
9193
const hdTree = await Ed25519Bip32HdTree.initialize();
9294
const MPC = await Eddsa.initialize(hdTree);
9395

src/api/enclaved/utils.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,11 @@ export async function decryptDataKey({
116116
};
117117
}
118118
}
119+
120+
export function checkRecoveryMode(config: EnclavedConfig) {
121+
if (!config.recoveryMode) {
122+
throw new Error(
123+
'Recovery operations are not enabled. The server must be in recovery mode to perform this action.',
124+
);
125+
}
126+
}

0 commit comments

Comments
 (0)