UNPKG

@sentry/core

Version:
244 lines (240 loc) 8.56 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const debugBuild = require('../debug-build.js'); const debugLogger = require('./debug-logger.js'); const timer = require('./timer.js'); const filteringSnippets = require('./data-collection/filtering-snippets.js'); const MAX_BODY_BYTE_LENGTH = 1024 * 1024; const TEXT_CONTENT_TYPES = [ "text/", "application/json", "application/x-www-form-urlencoded", "application/xml", "application/graphql" ]; function getMaxBodyByteLength(maxRequestBodySize) { if (maxRequestBodySize === "small") return 1e3; if (maxRequestBodySize === "medium") return 1e4; return MAX_BODY_BYTE_LENGTH; } function winterCGHeadersToDict(winterCGHeaders) { const headers = {}; try { winterCGHeaders.forEach((value, key) => { if (typeof value === "string") { headers[key] = value; } }); } catch { } return headers; } function headersToDict(reqHeaders) { const headers = /* @__PURE__ */ Object.create(null); try { Object.entries(reqHeaders).forEach(([key, value]) => { if (typeof value === "string") { headers[key] = value; } else if (typeof value === "number") { headers[key] = String(value); } }); } catch { } return headers; } function winterCGRequestToRequestData(req) { const headers = winterCGHeadersToDict(req.headers); return { method: req.method, url: req.url, query_string: extractQueryParamsFromUrl(req.url), headers // TODO: Can we extract body data from the request? }; } function isTextualContentType(contentType) { if (!contentType) { return false; } const lowerContentType = contentType.toLowerCase(); return TEXT_CONTENT_TYPES.some((type) => lowerContentType.includes(type)); } async function captureBodyFromWinterCGRequest(request, isolationScope, maxRequestBodySize) { try { const contentType = request.headers.get("content-type"); if (!isTextualContentType(contentType)) { debugBuild.DEBUG_BUILD && debugLogger.debug.log("Skipping body capture for non-textual content type:", contentType); return; } if (!request.body) { return; } const contentLength = request.headers.get("content-length"); const maxBodySize = getMaxBodyByteLength(maxRequestBodySize); if (contentLength) { const length = parseInt(contentLength, 10); if (!isNaN(length) && length > MAX_BODY_BYTE_LENGTH) { debugBuild.DEBUG_BUILD && debugLogger.debug.log("Skipping body capture: body too large", length); return; } } const clonedRequest = request.clone(); const bodyPromise = clonedRequest.text(); const timeoutPromise = new Promise((resolve) => { timer.safeUnref(setTimeout(() => resolve(null), 2e3)); }); const body = await Promise.race([bodyPromise, timeoutPromise]); if (body === null) { debugBuild.DEBUG_BUILD && debugLogger.debug.log("Timeout reading request body"); return; } if (!body) { return; } const encoder = new TextEncoder(); const bytes = encoder.encode(body); const bodyByteLength = bytes.length; let truncatedBody; if (bodyByteLength > maxBodySize) { const decoder = new TextDecoder(); truncatedBody = `${decoder.decode(bytes.slice(0, maxBodySize - 3))}...`; } else { truncatedBody = body; } isolationScope.setSDKProcessingMetadata({ normalizedRequest: { data: truncatedBody } }); debugBuild.DEBUG_BUILD && debugLogger.debug.log("Captured request body:", bodyByteLength, "bytes"); } catch (error) { debugBuild.DEBUG_BUILD && debugLogger.debug.error("Error capturing request body:", error); } } function httpRequestToRequestData(request) { const headers = request.headers || {}; const forwardedHost = typeof headers["x-forwarded-host"] === "string" ? headers["x-forwarded-host"] : void 0; const host = forwardedHost || (typeof headers.host === "string" ? headers.host : void 0); const forwardedProto = typeof headers["x-forwarded-proto"] === "string" ? headers["x-forwarded-proto"] : void 0; const protocol = forwardedProto || request.protocol || (request.socket?.encrypted ? "https" : "http"); const url = request.url || ""; const absoluteUrl = getAbsoluteUrl({ url, host, protocol }); const data = request.body || void 0; const cookies = request.cookies; return { url: absoluteUrl, method: request.method, query_string: extractQueryParamsFromUrl(url), headers: headersToDict(headers), cookies, data }; } function getAbsoluteUrl({ url, protocol, host }) { if (url?.startsWith("http")) { return url; } if (url && host) { return `${protocol}://${host}${url}`; } return void 0; } function httpHeadersToSpanAttributes(headers, sendDefaultPii = false, lifecycle = "request") { const spanAttributes = {}; try { Object.entries(headers).forEach(([key, value]) => { if (value == null) { return; } const lowerCasedHeaderKey = key.toLowerCase(); const isCookieHeader = lowerCasedHeaderKey === "cookie" || lowerCasedHeaderKey === "set-cookie"; if (isCookieHeader && typeof value === "string" && value !== "") { const isSetCookie = lowerCasedHeaderKey === "set-cookie"; const semicolonIndex = value.indexOf(";"); const cookieString = isSetCookie && semicolonIndex !== -1 ? value.substring(0, semicolonIndex) : value; const cookies = isSetCookie ? [cookieString] : cookieString.split("; "); for (const cookie of cookies) { const equalSignIndex = cookie.indexOf("="); const cookieKey = equalSignIndex !== -1 ? cookie.substring(0, equalSignIndex) : cookie; const cookieValue = equalSignIndex !== -1 ? cookie.substring(equalSignIndex + 1) : ""; const lowerCasedCookieKey = cookieKey.toLowerCase(); addSpanAttribute({ spanAttributes, headerKey: lowerCasedHeaderKey, cookieKey: lowerCasedCookieKey, value: cookieValue, sendDefaultPii, lifecycle }); } } else { addSpanAttribute({ spanAttributes, headerKey: lowerCasedHeaderKey, value, sendDefaultPii, lifecycle }); } }); } catch { } return spanAttributes; } function normalizeAttributeKey(key) { return key.replace(/-/g, "_"); } function addSpanAttribute({ spanAttributes, headerKey, cookieKey, value, sendDefaultPii, lifecycle }) { const isCookieSubKey = Boolean(cookieKey); const nameForSensitivity = cookieKey || headerKey; const headerValue = handleHttpHeader(nameForSensitivity, value, sendDefaultPii, isCookieSubKey); if (headerValue == null) { return; } const normalizedKey = `http.${lifecycle}.header.${normalizeAttributeKey(headerKey)}${cookieKey ? `.${normalizeAttributeKey(cookieKey)}` : ""}`; spanAttributes[normalizedKey] = headerValue; } function handleHttpHeader(lowerCasedKey, value, sendPii, isCookieSubKey = false) { const snippetsForSensitivity = isCookieSubKey ? [...filteringSnippets.SENSITIVE_KEY_SNIPPETS, ...filteringSnippets.SENSITIVE_COOKIE_NAME_SNIPPETS] : filteringSnippets.SENSITIVE_KEY_SNIPPETS; const isSensitive = sendPii ? snippetsForSensitivity.some((snippet) => lowerCasedKey.includes(snippet)) : [...filteringSnippets.PII_HEADER_SNIPPETS, ...snippetsForSensitivity].some((snippet) => lowerCasedKey.includes(snippet)); if (isSensitive) { return "[Filtered]"; } else if (Array.isArray(value)) { return value.map((v) => v != null ? String(v) : v).join(";"); } else if (typeof value === "string") { return value; } return void 0; } function extractQueryParamsFromUrl(url) { if (!url) { return; } try { const queryParams = new URL(url, "http://s.io").search.slice(1); return queryParams.length ? queryParams : void 0; } catch { return void 0; } } exports.MAX_BODY_BYTE_LENGTH = MAX_BODY_BYTE_LENGTH; exports.captureBodyFromWinterCGRequest = captureBodyFromWinterCGRequest; exports.extractQueryParamsFromUrl = extractQueryParamsFromUrl; exports.getMaxBodyByteLength = getMaxBodyByteLength; exports.headersToDict = headersToDict; exports.httpHeadersToSpanAttributes = httpHeadersToSpanAttributes; exports.httpRequestToRequestData = httpRequestToRequestData; exports.winterCGHeadersToDict = winterCGHeadersToDict; exports.winterCGRequestToRequestData = winterCGRequestToRequestData; //# sourceMappingURL=request.js.map