Skip to content

Commit 531d4f6

Browse files
Merge pull request #8493 from BitGo/claude/review-header-whitelist-JB0UY
feat: forward X-BitGo-OTP header from client requests to BitGo API
2 parents c671cfe + 7b1ab98 commit 531d4f6

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

modules/express/src/clientRoutes.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ const debug = debugLib('bitgo:express');
6969

7070
const BITGOEXPRESS_USER_AGENT = `BitGoExpress/${pjson.version} BitGoJS/${version}`;
7171

72+
/**
73+
* Headers from the incoming request that should be forwarded to the BitGo API.
74+
* Header names must be lowercase (Express normalizes incoming headers to lowercase).
75+
*/
76+
const FORWARDED_HEADERS = ['x-bitgo-otp'];
77+
7278
function handlePing(
7379
req: ExpressApiRouteRequest<'express.ping', 'get'>,
7480
res: express.Response,
@@ -1337,6 +1343,13 @@ export function redirectRequest(
13371343
request.set('enterprise-id', req.params.enterpriseId);
13381344
}
13391345

1346+
for (const header of FORWARDED_HEADERS) {
1347+
const value = req.headers[header];
1348+
if (value) {
1349+
request.set(header, Array.isArray(value) ? value[0] : value);
1350+
}
1351+
}
1352+
13401353
return request.result().then((result) => {
13411354
const status = request.res?.statusCode || 200;
13421355
return { status, body: result };

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('common methods', () => {
1616
req = {
1717
body: {},
1818
params: {},
19+
headers: {},
1920
bitgo,
2021
} as express.Request;
2122
next = () => undefined;
@@ -52,6 +53,36 @@ describe('common methods', () => {
5253
result.body.should.deepEqual({ success: true });
5354
});
5455

56+
it('should forward X-BitGo-OTP header when present', async () => {
57+
const url = 'https://example.com/api';
58+
const setStub = sandbox.stub();
59+
const response = { res: { statusCode: 200 }, result: async () => ({ success: true }), set: setStub };
60+
sandbox
61+
.stub(bitgo, 'get')
62+
.withArgs(url)
63+
.returns(response as any);
64+
65+
req.headers = { 'x-bitgo-otp': '123456' } as any;
66+
const result = await redirectRequest(bitgo, 'GET', url, req, next);
67+
result.status.should.equal(200);
68+
setStub.calledWith('x-bitgo-otp', '123456').should.be.true();
69+
});
70+
71+
it('should not forward X-BitGo-OTP header when not present', async () => {
72+
const url = 'https://example.com/api';
73+
const setStub = sandbox.stub();
74+
const response = { res: { statusCode: 200 }, result: async () => ({ success: true }), set: setStub };
75+
sandbox
76+
.stub(bitgo, 'get')
77+
.withArgs(url)
78+
.returns(response as any);
79+
80+
req.headers = {} as any;
81+
const result = await redirectRequest(bitgo, 'GET', url, req, next);
82+
result.status.should.equal(200);
83+
setStub.calledWith('x-bitgo-otp').should.be.false();
84+
});
85+
5586
it('should handle error response and return status and body', async () => {
5687
const url = 'https://example.com/api';
5788
const response = {

0 commit comments

Comments
 (0)