Files
twenty/packages/twenty-server/test/integration/metadata/suites/application/failing-upload-application-file-path-traversal.integration-spec.ts
T
Paul Rastoin 570c57563a Upload application file resolver exception management and integration coverage (#20803)
# Introduction
Earlier and better exception handling of the upload application file
resolver
+ coverage
2026-05-21 12:45:54 +00:00

127 lines
3.8 KiB
TypeScript

import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util';
import { cleanupApplicationAndAppRegistration } from 'test/integration/metadata/suites/application/utils/cleanup-application-and-app-registration.util';
import { setupApplicationForSync } from 'test/integration/metadata/suites/application/utils/setup-application-for-sync.util';
import { uploadApplicationFile } from 'test/integration/metadata/suites/application/utils/upload-application-file.util';
import {
type EachTestingContext,
eachTestingContextFilter,
} from 'twenty-shared/testing';
import { v4 as uuidv4 } from 'uuid';
const TEST_APP_ID = uuidv4();
const UNKNOWN_APP_ID = uuidv4();
type TestContext = {
applicationUniversalIdentifier: string;
fileFolder: string;
filePath: string;
};
const FAILING_TEST_CASES: EachTestingContext<TestContext>[] = [
{
title: 'when filePath contains relative path traversal (../)',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath:
'../../../other-workspace/other-app/BuiltFrontComponent/stolen.mjs',
},
},
{
title: 'when filePath contains upward traversal (../../)',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath: '../../etc/passwd',
},
},
{
title: 'when filePath is an absolute path',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath: '/etc/passwd',
},
},
{
title: 'when filePath contains backslash path traversal',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath: '..\\..\\..\\etc\\passwd',
},
},
{
title: 'when filePath is empty',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath: '',
},
},
{
title:
'when applicationUniversalIdentifier does not match any installed application',
context: {
applicationUniversalIdentifier: UNKNOWN_APP_ID,
fileFolder: 'BuiltFrontComponent',
filePath: 'src/components/legit.mjs',
},
},
{
title: 'when applicationUniversalIdentifier is empty',
context: {
applicationUniversalIdentifier: '',
fileFolder: 'BuiltFrontComponent',
filePath: 'src/components/legit.mjs',
},
},
{
title: 'when fileFolder is not an allowed application file folder',
context: {
applicationUniversalIdentifier: TEST_APP_ID,
fileFolder: 'CorePicture',
filePath: 'src/components/legit.mjs',
},
},
];
describe('Upload application file should fail', () => {
beforeAll(async () => {
await setupApplicationForSync({
applicationUniversalIdentifier: TEST_APP_ID,
name: 'Test Upload Path Traversal App',
description: 'App for testing path traversal on file upload',
sourcePath: 'test-upload-path-traversal',
});
}, 60000);
afterAll(async () => {
await cleanupApplicationAndAppRegistration({
applicationUniversalIdentifier: TEST_APP_ID,
});
});
it.each(eachTestingContextFilter(FAILING_TEST_CASES))(
'$title',
async ({ context }) => {
jest.useRealTimers();
const { errors } = await uploadApplicationFile({
applicationUniversalIdentifier: context.applicationUniversalIdentifier,
fileFolder: context.fileFolder,
filePath: context.filePath,
fileBuffer: Buffer.from('content'),
filename: 'test-file.mjs',
contentType: 'application/javascript',
expectToFail: true,
});
jest.useFakeTimers();
expectOneNotInternalServerErrorSnapshot({ errors });
},
60000,
);
});