mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-03-11 02:15:57 -05:00
chore(middleware): improve loop detection (#5111)
* chore(middleware): improve loop detection * rename loop > antiloop.spec * replace regex
This commit is contained in:
parent
016f0c2bcd
commit
411087391a
4 changed files with 111 additions and 37 deletions
5
.changeset/curly-mirrors-smile.md
Normal file
5
.changeset/curly-mirrors-smile.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@verdaccio/middleware': patch
|
||||
---
|
||||
|
||||
chore(middleware): improve loop detection
|
|
@ -14,13 +14,24 @@ export function antiLoop(config: Config) {
|
|||
const arr = req.get('via')?.split(',');
|
||||
if (Array.isArray(arr)) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
// the "via" header must contains an specific headers, this has to be on sync
|
||||
// the "via" header must contain a specific value, this has to be in sync
|
||||
// with the proxy request
|
||||
// match eg: Server 1 or Server 2
|
||||
// TODO: improve this RegEX
|
||||
const m = arr[i].trim().match(/\s*(\S+)\s+(\S+)/);
|
||||
if (m && m[2] === config.server_id) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.LOOP_DETECTED, 'loop detected'));
|
||||
|
||||
// RFC 7230: Via = 1*( "," OWS Via-value )
|
||||
// Via-value = received-protocol RWS received-by [ RWS comment ]
|
||||
// received-protocol = [ protocol-name "/" ] protocol-version
|
||||
// received-by = ( uri-host [ ":" port ] ) / pseudonym
|
||||
|
||||
// Split the trimmed header value into parts
|
||||
const parts = arr[i].trim().split(/\s+/);
|
||||
// Check if we have at least protocol/version and received-by parts
|
||||
if (parts.length >= 2) {
|
||||
// Get the received-by value (server id), removing any comment
|
||||
const serverId = parts[1].split('(')[0].trim();
|
||||
if (serverId === config.server_id) {
|
||||
return next(errorUtils.getCode(HTTP_STATUS.LOOP_DETECTED, 'loop detected'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
90
packages/middleware/test/antiLoop.spec.ts
Normal file
90
packages/middleware/test/antiLoop.spec.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import request from 'supertest';
|
||||
import { test } from 'vitest';
|
||||
|
||||
import { HTTP_STATUS } from '@verdaccio/core';
|
||||
|
||||
import { antiLoop } from '../src';
|
||||
import { getApp } from './helper';
|
||||
|
||||
test('should not be a loop', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', 'Server 2').expect(HTTP_STATUS.OK);
|
||||
});
|
||||
|
||||
test('should be a loop', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app)
|
||||
.get('/sec')
|
||||
.set('via', 'Server 1, Server 2')
|
||||
.expect(HTTP_STATUS.LOOP_DETECTED);
|
||||
});
|
||||
|
||||
test('should detect loop with protocol name in via header', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', 'HTTP/1.1 1').expect(HTTP_STATUS.LOOP_DETECTED);
|
||||
});
|
||||
|
||||
test('should detect loop with comment in via header', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', '1.1 1 (Verdaccio)').expect(HTTP_STATUS.LOOP_DETECTED);
|
||||
});
|
||||
|
||||
test('should detect loop in multiple via entries', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app)
|
||||
.get('/sec')
|
||||
.set('via', '1.1 server-a, 1.1 1, 1.1 server-b')
|
||||
.expect(HTTP_STATUS.LOOP_DETECTED);
|
||||
});
|
||||
|
||||
test('should handle malformed via header gracefully', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', 'malformed-header').expect(HTTP_STATUS.OK);
|
||||
});
|
||||
|
||||
test('should handle via header with unexpected format', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', 'unexpected format').expect(HTTP_STATUS.OK);
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
import request from 'supertest';
|
||||
import { test } from 'vitest';
|
||||
|
||||
import { HTTP_STATUS } from '@verdaccio/core';
|
||||
|
||||
import { antiLoop } from '../src';
|
||||
import { getApp } from './helper';
|
||||
|
||||
test('should not be a loop', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app).get('/sec').set('via', 'Server 2').expect(HTTP_STATUS.OK);
|
||||
});
|
||||
|
||||
test('should be a loop', async () => {
|
||||
const app = getApp([]);
|
||||
// @ts-ignore
|
||||
app.use(antiLoop({ server_id: '1' }));
|
||||
app.get('/sec', (req, res) => {
|
||||
res.status(HTTP_STATUS.OK).json({});
|
||||
});
|
||||
|
||||
return request(app)
|
||||
.get('/sec')
|
||||
.set('via', 'Server 1, Server 2')
|
||||
.expect(HTTP_STATUS.LOOP_DETECTED);
|
||||
});
|
Loading…
Add table
Reference in a new issue