CVE-2022-23618 (Java, published 2022-02-09T21:15Z)
Description: value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In affected versions there is no protection against URL redirection to untrusted sites, in particular some well known parameters (xredirect) can be used to perform url redirections. This problem has been patched in XWiki 12.10.7 and XWiki 13.3RC1. Users are advised to update. There are no known workarounds for this issue.
Severity: MEDIUM (CVSS v3 base score: 6.1)
Weakness: CWE-601 \u2014 URL Redirection to Untrusted Site ('Open Redirect')
A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
Repository: https://github.com/xwiki/xwiki-platform
Files changed:
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/user/impl/xwiki/MyFormAuthenticator.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/XWikiAction.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/XWikiServletResponse.java
xwiki-platform-core/xwiki-platform-url/pom.xml
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-api/pom.xml
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-api/src/main/java/org/xwiki/url/URLConfiguration.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-api/src/main/java/org/xwiki/url/URLSecurityManager.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/pom.xml
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/main/java/org/xwiki/url/internal/DefaultURLConfiguration.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/main/java/org/xwiki/url/internal/DefaultURLSecurityManager.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/main/java/org/xwiki/url/internal/XWikiServerClassListener.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/main/resources/META-INF/components.txt
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/test/java/org/xwiki/url/internal/DefaultURLSecurityManagerTest.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/test/java/org/xwiki/url/internal/RelativeExtendedURLTest.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-default/src/test/java/org/xwiki/url/internal/URLExecutionContextInitializerTest.java
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-schemes/xwiki-platform-url-scheme-filesystem/pom.xml
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-schemes/xwiki-platform-url-scheme-reference/pom.xml
xwiki-platform-core/xwiki-platform-url/xwiki-platform-url-schemes/xwiki-platform-url-scheme-standard/pom.xml
xwiki-platform-tools/xwiki-platform-tool-configuration-resources/src/main/resources/xwiki.properties.vm
Commit message: XWIKI-10309: Check URL domains based on a whitelist (#1592)

Introduce a new property for listing the trusted domains and API to
check an URL against that list and the aliases used in subwikis.

  * Add new property url.trustedDomains in xwiki.properties
  * Add new API in URLConfiguration to retrieve this configuration value
  * Create a new URLSecurityManager responsible to check if an URL can
    be trusted based on this property and on the subwikis configurations
  * Introduce a new listener to invalidate the cache of
    URLSecurityManager whenever a XWikiServerClass xobject is
added/updated/deleted
  * Move URL API implementations to URL default module
  * Add a new property url.enableTrustedDomains as a global switch off the
    checks on domains to avoid breaking behaviours on existing instances
  * Add a constant property in URLSecurityManager to be set in
    ExecutionContext to allow temporary switch off the check for
extensions
  * Use both those switches in DefaultURLSecurityManager to prevent
    performing the check when needed

Vulnerable code:
                String continueToURL = getContinueToURL(request);
    <!-- The reason for this low TPC value is because this module is tested using integration tests in the various
         URL Scheme modules -->
    <xwiki.jacoco.instructionRatio>0.45</xwiki.jacoco.instructionRatio>
package org.xwiki.url;
package org.xwiki.url;
      <artifactId>xwiki-platform-url-api</artifactId>
      <artifactId>xwiki-platform-url-api</artifactId>
      <artifactId>xwiki-platform-url-api</artifactId>

Fixed code:
import com.xpn.xwiki.web.XWikiResponse;

                String continueToURL = getContinueToURL(request);
    /**
     * Perform a redirect to the given URL.
     * @param response the response to use to perform the redirect
     * @param url the location of the redirect
     * @throws XWikiException in case of IOException when performing the redirect.
     */
import java.net.URL;
import java.util.regex.Pattern;
import org.xwiki.url.URLSecurityManager;
    private static final Pattern ABSOLUTE_URL_PATTERN = Pattern.compile("[a-z0-9]+://.*");

        // check for trusted domains, only if the given location is an absolute URL.
        if (ABSOLUTE_URL_PATTERN.matcher(redirect).matches()) {
            if (!getURLSecurityManager().isDomainTrusted(new URL(redirect))) {
                LOGGER.warn(
                    "Possible phishing attack, attempting to redirect to [{}], this request has been blocked. "
                        + "If the request was legitimate, add the domain related to this request in the list "
                        + "of trusted domains in the configuration.", redirect);
                return;
            }
        }
    private URLSecurityManager getURLSecurityManager()
    {
        return Utils.getComponent(URLSecurityManager.class);
    }

    <module>xwiki-platform-url-default</module>
    <xwiki.jacoco.instructionRatio>0.85</xwiki.jacoco.instructionRatio>
import java.util.Collections;
import java.util.List;

import org.xwiki.stability.Unstable;

    /**
     * Specify the list of domains that are considered as trusted by the administrators of the wiki: those domains can
     * be used safely for redirections from the wiki or for performing other requests on them.
     * @return the list of trusted domains that can be used in the wiki.
     * @since 13.3RC1
     * @since 12.10.7
     */
    @Unstable
    default List<String> getTrustedDomains()
    {
        return Collections.emptyList();
    }

    /**
     * Define if the trusted domains check should be performed or not. This option is provided only to allow bypassing
     * security checks globally on the wiki in case of problems.
     * @return {@code true} if the security check on domains should be performed. {@code false} otherwise.
     * @since 13.3RC1
     * @since 12.10.7
     */
    @Unstable
    default boolean isTrustedDomainsEnabled()
    {
        return true;
    }
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.url;

import java.net.URL;

import org.xwiki.component.annotation.Role;
import org.xwiki.stability.Unstable;

/**
 * Dedicated component to perform security checks on URLs.
 *
 * @version $Id$
 * @since 13.3RC1
 * @since 12.10.7
 */
@Role
@Unstable
public interface URLSecurityManager
{
    /**
     * Constant to be used in {@link org.xwiki.context.ExecutionContext} with the value {@code "true"} to bypass a
     * check of {@link #isDomainTrusted(URL)}.
     */
    String BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY = "bypassDomainSecurityCheck";

    /**
     * Check if the given {@link URL} can be trusted based on the trusted domains of the wiki.
     * This method check on both the list of trusted domains given by the configuration
     * (see {@link URLConfiguration#getTrustedDomains()}) and the list of aliases used by the wiki descriptors.
     * Note that this method always returns {@code true} if {@link URLConfiguration#isTrustedDomainsEnabled()} returns
     * {@code true}. Also the method will return {@code true} whenever the {@link org.xwiki.context.ExecutionContext}
     * contains a property named {@link #BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY} with the value {@code "true"}.
     *
     * @param urlToCheck the URL for which we want to know if the domain is trusted or not.
     * @return {@code true} if the URL domain can be trusted or if the check is skipped, {@code false} otherwise
     */
    boolean isDomainTrusted(URL urlToCheck);
}
<?xml version="1.0" encoding="UTF-8"?>

<!--
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.xwiki.platform</groupId>
    <artifactId>xwiki-platform-url</artifactId>
    <version>13.3-SNAPSHOT</version>
  </parent>
  <artifactId>xwiki-platform-url-default</artifactId>
  <name>XWiki Platform - URL - Default</name>
  <packaging>jar</packaging>
  <description>Default implementations of the API defined in xwiki-platform-url-api</description>
  <properties>
    <!-- The reason for this low TPC value is because this module is tested using integration tests in the various
    URL Scheme modules -->
    <xwiki.jacoco.instructionRatio>0.31</xwiki.jacoco.instructionRatio>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-url-api</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-oldcore</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-wiki-api</artifactId>
      <version>${project.version}</version>
    </dependency>
    <!-- Testing Dependencies -->
    <dependency>
      <groupId>org.xwiki.commons</groupId>
      <artifactId>xwiki-commons-tool-test-component</artifactId>
      <version>${commons.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
import java.util.Collections;
import java.util.List;


    @Override
    public List<String> getTrustedDomains()
    {
        return this.configuration.get().getProperty(PREFIX + "trustedDomains", Collections.emptyList());
    }

    @Override
    public boolean isTrustedDomainsEnabled()
    {
        return this.configuration.get().getProperty(PREFIX + "trustedDomainsEnabled", true);
    }
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.url.internal;

import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.context.Execution;
import org.xwiki.url.URLConfiguration;
import org.xwiki.url.URLSecurityManager;
import org.xwiki.wiki.descriptor.WikiDescriptor;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
import org.xwiki.wiki.manager.WikiManagerException;

/**
 * Default implementation of {@link URLSecurityManager}.
 * This implementation keeps a HashSet in memory containing the trusted domains defined in the configuration and
 * for all subwikis. Use {@link #invalidateCache()} to compute back this hashset.
 *
 * @version $Id$
 * @since 13.3RC1
 * @since 12.10.7
 */
@Component
@Singleton
public class DefaultURLSecurityManager implements URLSecurityManager
{
    private static final Pattern ACCEPTED_DOMAIN_PATTERN = Pattern.compile("([^.]+\\.[^.]+)+");
    private static final char DOT = '.';

    @Inject
    private URLConfiguration urlConfiguration;

    @Inject
    private WikiDescriptorManager wikiDescriptorManager;

    @Inject
    private Execution execution;

    @Inject
    private Logger logger;

    private Set<String> trustedDomains;

    private void computeTrustedDomains()
    {
        Set<String> domains;
        domains = new HashSet<>(this.urlConfiguration.getTrustedDomains());

        try {
            for (WikiDescriptor wikiDescriptor : wikiDescriptorManager.getAll()) {
                domains.addAll(wikiDescriptor.getAliases());
            }
        } catch (WikiManagerException e) {
            logger.warn("Error while getting wiki descriptor to fill list of trusted domains: [{}]. "
                + "The subwikis won't be taken into account for the list of trusted domains.",
                ExceptionUtils.getRootCauseMessage(e));
        }
        this.trustedDomains = new HashSet<>();

        for (String domain : domains) {
            if (ACCEPTED_DOMAIN_PATTERN.matcher(domain).matches()) {
                this.trustedDomains.add(domain);
            } else {
                logger.warn("The domain [{}] specified in the trusted domains configuration won't be taken into "
                    + "account since it doesn't respect the documented format.", domain);
            }
        }
    }

    @Override
    public boolean isDomainTrusted(URL urlToCheck)
    {
        if (this.urlConfiguration.isTrustedDomainsEnabled()) {
            if (this.trustedDomains == null) {
                computeTrustedDomains();
            }

            String host = urlToCheck.getHost();

            while (StringUtils.contains(host, DOT)) {
                if (trustedDomains.contains(host)) {
                    return true;
                } else {
                    host = host.substring(host.indexOf(DOT) + 1);
                }
            }

            Object bypassCheckProperty = execution.getContext()
                .getProperty(URLSecurityManager.BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY);
            boolean bypassCheck = bypassCheckProperty != null && Boolean.parseBoolean(bypassCheckProperty.toString());

            if (bypassCheck) {
                logger.info("Domain of URL [{}] does not belong to the list of trusted domains but it's considered as "
                    + "trusted since the check has been bypassed.", urlToCheck);
            }

            return bypassCheck;
        } else {
            return true;
        }
    }

    /**
     * Invalidate the set of trusted domains: this should mainly be used when a subwiki is added/edited/deleted.
     */
    public void invalidateCache()
    {
        this.trustedDomains = null;
    }
}
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.url.internal;

import java.util.Arrays;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.observation.AbstractEventListener;
import org.xwiki.observation.event.Event;
import org.xwiki.url.URLSecurityManager;

import com.xpn.xwiki.internal.event.XObjectAddedEvent;
import com.xpn.xwiki.internal.event.XObjectDeletedEvent;
import com.xpn.xwiki.internal.event.XObjectUpdatedEvent;

/**
 * Listener for changes on XWikiServerClass xobjects to ensure the {@link URLSecurityManager} cache is invalidated
 * in case of change on XWikiServerClass objects.
 *
 * @version $Id$
 * @since 13.3RC1
 * @since 12.10.7
 */
@Component
@Singleton
@Named(XWikiServerClassListener.NAME)
public class XWikiServerClassListener extends AbstractEventListener
{
    /**
     * Name of the listener.
     */
    public static final String NAME = "org.xwiki.url.internal.XWikiServerClassListener";

    private static final LocalDocumentReference XWIKISERVER_CLASS =
        new LocalDocumentReference("XWiki", "XWikiServerClass");

    private static final List<Event> EVENTS = Arrays.asList(
        new XObjectAddedEvent(XWIKISERVER_CLASS),
        new XObjectDeletedEvent(XWIKISERVER_CLASS),
        new XObjectUpdatedEvent(XWIKISERVER_CLASS)
    );

    @Inject
    private URLSecurityManager securityManager;

    /**
     * Default constructor.
     */
    public XWikiServerClassListener()
    {
        super(NAME, EVENTS);
    }

    @Override
    public void onEvent(Event event, Object source, Object data)
    {
        if (this.securityManager instanceof DefaultURLSecurityManager) {
            ((DefaultURLSecurityManager) this.securityManager).invalidateCache();
        }
    }
}
org.xwiki.url.internal.XWikiServerClassListener
org.xwiki.url.internal.DefaultURLSecurityManager
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.url.internal;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.xwiki.context.Execution;
import org.xwiki.context.ExecutionContext;
import org.xwiki.test.LogLevel;
import org.xwiki.test.junit5.LogCaptureExtension;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import org.xwiki.url.URLConfiguration;
import org.xwiki.url.URLSecurityManager;
import org.xwiki.wiki.descriptor.WikiDescriptor;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * Tests for {@link DefaultURLSecurityManager}.
 *
 * @version $Id$
 * @since 13.3RC1
 * @since 12.10.7
 */
@ComponentTest
class DefaultURLSecurityManagerTest
{
    @InjectMockComponents
    private DefaultURLSecurityManager urlSecurityManager;

    @MockComponent
    private URLConfiguration urlConfiguration;

    @MockComponent
    private WikiDescriptorManager wikiDescriptorManager;

    @MockComponent
    private Execution execution;
    
    private ExecutionContext executionContext;

    @RegisterExtension
    LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO);
    
    @BeforeEach
    void setup()
    {
        this.executionContext = mock(ExecutionContext.class);
        when(this.execution.getContext()).thenReturn(this.executionContext);
        when(this.urlConfiguration.isTrustedDomainsEnabled()).thenReturn(true);
    }

    @Test
    void isDomainTrusted() throws Exception
    {
        when(urlConfiguration.getTrustedDomains()).thenReturn(Arrays.asList(
            "foo.acme.org",
            "com" // this should not be taken into account
        ));

        WikiDescriptor wikiDescriptor1 = mock(WikiDescriptor.class);
        when(wikiDescriptor1.getAliases()).thenReturn(Arrays.asList(
            "www.xwiki.org",
            "something.bar.com"
        ));

        WikiDescriptor wikiDescriptor2 = mock(WikiDescriptor.class);
        when(wikiDescriptor2.getAliases()).thenReturn(Collections.singletonList(
            "enterprise.eu"
        ));

        when(this.wikiDescriptorManager.getAll()).thenReturn(Arrays.asList(wikiDescriptor1, wikiDescriptor2));

        assertThat("www.xwiki.org is trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("http://www.xwiki.org/xwiki/bin/view/XWiki/Login")));
        assertThat("www.xwiki.org is trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("https://www.xwiki.org/xwiki/bin/view/XWiki/Login")));
        assertThat("www.xwiki.com is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://www.xwiki.com/xwiki/bin/view/XWiki/Login")));
        assertThat("xwiki.org is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://xwiki.org/xwiki/bin/view/XWiki/Login")));
        assertThat("foo.acme.org is trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("https://foo.acme.org/something/else")));
        assertThat("bar.foo.acme.org is trusted since foo.acme.org is", this.urlSecurityManager
            .isDomainTrusted(new URL("https://bar.foo.acme.org/something/else")));
        assertThat("buz.bar.foo.acme.org is trusted since foo.acme.org is", this.urlSecurityManager
            .isDomainTrusted(new URL("https://buz.bar.foo.acme.org/something/else")));
        assertThat("acme.org is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://acme.org/something/else")));
        assertThat("www.acme.org is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://www.acme.org/something/else")));
        assertThat("something.bar.thing.com is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://something.bar.thing.com")));
        assertThat("bar.thing.com is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://bar.thing.com")));
        assertThat("something.bar.com is tristed", this.urlSecurityManager
            .isDomainTrusted(new URL("https://something.bar.com")));
        assertThat("enterprise.eu is trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("https://enterprise.eu/xwiki/")));
        assertThat("enterprise.eu. is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://enterprise.eu./xwiki/")));

        assertEquals("The domain [com] specified in the trusted domains configuration won't be taken into account "
                + "since it doesn't respect the documented format.",
            logCapture.getMessage(0));
    }

    @Test
    void invalidateCache() throws Exception
    {
        when(urlConfiguration.getTrustedDomains()).thenReturn(Collections.singletonList(
            "xwiki.org"
        ));
        assertThat("www.xwiki.org is trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("http://www.xwiki.org")));
        assertThat("foo.acme.org is not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://foo.acme.org/something/else")));

        when(urlConfiguration.getTrustedDomains()).thenReturn(Collections.singletonList(
            "foo.acme.org"
        ));

        // the asserts are still the same because we rely on cached values
        assertThat("www.xwiki.org is still trusted", this.urlSecurityManager
            .isDomainTrusted(new URL("http://www.xwiki.org")));
        assertThat("foo.acme.org is still not trusted", !this.urlSecurityManager
            .isDomainTrusted(new URL("https://foo.acme.org/something/else")));

        // after invalidation the cache has been recomputed.
        this.urlSecurityManager.invalidateCache();
        assertThat("www.xwiki.org is not trusted anymore", !this.urlSecurityManager
            .isDomainTrusted(new URL("http://www.xwiki.org")));
        assertThat("foo.acme.org is trusted now", this.urlSecurityManager
            .isDomainTrusted(new URL("https://foo.acme.org/something/else")));
    }

    @Test
    void isDomainTrustedWhenCheckSkipped() throws MalformedURLException
    {
        when(urlConfiguration.getTrustedDomains()).thenReturn(Collections.singletonList(
            "foo.acme.org"
        ));
        when(urlConfiguration.isTrustedDomainsEnabled()).thenReturn(false);
        assertThat("Any domain can be trusted when check is skipped: check with www.xwiki.org",
            this.urlSecurityManager.isDomainTrusted(new URL("http://www.xwiki.org")));
        assertThat("Any domain can be trusted when check is skipped: check with www.bar.eu",
            this.urlSecurityManager.isDomainTrusted(new URL("http://www.bar.eu")));
        assertThat("Any domain can be trusted when check is skipped: check with foo.acme.org",
            this.urlSecurityManager.isDomainTrusted(new URL("http://foo.acme.org")));

        when(urlConfiguration.isTrustedDomainsEnabled()).thenReturn(true);
        assertThat("www.xwiki.org should not be trusted",
            !this.urlSecurityManager.isDomainTrusted(new URL("http://www.xwiki.org")));
        assertThat("www.bar.eu should not be trusted",
            !this.urlSecurityManager.isDomainTrusted(new URL("http://www.bar.eu")));
        assertThat("foo.acme.org should be trusted",
            this.urlSecurityManager.isDomainTrusted(new URL("http://foo.acme.org")));

        when(this.executionContext.getProperty(URLSecurityManager.BYPASS_DOMAIN_SECURITY_CHECK_CONTEXT_PROPERTY))
            .thenReturn(true);
        assertThat("www.xwiki.org should be trusted when check is bypassed",
            this.urlSecurityManager.isDomainTrusted(new URL("http://www.xwiki.org")));

        assertEquals("Domain of URL [http://www.xwiki.org] does not belong to the list of trusted domains but "
                + "it's considered as trusted since the check has been bypassed.",
            logCapture.getMessage(0));
    }
}
package org.xwiki.url.internal;
package org.xwiki.url.internal;
import org.xwiki.url.URLConfiguration;
      <artifactId>xwiki-platform-url-default</artifactId>
      <artifactId>xwiki-platform-url-default</artifactId>
      <artifactId>xwiki-platform-url-default</artifactId>
#-# [Since 13.3RC1]
#-# [Since 12.10.7]
#-# Define a list of trusted domains that can be used in the wiki for performing requests or redirections even if
#-# the wiki does not use it. Domains are listed without http and separated with a comma in the list. Subdomains can be
#-# specified.
#-# Example of accepted value: foo.acme.org,enterprise.org
#-#
#-# By default the list of trusted domains is empty:
# url.trustedDomains=

#-# [Since 13.3RC1]
#-# [Since 12.10.7]
#-# Allow to enable or disable checks performed on domains by taking into account the list of trusted domains.
#-# Disable this property only if you experienced some issues on your wiki: some security check won't be performed when
#-# this property is set to false.
#-#
#-# By default this property is set to true:
# url.trustedDomainsEnabled=true