diff --git a/src/api_share.test.ts b/src/api_share.test.ts index 768d6b5..a7c1b1c 100644 --- a/src/api_share.test.ts +++ b/src/api_share.test.ts @@ -83,4 +83,35 @@ describe('share api', () => { url: 'https://example.com/mypath/to/share' }); }); + + test.each([ + '//evil.com/x', + '/\\evil.com', + '\\\\evil.com', + 'https://evil.com', + 'no-leading-slash' + ])('rejects scheme-relative or malformed share path %s', async (path) => { + const server = await api({ + title: 'my awesome service', + smbServerBaseUrl: 'http://localhost', + endpointIdleTimeout: '60', + publicHost: 'https://example.com', + dbManager: mockDbManager, + productionManager: mockProductionManager, + ingestManager: mockIngestManager, + coreFunctions: new CoreFunctions( + mockProductionManager, + new ConnectionQueue() + ) + }); + const response = await server.inject({ + method: 'POST', + url: '/api/v1/share', + body: { + path + } + }); + expect(response.statusCode).toBe(400); + expect(response.json().url).toBeUndefined(); + }); }); diff --git a/src/api_share.ts b/src/api_share.ts index a27ec1f..567a586 100644 --- a/src/api_share.ts +++ b/src/api_share.ts @@ -28,6 +28,11 @@ const apiShare: FastifyPluginCallback = ( }, async (req, reply) => { let shareLinkUrl = new URL(req.body.path, opts.publicHost); + if (shareLinkUrl.origin !== new URL(opts.publicHost).origin) { + return reply.code(400).send({ + message: 'Invalid path: must resolve within the application host' + }); + } if (process.env.OSC_ACCESS_TOKEN) { const response = await fetch( `https://token.svc.${OSC_ENVIRONMENT}.osaas.io/delegate/eyevinn-intercom-manager`, diff --git a/src/models.ts b/src/models.ts index b38f1ea..1b6fee4 100644 --- a/src/models.ts +++ b/src/models.ts @@ -328,7 +328,7 @@ export const ShareRequest = Type.Object({ path: Type.String({ description: 'The application path to share', maxLength: 500, - pattern: '^/' + pattern: '^/(?![/\\\\]).*' }) }); export type ShareRequest = Static;