1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Server: Fixed handling of request origin

This commit is contained in:
Laurent Cozic 2021-05-25 20:04:54 +02:00
parent f2b17560e6
commit 12a66342db
2 changed files with 51 additions and 2 deletions

View File

@ -1,4 +1,4 @@
import { parseSubPath, splitItemPath } from './routeUtils';
import { isValidOrigin, parseSubPath, splitItemPath } from './routeUtils';
import { ItemAddressingType } from '../db';
describe('routeUtils', function() {
@ -41,4 +41,46 @@ describe('routeUtils', function() {
}
});
it('should check the request origin', async function() {
const testCases: any[] = [
[
'https://example.com', // Request origin
'https://example.com', // Config base URL
true,
],
[
// Apache ProxyPreserveHost somehow converts https:// to http://
// but in this context it's valid as only the domain matters.
'http://example.com',
'https://example.com',
true,
],
[
// With Apache ProxyPreserveHost, the request might be eg
// https://example.com/joplin/api/ping but the origin part, as
// forwarded by Apache will be https://example.com/api/ping
// (without /joplin). In that case the request is valid anyway
// since we only care about the domain.
'https://example.com',
'https://example.com/joplin',
true,
],
[
'https://bad.com',
'https://example.com',
false,
],
[
'http://bad.com',
'https://example.com',
false,
],
];
for (const testCase of testCases) {
const [requestOrigin, configBaseUrl, expected] = testCase;
expect(isValidOrigin(requestOrigin, configBaseUrl)).toBe(expected);
}
});
});

View File

@ -3,6 +3,7 @@ import { Item, ItemAddressingType } from '../db';
import { ErrorBadRequest, ErrorForbidden, ErrorNotFound } from './errors';
import Router from './Router';
import { AppContext, HttpMethod } from './types';
import { URL } from 'url';
const { ltrimSlashes, rtrimSlashes } = require('@joplin/lib/path-utils');
@ -152,6 +153,12 @@ export function parseSubPath(basePath: string, p: string, rawPath: string = null
return output;
}
export function isValidOrigin(requestOrigin: string, endPointBaseUrl: string): boolean {
const host1 = (new URL(requestOrigin)).host;
const host2 = (new URL(endPointBaseUrl)).host;
return host1 === host2;
}
export function routeResponseFormat(context: AppContext): RouteResponseFormat {
// const rawPath = context.path;
// if (match && match.route.responseFormat) return match.route.responseFormat;
@ -168,7 +175,7 @@ export async function execRequest(routes: Routers, ctx: AppContext) {
if (!match) throw new ErrorNotFound();
const endPoint = match.route.findEndPoint(ctx.request.method as HttpMethod, match.subPath.schema);
if (ctx.URL && ctx.URL.origin !== baseUrl(endPoint.type)) throw new ErrorNotFound('Invalid origin', 'invalidOrigin');
if (ctx.URL && !isValidOrigin(ctx.URL.origin, baseUrl(endPoint.type))) throw new ErrorNotFound('Invalid origin', 'invalidOrigin');
// This is a generic catch-all for all private end points - if we
// couldn't get a valid session, we exit now. Individual end points