bruno icon indicating copy to clipboard operation
bruno copied to clipboard

feat: Add RFC 6761–compliant localhost loopback checks so `secure` cookies work on localhost (fixes: #1676)

Open Chriss4123 opened this issue 10 months ago • 9 comments

fixes: #1676

Jira: https://usebruno.atlassian.net/browse/BRU-859

Description

Contribution Checklist:

  • [x] The pull request only addresses one issue or adds one feature.
  • [x] The pull request does not introduce any breaking changes
  • [x] I have added screenshots or gifs to help explain the change if applicable.
  • [x] I have read the contribution guidelines.
  • [x] Create an issue and link to the pull request.

This commit extends how cookies are treated in secure contexts by fully recognizing localhost and loopback IPs as trustworthy origins, matching the de facto behavior of all modern browsers and RFC 6761. Previously, tough-cookie defaulted secure to true only for https: and wss: URLs, causing cookies with secure set to never be sent to localhost.

What Changed

  1. New trustworthy-util.js

    • Implements isPotentiallyTrustworthy(url) by checking:
      • Schemes: https, wss, file
      • Loopback IPs: 127.0.0.1/8 and ::1
      • Hostnames: localhost and *.localhost
    • Adapted from Chromium’s IsLocalhost, IsLoopback and HostNoBracketsPiece, located at:
  2. cookies.js Update

    • Passes the { secure: isPotentiallyTrustworthy(url) } option to cookieJar.getCookiesSync().
    • This overrides tough-cookie’s built-in:
      let secure = options.secure;
      if (secure == null && (context.protocol == "https:" || context.protocol == "wss:")) {
        secure = true;
      }
      
      We do everything tough-cookie does by default—plus treat localhost/loopback the same as modern browsers. No existing functionality is removed, only expanded.

Testing this change

from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/setcookie')
def set_cookie():
    resp = make_response('')
    resp.set_cookie('example', '', secure=True)
    return resp

@app.route('/getcookie')
def get_cookie():
    return 'Cookie is set' if 'example' in request.cookies else 'No cookie set'

app.run()
  • Before: The secure cookie would not be sent to /getcookie after it was set by /setcookie due to secure being set to True.
  • After: The secure cookie would be sent to /getcookie which mirrors the behavior of modern browsers.

All modern browsers (Chrome et al.) and many other API testing clients (Postman et al.) handle http://localhost as a trustworthy origin. It only makes sense that this behavior also exists in Bruno.

Chriss4123 avatar Feb 14 '25 19:02 Chriss4123

@sreelakshmi-bruno - Any chancel to get this merged in?

JoshuaHintze avatar Mar 07 '25 14:03 JoshuaHintze

This will be merged and will be included in the upcoming release which will likely go out on coming Tuesday.

sreelakshmi-bruno avatar Mar 08 '25 07:03 sreelakshmi-bruno

Apologies, we've postponed the release for this PR to April. Thank you for your patience!

sreelakshmi-bruno avatar Mar 24 '25 11:03 sreelakshmi-bruno

@Chriss4123 I see that your PR on salesforce/tough-cookie is already merged. That alone is great news and thanks for your contributions.

Do you think we can just get straight to upgrading tough-cookie once their major release is out?

ramki-bruno avatar Apr 18 '25 08:04 ramki-bruno

@Chriss4123 I see that your PR on salesforce/tough-cookie is already merged. That alone is great news and thanks for your contributions.

Do you think we can just get straight to upgrading tough-cookie once their major release is out?

Yes, that should work. Just keep in mind tough-cookie has been converted to TypeScript a while ago, however you are using an old version which still uses vanilla JS.

Chriss4123 avatar Apr 20 '25 00:04 Chriss4123

Since tough-cookie v6 is still in RC, I think we can go ahead with merging this for now. I'm also adding few tests for this in #4709. Will add a note as well regarding upgrading it.

ramki-bruno avatar May 19 '25 08:05 ramki-bruno

Moved the util to @usebruno/common and fixed it for CLI as well.

ramki-bruno avatar May 19 '25 12:05 ramki-bruno

@ramki-bruno I'd like to keep the bruno-common platform agnostic as it gets bundled. Can you move the logic to bruno-requests ?

helloanoop avatar May 22 '25 10:05 helloanoop

Please use Merge commit

ramki-bruno avatar May 23 '25 08:05 ramki-bruno