Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Use 'Expires' header to time-limit URLs
Co-Authored-By: Markus Horst Becker <[email protected]>
  • Loading branch information
markuscolourbox and mtib committed Mar 14, 2022
commit d4602fce8869de9020cd05bae75fa48ec694fb39
26 changes: 26 additions & 0 deletions source/image-handler/image-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class ImageRequest {
imageRequestInfo = { ...imageRequestInfo, ...originalImage };

imageRequestInfo.headers = this.parseImageHeaders(event, imageRequestInfo.requestType);
this.validateRequestExpires(imageRequestInfo);

// If the original image is SVG file and it has any edits but no output format, change the format to WebP.
if (imageRequestInfo.contentType === 'image/svg+xml' && imageRequestInfo.edits && Object.keys(imageRequestInfo.edits).length > 0 && !imageRequestInfo.edits.toFormat) {
Expand Down Expand Up @@ -443,4 +444,29 @@ export class ImageRequest {
}
}
}

private validateRequestExpires(requestInfo: ImageRequestInfo): void {
try {
const expires = requestInfo.headers?.expires;
if (expires !== undefined) {
const parsedDate = new Date(expires);
if (isNaN(parsedDate.getTime())) {
throw new ImageHandlerError(StatusCodes.BAD_REQUEST, 'ImageRequestExpiryFormat', 'Request has invalid expiry date.');
}
const now = new Date();
if (now > parsedDate) {
throw new ImageHandlerError(StatusCodes.FORBIDDEN, 'ImageRequestExpired', 'Request has expired.');
}
}
} catch (error) {
if (error.code === 'ImageRequestExpired') {
throw error;
}
if (error.code === 'ImageRequestExpiryFormat') {
throw error;
}
console.error('Error occurred while checking expiry.', error);
throw new ImageHandlerError(StatusCodes.INTERNAL_SERVER_ERROR, 'ExpiryDateCheckFailure', 'Expiry date check failed.');
}
}
}
40 changes: 40 additions & 0 deletions source/image-handler/test/image-request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,46 @@ describe('setup()', () => {
expect(imageRequestInfo).toEqual(expectedResult);
});
});

describe('011/expiryDate', () => {
it.each([
{
path: '/eyJidWNrZXQiOiJ0ZXN0IiwicmVxdWVzdFR5cGUiOiJEZWZhdWx0Iiwia2V5IjoidGVzdC5wbmciLCJoZWFkZXJzIjp7ImV4cGlyZXMiOiJUaHUsIDAxIEphbiAxOTcwIDAwOjAwOjAwIEdNVCJ9fQ==',
error: {
code: 'ImageRequestExpired',
message: 'Request has expired.',
status: StatusCodes.FORBIDDEN,
},
},
{
path: '/eyJidWNrZXQiOiJ0ZXN0IiwicmVxdWVzdFR5cGUiOiJEZWZhdWx0Iiwia2V5IjoidGVzdC5wbmciLCJoZWFkZXJzIjp7ImV4cGlyZXMiOiJpbnZhbGlkS2V5In19',
error: {
code: 'ImageRequestExpiryFormat',
message: 'Request has invalid expiry date.',
status: StatusCodes.BAD_REQUEST,
}
}
])("Should throw an error when $error.message", (async ({ path, error: expectedError }) => {
// Arrange
const event = {
path,
};
// Mock
mockAwsS3.getObject.mockImplementationOnce(() => ({
promise() {
return Promise.resolve({ Body: Buffer.from('SampleImageContent\n') });
}
}));
// Act
const imageRequest = new ImageRequest(s3Client, secretProvider);
try {
await imageRequest.setup(event);
} catch (error) {
// Assert
expect(error).toMatchObject(expectedError);
}
}));
});
});

describe('getOriginalImage()', () => {
Expand Down