mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: new helper functions to replace regexes #807
Implemented new StringUtil helper functions: splitCommaSeparated, sanitizeUrlPart, isValidFileName. Added helper functions to HeaderUtil: matchesAuthorizationScheme, hasScheme. Added unit tests for the new helper functions. Refactored codebase to use helper functions instead of regexes if applicable.
This commit is contained in:
committed by
Joachim Van Herwegen
parent
1b7cc1ea3a
commit
283c301f08
@@ -3,6 +3,7 @@ import request from 'supertest';
|
||||
import type { BaseHttpServerFactory } from '../../src/server/BaseHttpServerFactory';
|
||||
import type { HttpHandlerInput } from '../../src/server/HttpHandler';
|
||||
import { HttpHandler } from '../../src/server/HttpHandler';
|
||||
import { splitCommaSeparated } from '../../src/util/StringUtil';
|
||||
import { getPort } from '../util/Util';
|
||||
import { getTestConfigPath, instantiateFromConfig } from './Config';
|
||||
|
||||
@@ -96,46 +97,46 @@ describe('An http server with middleware', (): void => {
|
||||
it('exposes the Accept-[Method] header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Accept-Patch');
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Accept-Post');
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Accept-Put');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Accept-Patch');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Accept-Post');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Accept-Put');
|
||||
});
|
||||
|
||||
it('exposes the Last-Modified and ETag headers via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('ETag');
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Last-Modified');
|
||||
expect(splitCommaSeparated(exposed)).toContain('ETag');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Last-Modified');
|
||||
});
|
||||
|
||||
it('exposes the Link header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Link');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Link');
|
||||
});
|
||||
|
||||
it('exposes the Location header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Location');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Location');
|
||||
});
|
||||
|
||||
it('exposes the MS-Author-Via header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('MS-Author-Via');
|
||||
expect(splitCommaSeparated(exposed)).toContain('MS-Author-Via');
|
||||
});
|
||||
|
||||
it('exposes the WAC-Allow header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('WAC-Allow');
|
||||
expect(splitCommaSeparated(exposed)).toContain('WAC-Allow');
|
||||
});
|
||||
|
||||
it('exposes the Updates-Via header via CORS.', async(): Promise<void> => {
|
||||
const res = await request(server).get('/').expect(200);
|
||||
const exposed = res.header['access-control-expose-headers'];
|
||||
expect(exposed.split(/\s*,\s*/u)).toContain('Updates-Via');
|
||||
expect(splitCommaSeparated(exposed)).toContain('Updates-Via');
|
||||
});
|
||||
|
||||
it('sends incoming requests to the handler.', async(): Promise<void> => {
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { HttpResponse } from '../../../src/server/HttpResponse';
|
||||
import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError';
|
||||
import {
|
||||
addHeader,
|
||||
hasScheme,
|
||||
matchesAuthorizationScheme,
|
||||
parseAccept,
|
||||
parseAcceptCharset,
|
||||
parseAcceptDateTime,
|
||||
@@ -419,4 +421,52 @@ describe('HeaderUtil', (): void => {
|
||||
expect(parseLinkHeader()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#matchesAuthorizationScheme', (): void => {
|
||||
it('returns true if the provided authorization header value matches the provided scheme.', (): void => {
|
||||
const authorization = `Bearer Q0xXTzl1dTM4RF8xLXllSGx5am51WFUzbzZ2LTZ1WU1GWXpfMTBEajBjaw==`;
|
||||
expect(matchesAuthorizationScheme('Bearer', authorization)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if the provided authorization header value does not match the provided scheme.', (): void => {
|
||||
const authorization = `Basic YWxpY2U6YWxpY2U=`;
|
||||
expect(matchesAuthorizationScheme('Bearer', authorization)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('correctly detects scheme matches when a different casing is used.', (): void => {
|
||||
const authorization = `bAsIc YWxpY2U6YWxpY2U=`;
|
||||
expect(matchesAuthorizationScheme('Basic', authorization)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('escapes special regex characters in the scheme argument, resulting in a correct match.', (): void => {
|
||||
const authorization = `bA.*sIc$ YWxpY2U6YWxpY2U=`;
|
||||
expect(matchesAuthorizationScheme('bA.*sIc$', authorization)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if the authorization argument is undefined.', (): void => {
|
||||
expect(matchesAuthorizationScheme('Bearer')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#hasScheme', (): void => {
|
||||
it('returns true if the provided url matches the provided scheme.', (): void => {
|
||||
expect(hasScheme('http://example.com', 'http')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns true if the provided url matches one of the provided schemes.', (): void => {
|
||||
expect(hasScheme('ws://example.com', 'http', 'https', 'ws')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('returns false if the provided url does not match the provided scheme.', (): void => {
|
||||
expect(hasScheme('http://example.com', 'https')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns false if the provided value is not a valid url.', (): void => {
|
||||
expect(hasScheme('not-a-URL:test', 'http')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('is case insensitive: schemes with different case, result in a correct match.', (): void => {
|
||||
expect(hasScheme('wss://example.com', 'http', 'WSS')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
34
test/unit/util/StringUtil.test.ts
Normal file
34
test/unit/util/StringUtil.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
sanitizeUrlPart,
|
||||
splitCommaSeparated,
|
||||
isValidFileName,
|
||||
} from '../../../src/util/StringUtil';
|
||||
|
||||
describe('HeaderUtil', (): void => {
|
||||
describe('#sanitizeUrlPart', (): void => {
|
||||
it('sanitizes part of a URL by replacing non-word characters with dashes (\'-\').', (): void => {
|
||||
expect(sanitizeUrlPart('$path segment containing=non-word+chars'))
|
||||
.toBe('-path-segment-containing-non-word-chars');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#splitCommaSeparated', (): void => {
|
||||
it('splits strings containing commas into parts based on the location of these commas.', (): void => {
|
||||
expect(splitCommaSeparated('this,is,a,comma-separated,string'))
|
||||
.toEqual([ 'this', 'is', 'a', 'comma-separated', 'string' ]);
|
||||
});
|
||||
it('handles strings without commas by returning an array containing solely the original string.', (): void => {
|
||||
const strVal = 'this string has no commas';
|
||||
expect(splitCommaSeparated(strVal)).toEqual([ strVal ]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#validateFileName', (): void => {
|
||||
it('returns true if the provided file name is valid.', (): void => {
|
||||
expect(isValidFileName('valid-file.test')).toBeTruthy();
|
||||
});
|
||||
it('returns false if the provided file name is invalid.', (): void => {
|
||||
expect(isValidFileName('$%^*')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user