Skip to content

Commit 3c59ffe

Browse files
feat: capture raw body bytes for v4 hmac calculation
TICKET: CAAS-659
1 parent 8b4a279 commit 3c59ffe

File tree

4 files changed

+465
-6
lines changed

4 files changed

+465
-6
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,23 @@ async function handleNetworkV1EnterpriseClientConnections(
11921192
return handleProxyReq(req, res, next);
11931193
}
11941194

1195+
/**
1196+
* Helper to send request body, using raw bytes when available.
1197+
* For v4 HMAC authentication, we need to send the exact bytes that were
1198+
* received from the client to ensure the HMAC signature matches.
1199+
*
1200+
* @param request - The superagent request object
1201+
* @param req - The Express request containing body and rawBodyBuffer
1202+
* @returns The request with body attached
1203+
*/
1204+
function sendRequestBody(request: ReturnType<BitGo['post']>, req: express.Request) {
1205+
if (req.rawBodyBuffer && req.rawBodyBuffer.length > 0) {
1206+
return request.set('Content-Type', 'application/json').send(req.rawBodyBuffer);
1207+
}
1208+
// Fall back to parsed body for backward compatibility
1209+
return request.send(req.body);
1210+
}
1211+
11951212
/**
11961213
* Redirect a request using the bitgo request functions.
11971214
* @param bitgo
@@ -1214,19 +1231,19 @@ export function redirectRequest(
12141231
request = bitgo.get(url);
12151232
break;
12161233
case 'POST':
1217-
request = bitgo.post(url).send(req.body);
1234+
request = sendRequestBody(bitgo.post(url), req);
12181235
break;
12191236
case 'PUT':
1220-
request = bitgo.put(url).send(req.body);
1237+
request = sendRequestBody(bitgo.put(url), req);
12211238
break;
12221239
case 'PATCH':
1223-
request = bitgo.patch(url).send(req.body);
1240+
request = sendRequestBody(bitgo.patch(url), req);
12241241
break;
12251242
case 'OPTIONS':
1226-
request = bitgo.options(url).send(req.body);
1243+
request = sendRequestBody(bitgo.options(url), req);
12271244
break;
12281245
case 'DELETE':
1229-
request = bitgo.del(url).send(req.body);
1246+
request = sendRequestBody(bitgo.del(url), req);
12301247
break;
12311248
}
12321249

modules/express/src/expressApp.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,18 @@ export function app(cfg: Config): express.Application {
302302
checkPreconditions(cfg);
303303
debug('preconditions satisfied');
304304

305-
app.use(bodyParser.json({ limit: '20mb' }));
305+
app.use(
306+
bodyParser.json({
307+
limit: '20mb',
308+
verify: (req, res, buf) => {
309+
// Store the raw body buffer on the request object.
310+
// This preserves the exact bytes before JSON parsing,
311+
// which may alter whitespace, key ordering, etc.
312+
// Required for v4 HMAC authentication.
313+
(req as express.Request).rawBodyBuffer = buf;
314+
},
315+
})
316+
);
306317

307318
// Be more robust about accepting URLs with double slashes
308319
app.use(function replaceUrlSlashes(req, res, next) {

0 commit comments

Comments
 (0)