UNPKG

@specprotected/spec-proxy-aws-edge-lambda

Version:

Spec Proxy integration with AWS Edge@Lambda

179 lines 6.92 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.specProxyProcessResponse = exports.specProxyProcessRequest = void 0; const cookie_1 = require("cookie"); const node_fetch_1 = __importStar(require("node-fetch")); const uuid_1 = require("uuid"); const url_1 = require("url"); const SPEC_PATH_PREFIX = "/spec_traffic"; const SPEC_HEADER_FORWARD_ORIGIN = "x-spec-forward-origin"; const SPEC_COOKIE_ID = "x-spec-id"; const SPEC_HEADER_CUSTOMER_KEY = "x-spec-customer-authorization"; const HEADER_SET_COOKIE = "set-cookie"; const HEADER_COOKIE = "cookie"; const HEADER_HOST = "host"; const HEADER_X_FORWARDED_FOR = "x-forwarded-for"; const SPEC_INTERNAL = "spec-internal.com"; async function specProxyProcessRequest(event, config = {}) { const request = event.Records[0].cf.request; const host = request.headers[HEADER_HOST][0].value; const headers = request.headers; const uri = request.uri; const query = request.querystring ? `?${request.querystring}` : ""; const url = new url_1.URL(`https://${host}${uri}${query}`); const bodyData = request.body?.data ? Buffer.from(request.body.data, "base64").toString("utf-8") : undefined; if (config.disableSpecProxy) { return request; } let originalHost = url.hostname; let specUrl = new url_1.URL(url.toString()); specUrl.hostname = `${host}.${SPEC_INTERNAL}`; if (!shouldHandleRequest(url, headers, config)) { return request; } if (config.customerKey) { request.headers[`${SPEC_HEADER_CUSTOMER_KEY}`] = [ { key: `${SPEC_HEADER_CUSTOMER_KEY}`, value: config.customerKey }, ]; } if (config.inlineMode || (!config.disableSpecTraffic && specUrl.pathname.startsWith(SPEC_PATH_PREFIX))) { request.origin = { custom: { domainName: specUrl.hostname, protocol: "https", port: 443, path: "", sslProtocols: ["TLSv1.2"], readTimeout: 5, keepaliveTimeout: 5, customHeaders: {}, }, }; request.headers["host"] = [{ key: "host", value: specUrl.hostname }]; request.headers[`${SPEC_HEADER_FORWARD_ORIGIN}`] = [ { key: `${SPEC_HEADER_FORWARD_ORIGIN}`, value: originalHost }, ]; return request; } else { const body = bodyData; const specHeaders = new node_fetch_1.Headers(); for (const key in headers) { specHeaders.append(key, headers[key][0].value); } specHeaders.set("Host", specUrl.hostname); const requestInit = { body, headers: specHeaders, method: request.method, }; const specRequest = new node_fetch_1.Request(specUrl.toString(), requestInit); await (0, node_fetch_1.default)(specRequest); return request; } } exports.specProxyProcessRequest = specProxyProcessRequest; function specProxyProcessResponse(event, config = {}) { const response = event.Records[0].cf.response; const headers = response.headers; const cookieHeader = headers.hasOwnProperty(HEADER_COOKIE) ? headers[HEADER_COOKIE][0].value : ""; const request = event.Records[0].cf.request; const host = request.headers[HEADER_HOST][0].value; const uri = request.uri; const url = new url_1.URL(`https://${host}${uri}`); if (config.disableSpecProxy || config.inlineMode === true || !shouldHandleRequest(url, headers, config)) { return response; } let cookies = {}; if (cookieHeader.length) { cookies = (0, cookie_1.parse)(cookieHeader); } if (!cookies[SPEC_COOKIE_ID]) { let specId = (0, uuid_1.v4)(); let setCookie = (0, cookie_1.serialize)(SPEC_COOKIE_ID, specId, { maxAge: 320000000, domain: extractTopLevelDomain(request.headers) || undefined, path: "/", sameSite: "none", secure: true, }); let set_cookie_headers = response.headers[HEADER_SET_COOKIE]; let set_cookie_header = { key: HEADER_SET_COOKIE, value: setCookie }; if (set_cookie_headers) { set_cookie_headers.push(set_cookie_header); } else { set_cookie_headers = [set_cookie_header]; } response.headers[HEADER_SET_COOKIE] = set_cookie_headers; } return response; } exports.specProxyProcessResponse = specProxyProcessResponse; function shouldHandleRequest(url, headers, config) { if (!config.disableSpecTraffic && url.pathname.startsWith(SPEC_PATH_PREFIX)) { return true; } if (config.percentageOfIPs === undefined || config.percentageOfIPs >= 100) { return true; } else if (config.percentageOfIPs <= 0) { return false; } const headerVal = headers.hasOwnProperty(HEADER_X_FORWARDED_FOR) ? headers[HEADER_X_FORWARDED_FOR][0].value : "99"; let ip_octet_sum = headerVal .split(".") .map((octet) => parseInt(octet)) .reduce((acc, n) => acc + n, 0); if (isNaN(ip_octet_sum)) { ip_octet_sum = 99; } return ip_octet_sum % 100 < config.percentageOfIPs; } function extractTopLevelDomain(headers) { let host = headers.hasOwnProperty(HEADER_HOST) ? headers[HEADER_HOST][0].value : ""; if (host.length) { const domain_extract = /^(.*?\.)?(?<domain>[^.]+\.(com|co|org|edu|net|int|gov|mil|uk|co\.uk|ac\.uk|gov\.uk|ltd\.uk|me\.uk|net\.uk|nhs\.uk|org\.uk|plc\.uk|police\.uk))(\.spec-internal\.com)?$/; let matches = domain_extract.exec(host); if (matches?.groups?.domain) { host = matches.groups.domain; } } return host; } //# sourceMappingURL=index.js.map