Skip to content

Fix doubled base URL in survey "Go Back" navigation - #398

Merged
smarcet merged 1 commit into
masterfrom
fix/survey-go-back-doubled-base-url
Sep 8, 2026
Merged

Fix doubled base URL in survey "Go Back" navigation#398
smarcet merged 1 commit into
masterfrom
fix/survey-go-back-doubled-base-url

Conversation

@JpMaxMan

@JpMaxMan JpMaxMan commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

ref: https://tipit.avaza.com/project/view/188885?view=List#!task=5037004

Problem

Reported on /user-survey/survey-2026/your-openstack-deployments/edit/23400/deployment-decisions: pressing Go Back navigates to a URL with the origin repeated —

https://www.openstack.org/https://www.openstack.org/user-survey/...

— which the browser resolves as a path, and the request fails.

Cause

getPreviousStepUrl() built the URL as:

Controller::join_links(
    Director::absoluteBaseURL(),
    Controller::curr()->Link(),
    ...
)

Controller::join_links() concatenates its arguments and normalises slashes. It does not detect that a later argument is already an absolute URL, so this is correct only while Link() returns a relative path.

Behind the Netlify proxy, Link() is absolute. The application's origin is legacy.openstack.org; www.openstack.org is served by Netlify, which proxies the legacy path prefixes — /user-survey/* among them — to that origin. For those requests _ss_environment.php takes this branch:

if (!empty($_SERVER['HTTP_X_FROM']) && $_SERVER['HTTP_X_FROM'] == 'netlify') {
    define('TRUSTED_PROXY', true);
    define('BASE_URL', 'https://www.openstack.org');
    ...
}

BASE_URL is documented by the framework as a full URL to the webroot (framework/core/Constants.php:11), and Director::baseURL() returns it verbatim with a trailing slash (framework/control/Director.php:559). SiteTree::Link() is built on top of it (cms/code/model/SiteTree.php:469), so in that mode every page link is absolute — not only the survey ones. Any caller prepending the base by hand then emits the origin twice.

Confirmed live on https://www.openstack.org/user-survey/survey-2026/your-organization:

Element Value
data-prev-url on Go Back https://www.openstack.org/https://www.openstack.org/user-survey/survey-2026/about-you
<form action> https://www.openstack.org/user-survey/survey-2026/SurveyStepForm

The absolute form action is the tell: Controller::curr()->Link() is absolute, which is exactly the precondition the old code got wrong.

A request that reaches the origin without the X-From: netlify header takes the other branch — BASE_URL is auto-derived, Link() is relative, and the old code produced a correct URL. That is why the bug only shows up through the proxy.

The result is written to the button's data-prev-url, and survey_builder/js/survey.controller.js assigns it straight to window.location, so the bad URL is what the browser navigates to.

Fix

Wrap the joined path in Director::absoluteURL() instead of prepending Director::absoluteBaseURL(). Director::absoluteURL() returns a URL unchanged when it already begins with http and prepends the base otherwise (framework/control/Director.php:461), so it is idempotent — correct in both configurations above, and it stays correct if either changes.

Applied to all four call sites that prepended the base by hand:

File Path
builders/EntitySurveyRegularStepTemplateUIBuilder.php Go Back on an entity sub-step — the reported case
builders/SurveyAbstractStepTemplateUIBuilder.php Go Back on ordinary survey steps
builders/SurveyReviewStepTemplateUIBuilder.php Go Back from the review step
SurveyPage.php server-side redirect when a requested step is not allowed

Note the bug is not limited to the page it was reported on — through the proxy, Go Back is affected on every survey step, not just entity sub-steps.

These four are the only affected sites. Every other caller of absoluteBaseURL() composes it with Director::makeRelative(), RelativeLink(), SS_HTTPRequest::getURL() or a string literal — all base-free, so none of them can double the origin. EntitySurveyTeamMemberEmailSenderService is in that group and is deliberately left unchanged; it needs an absolute URL for email.

Verification

php -l passes on all four files.

The trigger is confirmed against production (table above) rather than inferred. Still to be checked by hand before merge:

  1. Go Back on an ordinary survey step, on an entity sub-step like the reported one, and from the review step.
  2. The disallowed-step redirect in SurveyPage.php, which is a server-side redirect rather than a JavaScript navigation and so exercises a different path.

Summary by CodeRabbit

  • Bug Fixes
    • Improved URL handling for survey step navigation and redirects.
    • Previous-step links and current-step redirects now consistently resolve to complete absolute URLs, helping navigation work reliably across different site configurations.

Pressing "Go Back" on a survey step could navigate to a URL with the
site origin repeated, for example:

  https://www.openstack.org + https://www.openstack.org/user-survey/...

which the browser then resolves as a path, and the request fails.

getPreviousStepUrl() built its URL as:

  Controller::join_links(
      Director::absoluteBaseURL(),
      Controller::curr()->Link(),
      ...
  )

Controller::join_links() concatenates its arguments and does not detect
that a later argument is already an absolute URL, so this is only
correct while Link() returns a relative path. Link() is built on
Director::baseURL(), which returns Director.alternate_base_url when one
is configured -- as SS_BASE_URL does. Setting SS_BASE_URL to a full
origin, which is reasonable when the site sits behind a reverse proxy,
makes Link() absolute and the origin is then emitted twice.

Use Director::absoluteURL() around the joined relative path instead. It
returns a URL unchanged when it already begins with http and prepends
the base otherwise, so the result is correct whether Link() is relative
or absolute, and stays correct if that configuration changes again.

Applied to all four call sites that prepended the base by hand: the
three getPreviousStepUrl() implementations behind the "Go Back" button,
and the server-side redirect in SurveyPage when a requested step is not
allowed.

EntitySurveyTeamMemberEmailSenderService also calls absoluteBaseURL(),
but composes it with Director::makeRelative(), which strips any base
first. It is unaffected and left unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uz3HpgshDPXb5GL7cdYZJh
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 38d0e08e-98e7-4efb-939c-6bea4b49b4bc

📥 Commits

Reviewing files that changed from the base of the PR and between 2172298 and b4bba8a.

📒 Files selected for processing (4)
  • survey_builder/code/ui/frontend/SurveyPage.php
  • survey_builder/code/ui/frontend/builders/EntitySurveyRegularStepTemplateUIBuilder.php
  • survey_builder/code/ui/frontend/builders/SurveyAbstractStepTemplateUIBuilder.php
  • survey_builder/code/ui/frontend/builders/SurveyReviewStepTemplateUIBuilder.php

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Survey redirects and previous-step links now join route paths before resolving them with Director::absoluteURL().

Changes

Survey URL resolution

Layer / File(s) Summary
Absolute URL construction
survey_builder/code/ui/frontend/SurveyPage.php, survey_builder/code/ui/frontend/builders/*
Survey redirects and previous-step URLs now pass joined controller paths and route segments to Director::absoluteURL() instead of prefixing Director::absoluteBaseURL().

Estimated code review effort: 1 (Trivial) | ~5 minutes

Merge Risk: ⚪ Minimal · up to b4bba

Survey back-navigation links and disallowed-step redirects now avoid duplicating the configured site origin when controller links are already absolute. No current merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 4 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: fixing duplicated base URLs in survey "Go Back" navigation.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/survey-go-back-doubled-base-url

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@smarcet smarcet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@smarcet
smarcet merged commit bbdcb65 into master Sep 8, 2026
1 check passed
smarcet pushed a commit that referenced this pull request Sep 8, 2026
Pressing "Go Back" on a survey step could navigate to a URL with the
site origin repeated, for example:

  https://www.openstack.org + https://www.openstack.org/user-survey/...

which the browser then resolves as a path, and the request fails.

getPreviousStepUrl() built its URL as:

  Controller::join_links(
      Director::absoluteBaseURL(),
      Controller::curr()->Link(),
      ...
  )

Controller::join_links() concatenates its arguments and does not detect
that a later argument is already an absolute URL, so this is only
correct while Link() returns a relative path. Link() is built on
Director::baseURL(), which returns Director.alternate_base_url when one
is configured -- as SS_BASE_URL does. Setting SS_BASE_URL to a full
origin, which is reasonable when the site sits behind a reverse proxy,
makes Link() absolute and the origin is then emitted twice.

Use Director::absoluteURL() around the joined relative path instead. It
returns a URL unchanged when it already begins with http and prepends
the base otherwise, so the result is correct whether Link() is relative
or absolute, and stays correct if that configuration changes again.

Applied to all four call sites that prepended the base by hand: the
three getPreviousStepUrl() implementations behind the "Go Back" button,
and the server-side redirect in SurveyPage when a requested step is not
allowed.

EntitySurveyTeamMemberEmailSenderService also calls absoluteBaseURL(),
but composes it with Director::makeRelative(), which strips any base
first. It is unaffected and left unchanged.


Claude-Session: https://claude.ai/code/session_01Uz3HpgshDPXb5GL7cdYZJh

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants