Skip to content

Commit

Permalink
Blocks updating and creating webresources with non multipart-formdata…
Browse files Browse the repository at this point in the history
… requests

Change-type: patch
  • Loading branch information
otaviojacobi committed Oct 20, 2023
1 parent 1a05b22 commit 71b49a0
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 4 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"memoizee": "^0.4.15",
"pinejs-client-core": "^6.13.0",
"randomstring": "^1.2.3",
"type-is": "^1.6.18",
"typed-error": "^3.2.2"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions src/sbvr-api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface HookReq {
custom?: AnyObject;
tx?: Tx;
hooks?: InstantiatedHooks;
is?: (type: string | string[]) => string | false | null;
}
export interface HookArgs {
req: HookReq;
Expand Down
20 changes: 18 additions & 2 deletions src/webresource-handler/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type * as Express from 'express';
import * as busboy from 'busboy';
import * as is from 'type-is';
import * as stream from 'stream';
import * as uriParser from '../sbvr-api/uri-parser';
import * as sbvrUtils from '../sbvr-api/sbvr-utils';
import type { HookArgs } from '../sbvr-api/hooks';
import { getApiRoot, getModel } from '../sbvr-api/sbvr-utils';
import { checkPermissions } from '../sbvr-api/permissions';
import { NoopHandler } from './handlers/NoopHandler';
Expand Down Expand Up @@ -108,7 +108,7 @@ export const getUploaderMiddlware = (
handler: WebResourceHandler,
): Express.RequestHandler => {
return async (req, res, next) => {
if (!is(req, ['multipart'])) {
if (!req.is('multipart')) {
return next();
}
const uploadedFilePaths: string[] = [];
Expand Down Expand Up @@ -226,10 +226,25 @@ const deleteFiles = async (
await Promise.all(promises);
};

const removeWebresourceInNonMultipartRequests = async ({
req,
request,
}: HookArgs) => {
if (!req.is?.('multipart')) {
const webResourceFields = getWebResourceFields(request);
for (const field of webResourceFields) {
if (request.values[field] != null) {
delete request.values[field];
}
}
}
};

const getCreateWebResourceHooks = (
webResourceHandler: WebResourceHandler,
): sbvrUtils.Hooks => {
return {
POSTPARSE: removeWebresourceInNonMultipartRequests,
'POSTRUN-ERROR': async ({ tx, request }) => {
tx?.on('rollback', () => {
void deleteRollbackPendingFields(request, webResourceHandler);
Expand Down Expand Up @@ -268,6 +283,7 @@ const getRemoveWebResourceHooks = (
webResourceHandler: WebResourceHandler,
): sbvrUtils.Hooks => {
return {
POSTPARSE: removeWebresourceInNonMultipartRequests,
PRERUN: async (args) => {
const { api, request, tx } = args;
let webResourceFields = getWebResourceFields(request);
Expand Down
76 changes: 76 additions & 0 deletions test/06-webresource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,82 @@ describe('06 webresources tests', function () {
expect(await isEventuallyDeleted(uniqueFilename)).to.be.true;
});

it('should not accept webresource payload on application/json requests', async () => {
const uniqueFilename = `${randomUUID()}_${filename}`;

const { body: organization } = await supertest(testLocalServer)
.post(`/${resourceName}/organization`)
.send({
name: 'John',
[resourcePath]: {
filename: uniqueFilename,
content_type: contentType,
size: fileSize,
href: 'http://dummy/bucket/other_href',
},
})
.expect(201);

expect(organization[resourcePath]).to.be.null;
const getRes = await supertest(testLocalServer)
.get(`/${resourceName}/organization(${organization.id})`)
.expect(200);

expect(getRes.body.d[0][resourcePath]).to.be.null;
});

it('does not modify stored file if uploading with application/json requests', async () => {
const uniqueFilename = `${randomUUID()}_${filename}`;

const { body: organization } = await supertest(testLocalServer)
.post(`/${resourceName}/organization`)
.field('name', 'john')
.attach(resourcePath, filePath, {
filename: uniqueFilename,
contentType,
})
.expect(201);

const href = organization[resourcePath].href;

await supertest(testLocalServer)
.patch(`/${resourceName}/organization(${organization.id})`)
.send({ name: 'test' })
.expect(200);

const getRes = await supertest(testLocalServer)
.get(`/${resourceName}/organization(${organization.id})`)
.expect(200);

expect(getRes.body.d[0].name).to.be.eq('test');
expect(getRes.body.d[0][resourcePath].href).to.be.eq(href);
});

it('should delete resource in S3 when passing null in application/json request', async () => {
const uniqueFilename = `${randomUUID()}_${filename}`;

const { body: organization } = await supertest(testLocalServer)
.post(`/${resourceName}/organization`)
.field('name', 'john')
.attach(resourcePath, filePath, {
filename: uniqueFilename,
contentType,
})
.expect(201);

await supertest(testLocalServer)
.patch(`/${resourceName}/organization(${organization.id})`)
.send({ [resourcePath]: null })
.expect(200);

const getRes = await supertest(testLocalServer)
.get(`/${resourceName}/organization(${organization.id})`)
.expect(200);

expect(getRes.body.d[0][resourcePath]).to.be.null;
expect(await isEventuallyDeleted(uniqueFilename)).to.be.true;
});

it('does not fail to serve if S3 resource is deleted but entry exists', async () => {
// This tests the current behavior, but we might want to change it in the future
// because the current behavior allows for a dangling reference to exist
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/06-webresource/translations/v1/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const addHook = (

addHook(['PUT', 'POST', 'PATCH'], 'organization', {
async POSTPARSE({ request }) {
if (request.values.other_image) {
if (request.values.other_image !== undefined) {
request.values.logo_image = request.values.other_image;
delete request.values.other_image;
}
Expand Down

0 comments on commit 71b49a0

Please sign in to comment.