requestUtils: work with IncomingMessage, make Response optional in trustOrigin

pull/859/head
Jonathan Perret 3 months ago
parent 0e5ea20adb
commit e7e060c013

@ -8,7 +8,9 @@ import {RequestWithGrist} from 'app/server/lib/GristServer';
import log from 'app/server/lib/log';
import {Permit} from 'app/server/lib/Permit';
import {Request, Response} from 'express';
import { IncomingMessage } from 'http';
import {Writable} from 'stream';
import { TLSSocket } from 'tls';
// log api details outside of dev environment (when GRIST_HOSTED_VERSION is set)
const shouldLogApiDetails = Boolean(process.env.GRIST_HOSTED_VERSION);
@ -75,28 +77,30 @@ export function getOrgUrl(req: Request, path: string = '/') {
}
/**
* Returns true for requests from permitted origins. For such requests, an
* "Access-Control-Allow-Origin" header is added to the response. Vary: Origin
* is also set to reflect the fact that the headers are a function of the origin,
* to prevent inappropriate caching on the browser's side.
* Returns true for requests from permitted origins. For such requests, if
* a Response object is provided, an "Access-Control-Allow-Origin" header is added
* to the response. Vary: Origin is also set to reflect the fact that the headers
* are a function of the origin, to prevent inappropriate caching on the browser's side.
*/
export function trustOrigin(req: Request, resp: Response): boolean {
export function trustOrigin(req: IncomingMessage, resp?: Response): boolean {
// TODO: We may want to consider changing allowed origin values in the future.
// Note that the request origin is undefined for non-CORS requests.
const origin = req.get('origin');
const origin = req.headers.origin;
if (!origin) { return true; } // Not a CORS request.
if (process.env.GRIST_HOST && req.hostname === process.env.GRIST_HOST) { return true; }
if (!allowHost(req, new URL(origin))) { return false; }
// For a request to a custom domain, the full hostname must match.
resp.header("Access-Control-Allow-Origin", origin);
resp.header("Vary", "Origin");
if (resp) {
// For a request to a custom domain, the full hostname must match.
resp.header("Access-Control-Allow-Origin", origin);
resp.header("Vary", "Origin");
}
return true;
}
// Returns whether req satisfies the given allowedHost. Unless req is to a custom domain, it is
// enough if only the base domains match. Differing ports are allowed, which helps in dev/testing.
export function allowHost(req: Request, allowedHost: string|URL) {
export function allowHost(req: IncomingMessage, allowedHost: string|URL) {
const mreq = req as RequestWithOrg;
const proto = getEndUserProtocol(req);
const actualUrl = new URL(getOriginUrl(req));
@ -330,8 +334,8 @@ export interface RequestWithGristInfo extends Request {
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
* https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
*/
export function getOriginUrl(req: Request) {
const host = req.get('host')!;
export function getOriginUrl(req: IncomingMessage) {
const host = req.headers.host;
const protocol = getEndUserProtocol(req);
return `${protocol}://${host}`;
}
@ -342,11 +346,11 @@ export function getOriginUrl(req: Request) {
* otherwise X-Forwarded-Proto is set on the provided request, otherwise
* the protocol of the request itself.
*/
export function getEndUserProtocol(req: Request) {
export function getEndUserProtocol(req: IncomingMessage) {
if (process.env.APP_HOME_URL) {
return new URL(process.env.APP_HOME_URL).protocol.replace(':', '');
}
return req.get("X-Forwarded-Proto") || req.protocol;
return req.headers["x-forwarded-proto"] || ((req.socket as TLSSocket).encrypted ? 'https' : 'http');
}
/**

Loading…
Cancel
Save