Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions core/src/client/AssayDesigner/AssayDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,9 @@ export class App extends React.Component<any, State> {

navigate(defaultUrl: string) {
this._dirty = false;

window.location.href = this.state.returnUrl || defaultUrl;
const redirectUrl = this.state.returnUrl || defaultUrl;
// TODO refactor this and other usages in platform/core/src/client to a helper safeRedirect() function from @labkey/components
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.

as noted in the commit message, I'll wait until this gets merged to develop to address this TODO so that I don't need to bump @labkey/components for this branch (and have to handle the merge forward conflicts)

window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
}

onCancel = (): void => {
Expand Down
5 changes: 2 additions & 3 deletions core/src/client/DataClassDesigner/DataClassDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ class DataClassDesignerWrapper extends React.Component<any, State> {

navigate(defaultUrl: string) {
this._dirty = false;

const returnUrl = ActionURL.getReturnUrl();
window.location.href = returnUrl || defaultUrl;
const redirectUrl = ActionURL.getReturnUrl() || defaultUrl;
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
}

onCancel = () => {
Expand Down
5 changes: 2 additions & 3 deletions core/src/client/DatasetDesigner/DatasetDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ class DatasetDesigner extends PureComponent<any, State> {

navigate(defaultUrl: string): void {
this._dirty = false;

const returnUrl = ActionURL.getReturnUrl();
window.location.href = returnUrl || defaultUrl;
const redirectUrl = ActionURL.getReturnUrl() || defaultUrl;
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
}

navigateOnComplete(model: DatasetModel): void {
Expand Down
5 changes: 2 additions & 3 deletions core/src/client/DomainDesigner/DomainDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,8 @@ class DomainDesigner extends React.PureComponent<any, Partial<IAppState>> {

navigate = (): void => {
this._dirty = false;

const returnUrl = ActionURL.getReturnUrl();
window.location.href = returnUrl || ActionURL.buildURL('project', 'begin', getServerContext().container.path);
const redirectUrl = ActionURL.getReturnUrl() || ActionURL.buildURL('project', 'begin', getServerContext().container.path);
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
};

renderWarningConfirm() {
Expand Down
5 changes: 2 additions & 3 deletions core/src/client/IssuesListDesigner/IssuesListDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ class IssuesListDesigner extends React.Component<{}, State> {

navigate = (defaultUrl: string) => {
this._dirty = false;

const returnUrl = ActionURL.getReturnUrl();
window.location.href = returnUrl || defaultUrl;
const redirectUrl = ActionURL.getReturnUrl() || defaultUrl;
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
};

onComplete = (model: IssuesListDefModel) => {
Expand Down
4 changes: 2 additions & 2 deletions core/src/client/ListDesigner/ListDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ export class ListDesigner extends React.Component<Props, State> {

navigate = async (returnUrlProvider: () => Promise<string>, model?: ListModel): Promise<void> => {
this._dirty = false;

window.location.href = this.getReturnUrl(model) ?? (await returnUrlProvider());
const redirectUrl = this.getReturnUrl(model) ?? (await returnUrlProvider());
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
};

getReturnUrl = (model?: ListModel): string => {
Expand Down
5 changes: 2 additions & 3 deletions core/src/client/SampleTypeDesigner/SampleTypeDesigner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,8 @@ class SampleTypeDesignerWrapper extends React.PureComponent<any, State> {

navigate(defaultUrl: string) {
this._dirty = false;

const returnUrl = ActionURL.getReturnUrl();
window.location.href = returnUrl || defaultUrl;
const redirectUrl = ActionURL.getReturnUrl() || defaultUrl;
window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: redirectUrl });
}

render() {
Expand Down
18 changes: 14 additions & 4 deletions core/src/org/labkey/core/CoreController.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
import org.labkey.api.action.ExportAction;
import org.labkey.api.action.MutatingApiAction;
import org.labkey.api.action.ReadOnlyApiAction;
import org.labkey.api.action.ReturnUrlForm;
import org.labkey.api.action.SimpleApiJsonForm;
import org.labkey.api.action.SimpleRedirectAction;
import org.labkey.api.action.SimpleViewAction;
import org.labkey.api.action.SpringActionController;
import org.labkey.api.admin.AbstractFolderContext.ExportType;
Expand Down Expand Up @@ -204,10 +206,6 @@

import static org.labkey.api.view.template.WarningService.SESSION_WARNINGS_BANNER_KEY;

/**
* User: jeckels
* Date: Jan 4, 2007
*/
public class CoreController extends SpringActionController
{
private static final Map<Container, Content> _customStylesheetCache = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -2908,4 +2906,16 @@ public void setProvider(String provider)

}

// Called by various client components to ensure safe redirects, GitHub Issue #1023. This action redirects to
// local URLs only, never to an external site, even if the host is on the "Allowed External Redirect Hosts" list.
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.

What is it that assures this uses local URLs only? Doesn't seem to be codified here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

First, ActionURL is guaranteed to be a local URL. You can give it an external URL and it'll ignore the schema, host, and port, always using the local AppProps values (e.g., when calling getURIString()). Second, SimpleRedirectAction throws RedirectException, which now guarantees a local redirect. This is the result of a recent refactor I did to tighten up our server-side redirect handling. The only way to redirect externally now is via ExternalRedirectException (which ensures an allowed host) or UnsafeExternalRedirectException (where caller is responsible for ensuring the URL is safe).

@SuppressWarnings("unused")
@RequiresNoPermission
public static class SafeRedirectAction extends SimpleRedirectAction<ReturnUrlForm>
{
@Override
public ActionURL getRedirectURL(ReturnUrlForm form) throws Exception
{
return form.getReturnActionURL(AppProps.getInstance().getHomePageActionURL());
}
}
}