Skip to content

Commit e7ee0d6

Browse files
authored
Merge pull request #1025 from jescalada/improve-plugin-test-coverage
test: improve proxy route test coverage
2 parents 96c4754 + c6ed94c commit e7ee0d6

File tree

4 files changed

+210
-19
lines changed

4 files changed

+210
-19
lines changed

src/proxy/routes/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,21 @@ const stripGitHubFromGitPath = (url: string): string | undefined => {
3434
*/
3535
const validGitRequest = (url: string, headers: any): boolean => {
3636
const { 'user-agent': agent, accept } = headers;
37+
if (!agent) {
38+
return false;
39+
}
3740
if (['/info/refs?service=git-upload-pack', '/info/refs?service=git-receive-pack'].includes(url)) {
3841
// https://www.git-scm.com/docs/http-protocol#_discovering_references
3942
// We can only filter based on User-Agent since the Accept header is not
4043
// sent in this request
4144
return agent.startsWith('git/');
4245
}
4346
if (['/git-upload-pack', '/git-receive-pack'].includes(url)) {
47+
if (!accept) {
48+
return false;
49+
}
4450
// https://www.git-scm.com/docs/http-protocol#_uploading_data
45-
return agent.startsWith('git/') && accept.startsWith('application/x-git-');
51+
return agent.startsWith('git/') && accept.startsWith('application/x-git-') ;
4652
}
4753
return false;
4854
};
@@ -67,7 +73,6 @@ router.use(
6773

6874
if (action.error || action.blocked) {
6975
res.set('content-type', 'application/x-git-receive-pack-result');
70-
res.set('transfer-encoding', 'chunked');
7176
res.set('expires', 'Fri, 01 Jan 1980 00:00:00 GMT');
7277
res.set('pragma', 'no-cache');
7378
res.set('cache-control', 'no-cache, max-age=0, must-revalidate');

test/testProxyRoute.js

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

test/testProxyRoute.test.js

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
const { handleMessage, validGitRequest, stripGitHubFromGitPath } = require('../src/proxy/routes');
2+
const chai = require('chai');
3+
const chaiHttp = require('chai-http');
4+
const sinon = require('sinon');
5+
const express = require('express');
6+
const proxyRouter = require('../src/proxy/routes').router;
7+
const chain = require('../src/proxy/chain');
8+
9+
chai.use(chaiHttp);
10+
chai.should();
11+
const expect = chai.expect;
12+
13+
describe('proxy route filter middleware', () => {
14+
let app;
15+
16+
beforeEach(() => {
17+
app = express();
18+
app.use('/', proxyRouter);
19+
});
20+
21+
afterEach(() => {
22+
sinon.restore();
23+
});
24+
25+
it('should reject invalid git requests with 400', async () => {
26+
const res = await chai
27+
.request(app)
28+
.get('/owner/repo.git/invalid/path')
29+
.set('user-agent', 'git/2.42.0')
30+
.set('accept', 'application/x-git-upload-pack-request');
31+
32+
expect(res).to.have.status(400);
33+
expect(res.text).to.equal('Invalid request received');
34+
});
35+
36+
it('should handle blocked requests and return custom packet message', async () => {
37+
sinon.stub(chain, 'executeChain').resolves({
38+
blocked: true,
39+
blockedMessage: 'You shall not push!',
40+
error: true,
41+
});
42+
43+
const res = await chai
44+
.request(app)
45+
.get('/owner/repo.git/info/refs?service=git-upload-pack')
46+
.set('user-agent', 'git/2.42.0')
47+
.set('accept', 'application/x-git-upload-pack-request')
48+
.buffer();
49+
50+
expect(res.status).to.equal(200);
51+
expect(res.text).to.contain('You shall not push!');
52+
expect(res.headers['content-type']).to.include('application/x-git-receive-pack-result');
53+
expect(res.headers['x-frame-options']).to.equal('DENY');
54+
});
55+
56+
describe('when request is valid and not blocked', () => {
57+
it('should return error if repo is not found', async () => {
58+
sinon.stub(chain, 'executeChain').resolves({
59+
blocked: false,
60+
blockedMessage: '',
61+
error: false,
62+
});
63+
64+
const res = await chai
65+
.request(app)
66+
.get('/owner/repo.git/info/refs?service=git-upload-pack')
67+
.set('user-agent', 'git/2.42.0')
68+
.set('accept', 'application/x-git-upload-pack-request')
69+
.buffer();
70+
71+
expect(res.status).to.equal(401);
72+
expect(res.text).to.equal('Repository not found.');
73+
});
74+
75+
it('should pass through if repo is found', async () => {
76+
sinon.stub(chain, 'executeChain').resolves({
77+
blocked: false,
78+
blockedMessage: '',
79+
error: false,
80+
});
81+
82+
const res = await chai
83+
.request(app)
84+
.get('/finos/git-proxy.git/info/refs?service=git-upload-pack')
85+
.set('user-agent', 'git/2.42.0')
86+
.set('accept', 'application/x-git-upload-pack-request')
87+
.buffer();
88+
89+
expect(res.status).to.equal(200);
90+
expect(res.text).to.contain('git-upload-pack');
91+
});
92+
});
93+
});
94+
95+
describe('proxy route helpers', () => {
96+
describe('handleMessage', async () => {
97+
it('should handle short messages', async function () {
98+
const res = await handleMessage('one');
99+
expect(res).to.contain('one');
100+
});
101+
102+
it('should handle emoji messages', async function () {
103+
const res = await handleMessage('❌ push failed: too many errors');
104+
expect(res).to.contain('❌');
105+
});
106+
});
107+
108+
describe('validGitRequest', () => {
109+
it('should return true for /info/refs?service=git-upload-pack with valid user-agent', () => {
110+
const res = validGitRequest('/info/refs?service=git-upload-pack', {
111+
'user-agent': 'git/2.30.1',
112+
});
113+
expect(res).to.be.true;
114+
});
115+
116+
it('should return true for /info/refs?service=git-receive-pack with valid user-agent', () => {
117+
const res = validGitRequest('/info/refs?service=git-receive-pack', {
118+
'user-agent': 'git/1.9.1',
119+
});
120+
expect(res).to.be.true;
121+
});
122+
123+
it('should return false for /info/refs?service=git-upload-pack with missing user-agent', () => {
124+
const res = validGitRequest('/info/refs?service=git-upload-pack', {});
125+
expect(res).to.be.false;
126+
});
127+
128+
it('should return false for /info/refs?service=git-upload-pack with non-git user-agent', () => {
129+
const res = validGitRequest('/info/refs?service=git-upload-pack', {
130+
'user-agent': 'curl/7.79.1',
131+
});
132+
expect(res).to.be.false;
133+
});
134+
135+
it('should return true for /git-upload-pack with valid user-agent and accept', () => {
136+
const res = validGitRequest('/git-upload-pack', {
137+
'user-agent': 'git/2.40.0',
138+
accept: 'application/x-git-upload-pack-request',
139+
});
140+
expect(res).to.be.true;
141+
});
142+
143+
it('should return false for /git-upload-pack with missing accept header', () => {
144+
const res = validGitRequest('/git-upload-pack', {
145+
'user-agent': 'git/2.40.0',
146+
});
147+
expect(res).to.be.false;
148+
});
149+
150+
it('should return false for /git-upload-pack with wrong accept header', () => {
151+
const res = validGitRequest('/git-upload-pack', {
152+
'user-agent': 'git/2.40.0',
153+
accept: 'application/json',
154+
});
155+
expect(res).to.be.false;
156+
});
157+
158+
it('should return false for unknown paths', () => {
159+
const res = validGitRequest('/not-a-valid-git-path', {
160+
'user-agent': 'git/2.40.0',
161+
accept: 'application/x-git-upload-pack-request',
162+
});
163+
expect(res).to.be.false;
164+
});
165+
});
166+
167+
describe('stripGitHubFromGitPath', () => {
168+
it('should strip owner and repo from a valid GitHub-style path with 4 parts', () => {
169+
const res = stripGitHubFromGitPath('/foo/bar.git/info/refs');
170+
expect(res).to.equal('/info/refs');
171+
});
172+
173+
it('should strip owner and repo from a valid GitHub-style path with 5 parts', () => {
174+
const res = stripGitHubFromGitPath('/foo/bar.git/git-upload-pack');
175+
expect(res).to.equal('/git-upload-pack');
176+
});
177+
178+
it('should return undefined for malformed path with too few segments', () => {
179+
const res = stripGitHubFromGitPath('/foo/bar.git');
180+
expect(res).to.be.undefined;
181+
});
182+
183+
it('should return undefined for malformed path with too many segments', () => {
184+
const res = stripGitHubFromGitPath('/foo/bar.git/extra/path/stuff');
185+
expect(res).to.be.undefined;
186+
});
187+
188+
it('should handle repo names that include dots correctly', () => {
189+
const res = stripGitHubFromGitPath('/foo/some.repo.git/info/refs');
190+
expect(res).to.equal('/info/refs');
191+
});
192+
193+
it('should not break if the path is just a slash', () => {
194+
const res = stripGitHubFromGitPath('/');
195+
expect(res).to.be.undefined;
196+
});
197+
198+
it('should not break if the path is empty', () => {
199+
const res = stripGitHubFromGitPath('');
200+
expect(res).to.be.undefined;
201+
});
202+
});
203+
});
File renamed without changes.

0 commit comments

Comments
 (0)