Skip to content

Commit dcf154f

Browse files
authored
Merge pull request #1209 from fabiovincenzi/duplicate-executechain
refactor: eliminate duplicate executeChain calls in push approval flow
2 parents cbc7ad6 + afece3a commit dcf154f

File tree

2 files changed

+25
-54
lines changed

2 files changed

+25
-54
lines changed

src/proxy/routes/index.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ const proxyFilter: ProxyOptions['filter'] = async (req, res) => {
4848
return false;
4949
}
5050

51+
// For POST pack requests, use the raw body extracted by extractRawBody middleware
52+
if (isPackPost(req) && (req as any).bodyRaw) {
53+
(req as any).body = (req as any).bodyRaw;
54+
// Clean up the bodyRaw property before forwarding the request
55+
delete (req as any).bodyRaw;
56+
}
57+
5158
const action = await executeChain(req, res);
5259

5360
if (action.error || action.blocked) {
@@ -131,7 +138,7 @@ const proxyReqBodyDecorator: ProxyOptions['proxyReqBodyDecorator'] = (bodyConten
131138
return bodyContent;
132139
};
133140

134-
const proxyErrorHandler: ProxyOptions['proxyErrorHandler'] = (err, res, next) => {
141+
const proxyErrorHandler: ProxyOptions['proxyErrorHandler'] = (err, _res, next) => {
135142
console.log(`ERROR=${err}`);
136143
next(err);
137144
};
@@ -140,42 +147,24 @@ const isPackPost = (req: Request) =>
140147
req.method === 'POST' &&
141148
/^(?:\/[^/]+)*\/[^/]+\.git\/(?:git-upload-pack|git-receive-pack)$/.test(req.url);
142149

143-
const teeAndValidate = async (req: Request, res: Response, next: NextFunction) => {
150+
const extractRawBody = async (req: Request, res: Response, next: NextFunction) => {
144151
if (!isPackPost(req)) {
145152
return next();
146153
}
147154

148-
const proxyStream = new PassThrough();
149-
const pluginStream = new PassThrough();
155+
const proxyStream = new PassThrough({
156+
highWaterMark: 4 * 1024 * 1024,
157+
});
158+
const pluginStream = new PassThrough({
159+
highWaterMark: 4 * 1024 * 1024,
160+
});
150161

151162
req.pipe(proxyStream);
152163
req.pipe(pluginStream);
153164

154165
try {
155166
const buf = await getRawBody(pluginStream, { limit: '1gb' });
156-
(req as any).body = buf;
157-
const verdict = await executeChain(req, res);
158-
if (verdict.error || verdict.blocked) {
159-
const message = verdict.errorMessage ?? verdict.blockedMessage ?? 'Unknown error';
160-
const type = verdict.error ? ActionType.ERROR : ActionType.BLOCKED;
161-
162-
logAction(req.url, req.headers?.host, req.headers?.['user-agent'], type, message);
163-
164-
res
165-
.set({
166-
'content-type': 'application/x-git-receive-pack-result',
167-
expires: 'Fri, 01 Jan 1980 00:00:00 GMT',
168-
pragma: 'no-cache',
169-
'cache-control': 'no-cache, max-age=0, must-revalidate',
170-
vary: 'Accept-Encoding',
171-
'x-frame-options': 'DENY',
172-
connection: 'close',
173-
})
174-
.status(200) // return status 200 to ensure that the error message is rendered by the git client
175-
.send(handleMessage(message));
176-
return;
177-
}
178-
167+
(req as any).bodyRaw = buf;
179168
(req as any).pipe = (dest: any, opts: any) => proxyStream.pipe(dest, opts);
180169
next();
181170
} catch (e) {
@@ -187,7 +176,7 @@ const teeAndValidate = async (req: Request, res: Response, next: NextFunction) =
187176

188177
const getRouter = async () => {
189178
const router = Router();
190-
router.use(teeAndValidate);
179+
router.use(extractRawBody);
191180

192181
const originsToProxy = await getAllProxiedHosts();
193182
const proxyKeys: string[] = [];
@@ -262,6 +251,6 @@ export {
262251
handleMessage,
263252
handleRefsErrorMessage,
264253
isPackPost,
265-
teeAndValidate,
254+
extractRawBody,
266255
validGitRequest,
267256
};

test/teeAndValidation.test.js renamed to test/extractRawBody.test.js

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ const fakeChain = {
99
executeChain: sinon.stub(),
1010
};
1111

12-
const { teeAndValidate, isPackPost, handleMessage } = proxyquire('../src/proxy/routes', {
12+
const { extractRawBody, isPackPost } = proxyquire('../src/proxy/routes', {
1313
'raw-body': fakeRawBody,
1414
'../chain': fakeChain,
1515
});
1616

17-
describe('teeAndValidate middleware', () => {
17+
describe('extractRawBody middleware', () => {
1818
let req;
1919
let res;
2020
let next;
@@ -38,39 +38,21 @@ describe('teeAndValidate middleware', () => {
3838

3939
it('skips non-pack posts', async () => {
4040
req.method = 'GET';
41-
await teeAndValidate(req, res, next);
41+
await extractRawBody(req, res, next);
4242
expect(next.calledOnce).to.be.true;
4343
expect(fakeRawBody.called).to.be.false;
4444
});
4545

46-
it('when the chain blocks it sends a packet and does NOT call next()', async () => {
47-
fakeChain.executeChain.resolves({ blocked: true, blockedMessage: 'denied!' });
48-
49-
req.write('abcd');
50-
req.end();
51-
52-
await teeAndValidate(req, res, next);
53-
54-
expect(fakeRawBody.calledOnce).to.be.true;
55-
expect(fakeChain.executeChain.calledOnce).to.be.true;
56-
expect(next.called).to.be.false;
57-
58-
expect(res.set.called).to.be.true;
59-
expect(res.status.calledWith(200)).to.be.true; // status 200 is used to ensure error message is rendered by git client
60-
expect(res.send.calledWith(handleMessage('denied!'))).to.be.true;
61-
});
62-
63-
it('when the chain allow it calls next() and overrides req.pipe', async () => {
64-
fakeChain.executeChain.resolves({ blocked: false, error: false });
65-
46+
it('extracts raw body and sets bodyRaw property', async () => {
6647
req.write('abcd');
6748
req.end();
6849

69-
await teeAndValidate(req, res, next);
50+
await extractRawBody(req, res, next);
7051

7152
expect(fakeRawBody.calledOnce).to.be.true;
72-
expect(fakeChain.executeChain.calledOnce).to.be.true;
53+
expect(fakeChain.executeChain.called).to.be.false;
7354
expect(next.calledOnce).to.be.true;
55+
expect(req.bodyRaw).to.exist;
7456
expect(typeof req.pipe).to.equal('function');
7557
});
7658
});

0 commit comments

Comments
 (0)