Feat/handling authorization header leaks to third party servers on redirect#8380
Conversation
…ensitive headers on cross-origin redirects
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (4)
WalkthroughThis PR adds a ChangesforwardAuthorizationHeader feature
Sequence Diagram(s)sequenceDiagram
participant RequestPane
participant AxiosConfig
participant RedirectTarget
participant OriginCheck
RequestPane->>AxiosConfig: request.settings.forwardAuthorizationHeader
AxiosConfig->>RedirectTarget: send request with redirect handling
RedirectTarget-->>AxiosConfig: 3xx Location header
AxiosConfig->>OriginCheck: compare original URL vs redirect URL
OriginCheck-->>AxiosConfig: same-origin / cross-origin
AxiosConfig->>AxiosConfig: strip or preserve Authorization headers
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/bruno-converters/src/opencollection/items/http.ts (1)
105-114: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winSame
false-default inconsistency as ingraphql.ts— affects both conversion directions here.
fromOpenCollectionHttpItem(line 111) andtoOpenCollectionHttpItem(line 228) both default a missing/non-booleanforwardAuthorizationHeadertofalse. MeanwhileparseHttpRequest.ts/stringifyHttpRequest.tsin bruno-filestore default the same field totruefor the identical "field absent" case. Since these two packages both implement conversion to/from the sameHttpRequestSettings.forwardAuthorizationHeadercontract, this is a real cross-package contract break: importing/exporting a legacy collection (no explicit setting) through bruno-converters silently changes auth-forwarding behavior on redirects, while doing the same round-trip through bruno-filestore preserves it. Recommend unifying ontrueas the "missing value" default everywhere, since the secure-by-defaultfalseis already applied explicitly at request-creation time (cURL import, ReduxnewEphemeralHttpRequest, etc.) rather than needing to be re-derived here.🔧 Suggested fix
- forwardAuthorizationHeader: typeof ocRequest.settings.forwardAuthorizationHeader === 'boolean' ? ocRequest.settings.forwardAuthorizationHeader : false + forwardAuthorizationHeader: typeof ocRequest.settings.forwardAuthorizationHeader === 'boolean' ? ocRequest.settings.forwardAuthorizationHeader : true- forwardAuthorizationHeader: typeof brunoSettings?.forwardAuthorizationHeader === 'boolean' ? brunoSettings.forwardAuthorizationHeader : false + forwardAuthorizationHeader: typeof brunoSettings?.forwardAuthorizationHeader === 'boolean' ? brunoSettings.forwardAuthorizationHeader : trueAlso applies to: 227-229
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/src/opencollection/items/http.ts` around lines 105 - 114, `fromOpenCollectionHttpItem` and `toOpenCollectionHttpItem` in the HTTP converter are defaulting a missing `forwardAuthorizationHeader` to false, which conflicts with the shared `HttpRequestSettings` behavior used elsewhere. Update both conversion paths to treat an absent or non-boolean `forwardAuthorizationHeader` as true, matching `parseHttpRequest.ts` and `stringifyHttpRequest.ts` so round-trips preserve legacy behavior; keep the explicit boolean check in the `settings` mapping and align the default with the `forwardAuthorizationHeader` contract.
🧹 Nitpick comments (4)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (1)
1403-1406: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor duplication of default item settings shape.
The
{ encodeUrl: true, forwardAuthorizationHeader: false }literal also appears innewEphemeralHttpRequestinpackages/bruno-app/src/providers/ReduxStore/slices/collections/index.js(lines 966-969). Consider extracting a sharedDEFAULT_HTTP_ITEM_SETTINGSconstant if a third setting is ever added, to avoid drift between the two defaults.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js` around lines 1403 - 1406, The HTTP item settings default shape is duplicated between actions.js and the newEphemeralHttpRequest flow, which can drift over time. Extract the shared default object into a common DEFAULT_HTTP_ITEM_SETTINGS constant (or equivalent shared symbol used by both create/update paths) and have both the settings initializer in actions.js and newEphemeralHttpRequest in collections/index.js reference it instead of inlining the literal.packages/bruno-converters/src/postman/postman-to-bruno.js (1)
508-511: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winConsider adding/asserting converter test coverage for the new default.
This and the sibling Insomnia/OpenAPI/Swagger2 converters all now hardcode
forwardAuthorizationHeader: falsefor every imported request — a security-relevant behavior change. Worth confirming existing converter test suites assert this default so a future refactor doesn't silently regress it.As per coding guidelines, "Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/src/postman/postman-to-bruno.js` around lines 508 - 511, The new hardcoded forwardAuthorizationHeader default in the Postman converter needs test coverage so this security-relevant behavior is locked in. Update the relevant converter test suite around postmanToBruno (and, if applicable, the sibling converter test suites) to assert imported requests always set forwardAuthorizationHeader to false, so future refactors cannot silently change the default.Source: Coding guidelines
tests/request/settings/redirect-auth-strip.spec.ts (2)
8-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract repeated sidebar locator into a variable.
The
#sidebar-collection-name→getByText('settings-test')locator is rebuilt identically in both tests. As per path instructions, "Use locator variables for locators."Also applies to: 34-35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/request/settings/redirect-auth-strip.spec.ts` around lines 8 - 9, The repeated sidebar locator built with `#sidebar-collection-name` and getByText('settings-test') should be extracted into a shared locator variable. Update the affected tests in redirect-auth-strip.spec.ts to define a reusable locator once and reuse it for both the visibility assertion and the click, following the existing locator-variable pattern used elsewhere in the test file.Source: Path instructions
4-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap test phases in
test.stepfor readable reports.Neither test breaks its Arrange/Act/Assert/Cleanup phases into
test.stepcalls. As per path instructions, "Promote the use oftest.stepas much as possible so the generated reports are easier to read."♻️ Example refactor for the first test
test('should strip Authorization and Proxy-Authorization on cross-origin redirects when setting is OFF', async ({ pageWithUserData: page }) => { - // Open collection - await expect(page.locator('`#sidebar-collection-name`').getByText('settings-test')).toBeVisible(); - await page.locator('`#sidebar-collection-name`').getByText('settings-test').click(); - - // Open request - await page.getByRole('complementary').getByText('cross-origin-redirect-auth-strip').click(); - - // Send request - await page.getByTestId('send-arrow-icon').click(); - - // Verify status code - await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 }); - - // Verify headers are stripped - const responseTexts = await page.getByTestId('response-preview-container').locator('.CodeMirror-scroll').allInnerTexts(); - const fullText = responseTexts.join('\n'); - expect(fullText).not.toContain('"authorization":'); - expect(fullText).not.toContain('"proxy-authorization":'); - - // Close tab - await page.locator('.close-icon-container').click({ force: true }); + const collectionItem = page.locator('`#sidebar-collection-name`').getByText('settings-test'); + + await test.step('Open collection and request', async () => { + await expect(collectionItem).toBeVisible(); + await collectionItem.click(); + await page.getByRole('complementary').getByText('cross-origin-redirect-auth-strip').click(); + }); + + await test.step('Send request', async () => { + await page.getByTestId('send-arrow-icon').click(); + await expect(page.getByTestId('response-status-code')).toContainText('200', { timeout: 15000 }); + }); + + await test.step('Verify headers are stripped', async () => { + const responseTexts = await page.getByTestId('response-preview-container').locator('.CodeMirror-scroll').allInnerTexts(); + const fullText = responseTexts.join('\n'); + expect(fullText).not.toContain('"authorization":'); + expect(fullText).not.toContain('"proxy-authorization":'); + }); + + await test.step('Cleanup', async () => { + await page.locator('.close-icon-container').click({ force: true }); + }); });Also applies to: 30-54
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/request/settings/redirect-auth-strip.spec.ts` around lines 4 - 28, The test in redirect-auth-strip.spec.ts should be refactored to wrap its Arrange/Act/Assert/Cleanup phases in test.step blocks for clearer Playwright reports. Update the cross-origin-redirect-auth-strip flow by grouping the existing page interactions and assertions inside named steps within the current test('should strip Authorization and Proxy-Authorization on cross-origin redirects when setting is OFF') so the sequence is easier to follow. Apply the same pattern to the other affected test in this file as well, keeping the existing locators and assertions intact while moving them into test.step calls.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/bruno-converters/src/opencollection/items/graphql.ts`:
- Around line 187-188: The GraphQL request setting default is inconsistent
across converters: `forwardAuthorizationHeader` is currently defaulted to
`false` in the OpenCollection mapping, which conflicts with the filestore
GraphQL parsing/stringifying and the HTTP item conversion paths. Update the
`graphql.ts` conversion logic (and any matching
`toOpenCollectionHttpItem`/`fromOpenCollectionHttpItem` behavior if needed) so
legacy or missing `GraphQLRequestSettings.forwardAuthorizationHeader` values
default to `true`, while keeping `false` only for explicitly created new
requests handled by `NewRequest`/`actions.js`/`collections/index.js`.
---
Outside diff comments:
In `@packages/bruno-converters/src/opencollection/items/http.ts`:
- Around line 105-114: `fromOpenCollectionHttpItem` and
`toOpenCollectionHttpItem` in the HTTP converter are defaulting a missing
`forwardAuthorizationHeader` to false, which conflicts with the shared
`HttpRequestSettings` behavior used elsewhere. Update both conversion paths to
treat an absent or non-boolean `forwardAuthorizationHeader` as true, matching
`parseHttpRequest.ts` and `stringifyHttpRequest.ts` so round-trips preserve
legacy behavior; keep the explicit boolean check in the `settings` mapping and
align the default with the `forwardAuthorizationHeader` contract.
---
Nitpick comments:
In `@packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js`:
- Around line 1403-1406: The HTTP item settings default shape is duplicated
between actions.js and the newEphemeralHttpRequest flow, which can drift over
time. Extract the shared default object into a common DEFAULT_HTTP_ITEM_SETTINGS
constant (or equivalent shared symbol used by both create/update paths) and have
both the settings initializer in actions.js and newEphemeralHttpRequest in
collections/index.js reference it instead of inlining the literal.
In `@packages/bruno-converters/src/postman/postman-to-bruno.js`:
- Around line 508-511: The new hardcoded forwardAuthorizationHeader default in
the Postman converter needs test coverage so this security-relevant behavior is
locked in. Update the relevant converter test suite around postmanToBruno (and,
if applicable, the sibling converter test suites) to assert imported requests
always set forwardAuthorizationHeader to false, so future refactors cannot
silently change the default.
In `@tests/request/settings/redirect-auth-strip.spec.ts`:
- Around line 8-9: The repeated sidebar locator built with
`#sidebar-collection-name` and getByText('settings-test') should be extracted into
a shared locator variable. Update the affected tests in
redirect-auth-strip.spec.ts to define a reusable locator once and reuse it for
both the visibility assertion and the click, following the existing
locator-variable pattern used elsewhere in the test file.
- Around line 4-28: The test in redirect-auth-strip.spec.ts should be refactored
to wrap its Arrange/Act/Assert/Cleanup phases in test.step blocks for clearer
Playwright reports. Update the cross-origin-redirect-auth-strip flow by grouping
the existing page interactions and assertions inside named steps within the
current test('should strip Authorization and Proxy-Authorization on cross-origin
redirects when setting is OFF') so the sequence is easier to follow. Apply the
same pattern to the other affected test in this file as well, keeping the
existing locators and assertions intact while moving them into test.step calls.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a6d54005-65fa-4142-a010-101e0bf42957
📒 Files selected for processing (27)
packages/bruno-app/src/components/RequestPane/Settings/index.jspackages/bruno-app/src/components/Sidebar/NewRequest/index.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/actions.jspackages/bruno-app/src/providers/ReduxStore/slices/collections/index.jspackages/bruno-cli/src/runner/run-single-request.jspackages/bruno-cli/src/utils/axios-instance.jspackages/bruno-cli/tests/utils/axios-instance.spec.jspackages/bruno-converters/src/insomnia/insomnia-to-bruno.jspackages/bruno-converters/src/openapi/openapi-to-bruno.jspackages/bruno-converters/src/openapi/swagger2-to-bruno.jspackages/bruno-converters/src/opencollection/items/graphql.tspackages/bruno-converters/src/opencollection/items/http.tspackages/bruno-converters/src/postman/postman-to-bruno.jspackages/bruno-electron/src/ipc/network/axios-instance.jspackages/bruno-electron/src/ipc/network/index.jspackages/bruno-electron/tests/network/axios-instance.spec.jspackages/bruno-filestore/src/formats/yml/items/parseGraphQLRequest.tspackages/bruno-filestore/src/formats/yml/items/parseHttpRequest.tspackages/bruno-filestore/src/formats/yml/items/stringifyGraphQLRequest.tspackages/bruno-filestore/src/formats/yml/items/stringifyHttpRequest.tspackages/bruno-lang/v2/src/bruToJson.jspackages/bruno-schema-types/src/collection/item.tspackages/bruno-schema/src/collections/index.jspackages/bruno-tests/src/redirect/index.jstests/request/settings/collection/cross-origin-redirect-auth-forward.brutests/request/settings/collection/cross-origin-redirect-auth-strip.brutests/request/settings/redirect-auth-strip.spec.ts
| content: null | ||
| } | ||
| }, | ||
| settings: cloneDeep(DEFAULT_HTTP_ITEM_SETTINGS), |
There was a problem hiding this comment.
do we need cloneDeep here? DEFAULT_HTTP_ITEM_SETTINGS is lain flat object with only primitive values.
| maxRedirects: 5, | ||
| timeout: 'inherit' | ||
| timeout: 'inherit', | ||
| forwardAuthorizationHeader: true |
There was a problem hiding this comment.
can we please change the name to something like forwardAuthorizationOnRedirect or shouldForwardAuthorizationHeader?
There was a problem hiding this comment.
Earlier it was named forwardAuthorizationOnRedirect but after discussing with Bijin and Anoop, forwardAuthorizationHeader was finalised yesterday.
| }); | ||
| }); | ||
|
|
||
| describe('axios-instance: cross-origin redirects authorization stripping', () => { |
There was a problem hiding this comment.
can we please add one test for redirect chains to verify stripping happens correctly throughout multiple redirects.
| // Redirected call should strip auth headers but keep custom headers | ||
| expect(calls[1].headers['Authorization']).toBeUndefined(); | ||
| expect(calls[1].headers['Proxy-Authorization']).toBeUndefined(); | ||
| expect(calls[1].headers['Custom-Header']).toBe('keep-me'); |
There was a problem hiding this comment.
also add expect(calls[1].url).toBe('https://other-domain.com/target');
This ensures the second request was made to the redirected URL, not just with modified headers.
We can also add the same assertions to other tests.
Description
This PR introduces the
forwardAuthorizationHeadersetting to prevent sensitive authentication headers (AuthorizationandProxy-Authorization) from leaking to third-party/cross-origin servers during redirects.Key Changes:
bruno-cliandbruno-electron).forwardAuthorizationHeaderis set tofalse, the sensitiveAuthorizationandProxy-Authorizationheaders are stripped before following the redirect.Forward Auth Headers on Redirecttoggle under the request settings pane.false(secure by default).true(backward-compatible) to preserve current workflows unless explicitly updated by the user.bruno-schema,bruno-schema-types, andbruno-lang(bruToJson.js) to support the new request property.bruno-convertersto support this setting on GraphQL and HTTP requests without modifying external dependency manifests.bruno-cli(packages/bruno-cli/tests/utils/axios-instance.spec.js) andbruno-electron(packages/bruno-electron/tests/network/axios-instance.spec.js) covering same-origin vs. cross-origin redirects with varying settings.tests/request/settings/redirect-auth-strip.spec.ts) using local redirect test routes.Contribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.
Credits
Special thanks to
@abhishek-brunofor the initial implementation and research of the cross-origin header stripping logic in #7578. This PR builds upon that implementation by adding the requested settings configuration for backward compatibility.Summary by CodeRabbit
Summary by CodeRabbit
New Features
Authorization/Proxy-Authorizationheaders are forwarded during redirects.Bug Fixes
Tests