mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Parse content-type more strictly
This commit is contained in:
@@ -26,8 +26,8 @@ describe('An RdfValidator', (): void => {
|
||||
});
|
||||
|
||||
it('validates data by running it through a converter.', async(): Promise<void> => {
|
||||
converter.handleSafe = jest.fn().mockResolvedValue(new BasicRepresentation('transformedData', 'wrongType'));
|
||||
const representation = new BasicRepresentation('data', 'content-type');
|
||||
converter.handleSafe = jest.fn().mockResolvedValue(new BasicRepresentation('transformedData', 'wrong/type'));
|
||||
const representation = new BasicRepresentation('data', 'content/type');
|
||||
const quads = representation.metadata.quads();
|
||||
// Output is not important for this Validator
|
||||
await expect(validator.handle({ representation, identifier })).resolves.toBeDefined();
|
||||
@@ -39,7 +39,7 @@ describe('An RdfValidator', (): void => {
|
||||
|
||||
it('throws an error when validating invalid data.', async(): Promise<void> => {
|
||||
converter.handleSafe = jest.fn().mockRejectedValue(new Error('bad data!'));
|
||||
const representation = new BasicRepresentation('data', 'content-type');
|
||||
const representation = new BasicRepresentation('data', 'content/type');
|
||||
await expect(validator.handle({ representation, identifier })).rejects.toThrow('bad data!');
|
||||
// Make sure the data on the readable has not been reset
|
||||
expect(representation.data.destroyed).toBe(true);
|
||||
|
||||
@@ -16,7 +16,7 @@ describe('A BasicRequestParser', (): void => {
|
||||
let requestParser: BasicRequestParser;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
targetExtractor = new StaticAsyncHandler(true, 'target' as any);
|
||||
targetExtractor = new StaticAsyncHandler(true, { path: 'target' });
|
||||
preferenceParser = new StaticAsyncHandler(true, 'preference' as any);
|
||||
metadataParser = new StaticAsyncHandler(true, undefined);
|
||||
conditionsParser = new StaticAsyncHandler(true, 'conditions' as any);
|
||||
@@ -39,10 +39,10 @@ describe('A BasicRequestParser', (): void => {
|
||||
bodyParser.handle = ({ metadata }): any => ({ data: 'body', metadata });
|
||||
await expect(requestParser.handle({ url: 'url', method: 'GET' } as any)).resolves.toEqual({
|
||||
method: 'GET',
|
||||
target: 'target',
|
||||
target: { path: 'target' },
|
||||
preferences: 'preference',
|
||||
conditions: 'conditions',
|
||||
body: { data: 'body', metadata: new RepresentationMetadata('target') },
|
||||
body: { data: 'body', metadata: new RepresentationMetadata({ path: 'target' }) },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { Representation } from '../../../../src/http/representation/Represe
|
||||
import { RepresentationMetadata } from '../../../../src/http/representation/RepresentationMetadata';
|
||||
import { FileDataAccessor } from '../../../../src/storage/accessors/FileDataAccessor';
|
||||
import { ExtensionBasedMapper } from '../../../../src/storage/mapping/ExtensionBasedMapper';
|
||||
import type { FileIdentifierMapper, ResourceLink } from '../../../../src/storage/mapping/FileIdentifierMapper';
|
||||
import { APPLICATION_OCTET_STREAM } from '../../../../src/util/ContentTypes';
|
||||
import { ConflictHttpError } from '../../../../src/util/errors/ConflictHttpError';
|
||||
import { NotFoundHttpError } from '../../../../src/util/errors/NotFoundHttpError';
|
||||
@@ -26,6 +27,7 @@ now.setMilliseconds(0);
|
||||
|
||||
describe('A FileDataAccessor', (): void => {
|
||||
const base = 'http://test.com/';
|
||||
let mapper: FileIdentifierMapper;
|
||||
let accessor: FileDataAccessor;
|
||||
let cache: { data: any };
|
||||
let metadata: RepresentationMetadata;
|
||||
@@ -33,7 +35,8 @@ describe('A FileDataAccessor', (): void => {
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
cache = mockFs(rootFilePath, now);
|
||||
accessor = new FileDataAccessor(new ExtensionBasedMapper(base, rootFilePath));
|
||||
mapper = new ExtensionBasedMapper(base, rootFilePath);
|
||||
accessor = new FileDataAccessor(mapper);
|
||||
|
||||
metadata = new RepresentationMetadata(APPLICATION_OCTET_STREAM);
|
||||
|
||||
@@ -192,6 +195,48 @@ describe('A FileDataAccessor', (): void => {
|
||||
}
|
||||
});
|
||||
|
||||
it('does not generate IANA URIs for children with invalid content-types.', async(): Promise<void> => {
|
||||
cache.data = {
|
||||
container: {
|
||||
resource1: 'data',
|
||||
resource2: 'badData',
|
||||
},
|
||||
};
|
||||
|
||||
const badMapper: jest.Mocked<FileIdentifierMapper> = {
|
||||
mapFilePathToUrl: jest.fn(async(filePath: string, isContainer: boolean): Promise<ResourceLink> => {
|
||||
const result = await mapper.mapFilePathToUrl(filePath, isContainer);
|
||||
if (filePath.endsWith('resource2')) {
|
||||
result.contentType = 'this is not a valid type';
|
||||
}
|
||||
return result;
|
||||
}),
|
||||
mapUrlToFilePath: jest.fn((...args): Promise<ResourceLink> => mapper.mapUrlToFilePath(...args)),
|
||||
};
|
||||
|
||||
accessor = new FileDataAccessor(badMapper);
|
||||
|
||||
const children = [];
|
||||
for await (const child of accessor.getChildren({ path: `${base}container/` })) {
|
||||
children.push(child);
|
||||
}
|
||||
|
||||
// Identifiers
|
||||
expect(children).toHaveLength(2);
|
||||
expect(new Set(children.map((child): string => child.identifier.value))).toEqual(new Set([
|
||||
`${base}container/resource1`,
|
||||
`${base}container/resource2`,
|
||||
]));
|
||||
|
||||
const types1 = children[0].getAll(RDF.type).map((term): string => term.value);
|
||||
const types2 = children[1].getAll(RDF.type).map((term): string => term.value);
|
||||
|
||||
expect(types1).toContain('http://www.w3.org/ns/iana/media-types/application/octet-stream#Resource');
|
||||
for (const type of types2) {
|
||||
expect(type).not.toMatch(/^http:\/\/www\.w3.org\/ns\/iana\/media-types\//u);
|
||||
}
|
||||
});
|
||||
|
||||
it('adds stored metadata when requesting metadata.', async(): Promise<void> => {
|
||||
cache.data = { resource: 'data', 'resource.meta': '<http://this> <http://is> <http://metadata>.' };
|
||||
metadata = await accessor.getMetadata({ path: `${base}resource` });
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('A SparqlDataAccessor', (): void => {
|
||||
it('can only handle quad data.', async(): Promise<void> => {
|
||||
let representation = new BasicRepresentation(data, metadata, true);
|
||||
await expect(accessor.canHandle(representation)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
representation = new BasicRepresentation(data, 'newInternalType', false);
|
||||
representation = new BasicRepresentation(data, 'internal/newInternalType', false);
|
||||
await expect(accessor.canHandle(representation)).rejects.toThrow(UnsupportedMediaTypeHttpError);
|
||||
representation = new BasicRepresentation(data, INTERNAL_QUADS, false);
|
||||
metadata.contentType = INTERNAL_QUADS;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { HttpResponse } from '../../../src/server/HttpResponse';
|
||||
import { BadRequestHttpError } from '../../../src/util/errors/BadRequestHttpError';
|
||||
import {
|
||||
addHeader,
|
||||
parseAccept,
|
||||
@@ -213,6 +214,10 @@ describe('HeaderUtil', (): void => {
|
||||
contentTypePlain.parameters.test = 'value1';
|
||||
expect(parseContentType('text/plain; charset=utf-8;test="value1"')).toEqual(contentTypePlain);
|
||||
});
|
||||
|
||||
it('errors on invalid content-types.', (): void => {
|
||||
expect((): any => parseContentType('invalid type')).toThrow(BadRequestHttpError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#parseForwarded', (): void => {
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('ResourceUtil', (): void => {
|
||||
let representation: Representation;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
representation = new BasicRepresentation('data', 'metadata');
|
||||
representation = new BasicRepresentation('data', 'meta/data');
|
||||
});
|
||||
|
||||
describe('#updateModifiedDate', (): void => {
|
||||
@@ -55,7 +55,7 @@ describe('ResourceUtil', (): void => {
|
||||
|
||||
it('ensures that original representation does not update when the clone is updated.', async(): Promise<void> => {
|
||||
const res = await cloneRepresentation(representation);
|
||||
res.metadata.contentType = 'typetype';
|
||||
res.metadata.contentType = 'type/type';
|
||||
expect(representation.metadata.contentType).not.toBe(res.metadata.contentType);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user