feat(backend-common): added ports range
Signed-off-by: Antonio Musolino <antoniomusolino007@gmail.com>
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Added port ranges in allowed hosts:
|
||||
|
||||
```yaml
|
||||
reading:
|
||||
allow:
|
||||
- host: *.examples.org:900-1000
|
||||
```
|
||||
@@ -74,9 +74,14 @@ describe('FetchUrlReader', () => {
|
||||
reading: {
|
||||
allow: [
|
||||
{ host: 'example.com' },
|
||||
{ host: 'example.com:100-200' },
|
||||
{ host: 'example.com:700' },
|
||||
{ host: '*.examples.org' },
|
||||
{ host: '*.examples.org:700' },
|
||||
{ host: '*.examples.org:900-1000' },
|
||||
{ host: '*.examples.org:900-1000' },
|
||||
{ host: 'https.org:443' },
|
||||
{ host: 'http.org:80' },
|
||||
{
|
||||
host: 'foobar.org',
|
||||
paths: ['/dir1/'],
|
||||
@@ -113,6 +118,55 @@ describe('FetchUrlReader', () => {
|
||||
expect(predicate(new URL('https://foobar.org/dir1/subpath'))).toBe(true);
|
||||
expect(predicate(new URL('https://foobar.org/dir12'))).toBe(false);
|
||||
expect(predicate(new URL('https://foobar.org/'))).toBe(false);
|
||||
expect(predicate(new URL('https://a.examples.org:900/test'))).toBe(true);
|
||||
expect(predicate(new URL('https://a.examples.org:1000/test'))).toBe(true);
|
||||
expect(predicate(new URL('https://a.examples.org:950/test'))).toBe(true);
|
||||
expect(predicate(new URL('https://a.examples.org:1050/test'))).toBe(false);
|
||||
expect(predicate(new URL('https://example.com:150/test'))).toBe(true);
|
||||
expect(predicate(new URL('https://example.com:4000/test'))).toBe(false);
|
||||
expect(predicate(new URL('https://https.org'))).toBe(true);
|
||||
expect(predicate(new URL('http://https.org'))).toBe(false);
|
||||
expect(predicate(new URL('http://http.org'))).toBe(true);
|
||||
expect(predicate(new URL('https://http.org'))).toBe(false);
|
||||
});
|
||||
|
||||
it('factory should throw for malformed uri', async () => {
|
||||
const buildFactory = (hosts: string[]) => {
|
||||
return FetchUrlReader.factory({
|
||||
config: new ConfigReader({
|
||||
backend: {
|
||||
reading: {
|
||||
allow: hosts.map(host => ({ host })),
|
||||
},
|
||||
},
|
||||
}),
|
||||
logger: getVoidLogger(),
|
||||
treeResponseFactory: DefaultReadTreeResponseFactory.create({
|
||||
config: new ConfigReader({}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:100-']),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:-']),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:500-']),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:100-50']),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:-330-']),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:-100-300']),
|
||||
).not.toThrow();
|
||||
expect(() =>
|
||||
buildFactory(['example.com:100-200', 'example.com:nb-300']),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
describe('read', () => {
|
||||
|
||||
@@ -27,6 +27,41 @@ import {
|
||||
import path from 'path';
|
||||
import { ReadUrlResponseFactory } from './ReadUrlResponseFactory';
|
||||
|
||||
const isInRange = (num: number, [start, end]: [number, number]) => {
|
||||
return num >= start && num <= end;
|
||||
};
|
||||
|
||||
const parsePortRange = (port: string): [number, number] => {
|
||||
const isRange = port.includes('-');
|
||||
if (isRange) {
|
||||
const range = port
|
||||
.split('-')
|
||||
.map(v => parseInt(v, 10))
|
||||
.filter(Boolean) as [number, number];
|
||||
if (range.length !== 2) throw new Error(`Port range is not valid: ${port}`);
|
||||
const [start, end] = range;
|
||||
if (start <= 0 || end <= 0 || start > end)
|
||||
throw new Error(`Port range is not valid: [${start}, ${end}]`);
|
||||
return range;
|
||||
}
|
||||
const parsedPort = parseInt(port, 10);
|
||||
return [parsedPort, parsedPort];
|
||||
};
|
||||
|
||||
const parsePortPredicate = (port: string | undefined) => {
|
||||
if (port) {
|
||||
const range = parsePortRange(port);
|
||||
return (url: URL) => {
|
||||
if (url.port) return isInRange(parseInt(url.port, 10), range);
|
||||
|
||||
if (url.protocol === 'http:') return isInRange(80, range);
|
||||
if (url.protocol === 'https:') return isInRange(443, range);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
return (url: URL) => !url.port;
|
||||
};
|
||||
|
||||
/**
|
||||
* A {@link UrlReader} that does a plain fetch of the URL.
|
||||
*
|
||||
@@ -60,11 +95,17 @@ export class FetchUrlReader implements UrlReader {
|
||||
}
|
||||
: (_url: URL) => true;
|
||||
const host = allowConfig.getString('host');
|
||||
if (host.startsWith('*.')) {
|
||||
const suffix = host.slice(1);
|
||||
return (url: URL) => url.host.endsWith(suffix) && checkPath(url);
|
||||
const [hostname, port] = host.split(':');
|
||||
|
||||
const checkPort = parsePortPredicate(port);
|
||||
|
||||
if (hostname.startsWith('*.')) {
|
||||
const suffix = hostname.slice(1);
|
||||
return (url: URL) =>
|
||||
url.hostname.endsWith(suffix) && checkPath(url) && checkPort(url);
|
||||
}
|
||||
return (url: URL) => url.host === host && checkPath(url);
|
||||
return (url: URL) =>
|
||||
url.hostname === hostname && checkPath(url) && checkPort(url);
|
||||
}) ?? [];
|
||||
|
||||
const reader = new FetchUrlReader();
|
||||
|
||||
Reference in New Issue
Block a user