From a4fff01647b683a4188ae03f3e818905100178a9 Mon Sep 17 00:00:00 2001 From: Chriss4123 Date: Fri, 14 Feb 2025 20:48:48 +0200 Subject: [PATCH 1/2] Support Secure cookies for localhost and loopback addresses --- packages/bruno-electron/src/utils/cookies.js | 5 +- .../src/utils/trustworthy-util.js | 103 ++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 packages/bruno-electron/src/utils/trustworthy-util.js diff --git a/packages/bruno-electron/src/utils/cookies.js b/packages/bruno-electron/src/utils/cookies.js index 7585e9a8a..5095a8b20 100644 --- a/packages/bruno-electron/src/utils/cookies.js +++ b/packages/bruno-electron/src/utils/cookies.js @@ -1,4 +1,5 @@ const { Cookie, CookieJar } = require('tough-cookie'); +const { isPotentiallyTrustworthy } = require('./trustworthy-util'); const each = require('lodash/each'); const moment = require('moment'); @@ -12,7 +13,9 @@ const addCookieToJar = (setCookieHeader, requestUrl) => { }; const getCookiesForUrl = (url) => { - return cookieJar.getCookiesSync(url); + return cookieJar.getCookiesSync(url, { + secure: isPotentiallyTrustworthy(url) + }); }; const getCookieStringForUrl = (url) => { diff --git a/packages/bruno-electron/src/utils/trustworthy-util.js b/packages/bruno-electron/src/utils/trustworthy-util.js new file mode 100644 index 000000000..f709e5e80 --- /dev/null +++ b/packages/bruno-electron/src/utils/trustworthy-util.js @@ -0,0 +1,103 @@ +const { URL } = require('url'); +const net = require('net'); + +const isLoopbackV4 = (address) => { + // 127.0.0.0/8: first octet = 127 + const octets = address.split('.'); + return ( + octets.length === 4 + ) && parseInt(octets[0], 10) === 127; +} + +const isLoopbackV6 = (address) => { + // new URL(...) follows the WHATWG URL Standard + // which compresses IPv6 addresses, therefore the IPv6 + // loopback address will always be compressed to '[::1]': + // https://url.spec.whatwg.org/#concept-ipv6-serializer + return (address === '::1'); +} + +const isIpLoopback = (address) => { + if (net.isIPv4(address)) { + return isLoopbackV4(address); + } + + if (net.isIPv6(address)) { + return isLoopbackV6(address); + } + + return false; +} + +const isNormalizedLocalhostTLD = (host) => { + return host.toLowerCase().endsWith('.localhost'); +} + +const isLocalHostname = (host) => { + return host.toLowerCase() === 'localhost' || + isNormalizedLocalhostTLD(host); +} + +/** + * Removes leading and trailing square brackets if present. + * Adapted from https://github.com/chromium/chromium/blob/main/url/gurl.cc#L440-L448 + * + * @param {string} host + * @returns {string} + */ +const hostNoBrackets = (host) => { + if (host.length >= 2 && host.startsWith('[') && host.endsWith(']')) { + return host.substring(1, host.length - 1); + } + return host; +} + +/** + * Determines if a URL string represents a potentially trustworthy origin. + * + * A URL is considered potentially trustworthy if it: + * - Uses HTTPS, WSS or file schemes + * - Points to a loopback address (IPv4 127.0.0.0/8 or IPv6 ::1) + * - Uses localhost or *.localhost hostnames + * + * @param {string} urlString - The URL to check + * @returns {boolean} + * @see {@link https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin W3C Spec} + */ +const isPotentiallyTrustworthy = (urlString) => { + let url; + + // try ... catch doubles as an opaque origin check + try { + url = new URL(urlString); + } catch { + return false; + } + + const scheme = url.protocol.replace(':', '').toLowerCase(); + const hostname = hostNoBrackets( + url.hostname + ).replace(/\.+$/, ''); + + if ( + scheme === 'https' || + scheme === 'wss' || + scheme === 'file' // https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin + ) { + return true; + } + + // If it's already an IP literal, check if it's a loopback address + if (net.isIP(hostname)) { + return isIpLoopback(hostname); + } + + // RFC 6761 states that localhost names will always resolve + // to the respective IP loopback address: + // https://datatracker.ietf.org/doc/html/rfc6761#section-6.3 + return isLocalHostname(hostname); +} + +module.exports = { + isPotentiallyTrustworthy +}; \ No newline at end of file From 2c3d2ff6a74d259993dcc4923f6c44232eb43a21 Mon Sep 17 00:00:00 2001 From: ramki-bruno Date: Mon, 19 May 2025 18:09:38 +0530 Subject: [PATCH 2/2] Make Secure-local-cookies work in CLI as well --- packages/bruno-cli/src/utils/cookies.js | 5 ++++- packages/bruno-electron/src/utils/cookies.js | 4 ++-- packages/bruno-requests/src/index.ts | 2 ++ .../src/utils/cookie-utils.js} | 14 ++++++++------ packages/bruno-requests/src/utils/index.ts | 1 + 5 files changed, 17 insertions(+), 9 deletions(-) rename packages/{bruno-electron/src/utils/trustworthy-util.js => bruno-requests/src/utils/cookie-utils.js} (89%) create mode 100644 packages/bruno-requests/src/utils/index.ts diff --git a/packages/bruno-cli/src/utils/cookies.js b/packages/bruno-cli/src/utils/cookies.js index acb58b505..01a82316b 100644 --- a/packages/bruno-cli/src/utils/cookies.js +++ b/packages/bruno-cli/src/utils/cookies.js @@ -1,5 +1,6 @@ const { Cookie, CookieJar } = require('tough-cookie'); const each = require('lodash/each'); +const { isPotentiallyTrustworthyOrigin } = require('@usebruno/requests').utils; const cookieJar = new CookieJar(); @@ -11,7 +12,9 @@ const addCookieToJar = (setCookieHeader, requestUrl) => { }; const getCookiesForUrl = (url) => { - return cookieJar.getCookiesSync(url); + return cookieJar.getCookiesSync(url, { + secure: isPotentiallyTrustworthyOrigin(url) + }); }; const getCookieStringForUrl = (url) => { diff --git a/packages/bruno-electron/src/utils/cookies.js b/packages/bruno-electron/src/utils/cookies.js index 5095a8b20..7f3751eaf 100644 --- a/packages/bruno-electron/src/utils/cookies.js +++ b/packages/bruno-electron/src/utils/cookies.js @@ -1,7 +1,7 @@ const { Cookie, CookieJar } = require('tough-cookie'); -const { isPotentiallyTrustworthy } = require('./trustworthy-util'); const each = require('lodash/each'); const moment = require('moment'); +const { isPotentiallyTrustworthyOrigin } = require('@usebruno/requests').utils; const cookieJar = new CookieJar(); @@ -14,7 +14,7 @@ const addCookieToJar = (setCookieHeader, requestUrl) => { const getCookiesForUrl = (url) => { return cookieJar.getCookiesSync(url, { - secure: isPotentiallyTrustworthy(url) + secure: isPotentiallyTrustworthyOrigin(url) }); }; diff --git a/packages/bruno-requests/src/index.ts b/packages/bruno-requests/src/index.ts index 5513916c5..01850f3e4 100644 --- a/packages/bruno-requests/src/index.ts +++ b/packages/bruno-requests/src/index.ts @@ -1 +1,3 @@ export { addDigestInterceptor, getOAuth2Token } from './auth'; + +export * as utils from './utils'; diff --git a/packages/bruno-electron/src/utils/trustworthy-util.js b/packages/bruno-requests/src/utils/cookie-utils.js similarity index 89% rename from packages/bruno-electron/src/utils/trustworthy-util.js rename to packages/bruno-requests/src/utils/cookie-utils.js index f709e5e80..6a1a5ac57 100644 --- a/packages/bruno-electron/src/utils/trustworthy-util.js +++ b/packages/bruno-requests/src/utils/cookie-utils.js @@ -1,5 +1,5 @@ -const { URL } = require('url'); -const net = require('net'); +const { URL } = require('node:url'); +const net = require('node:net'); const isLoopbackV4 = (address) => { // 127.0.0.0/8: first octet = 127 @@ -64,14 +64,16 @@ const hostNoBrackets = (host) => { * @returns {boolean} * @see {@link https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin W3C Spec} */ -const isPotentiallyTrustworthy = (urlString) => { +const isPotentiallyTrustworthyOrigin = (urlString) => { let url; // try ... catch doubles as an opaque origin check try { url = new URL(urlString); - } catch { - return false; + } catch (e) { + if (e instanceof TypeError && e.code === 'ERR_INVALID_URL') { + return false; + } else throw e; } const scheme = url.protocol.replace(':', '').toLowerCase(); @@ -99,5 +101,5 @@ const isPotentiallyTrustworthy = (urlString) => { } module.exports = { - isPotentiallyTrustworthy + isPotentiallyTrustworthyOrigin }; \ No newline at end of file diff --git a/packages/bruno-requests/src/utils/index.ts b/packages/bruno-requests/src/utils/index.ts new file mode 100644 index 000000000..dd94dd186 --- /dev/null +++ b/packages/bruno-requests/src/utils/index.ts @@ -0,0 +1 @@ +export * from './cookie-utils';