org.auraframework.integration.test.http.AuraServletHttpTest.java Source code

Java tutorial

Introduction

Here is the source code for org.auraframework.integration.test.http.AuraServletHttpTest.java

Source

/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.auraframework.integration.test.http;

import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.inject.Inject;

import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;
import org.auraframework.adapter.ConfigAdapter;
import org.auraframework.adapter.ContentSecurityPolicy;
import org.auraframework.adapter.DefaultContentSecurityPolicy;
import org.auraframework.def.ApplicationDef;
import org.auraframework.def.ComponentDef;
import org.auraframework.def.DefDescriptor;
import org.auraframework.http.AuraBaseServlet;
import org.auraframework.integration.test.util.AuraHttpTestCase;
import org.auraframework.system.AuraContext.Mode;
import org.auraframework.test.adapter.MockConfigAdapter;
import org.auraframework.test.client.UserAgent;
import org.auraframework.util.json.JsFunction;
import org.auraframework.util.json.JsonEncoder;
import org.auraframework.util.json.JsonReader;
import org.auraframework.util.test.annotation.ThreadHostileTest;
import org.auraframework.util.test.annotation.UnAdaptableTest;
import org.junit.Test;

/**
 * Automation to verify the handling of AuraServlet requests.
 */
public class AuraServletHttpTest extends AuraHttpTestCase {

    @Inject
    private ConfigAdapter configAdapter;

    private static class MockCsp implements ContentSecurityPolicy {
        private final String[] ancestors;

        public MockCsp(String... ancestors) {
            this.ancestors = ancestors;
        }

        @Override
        public String getCspHeaderValue() {
            return DefaultContentSecurityPolicy.buildHeaderNormally(this);
        }

        @Override
        public Collection<String> getFrameAncestors() {
            if (ancestors == null) {
                return null;
            }
            List<String> list = new ArrayList<>(ancestors.length);
            for (String item : ancestors) {
                list.add(item);
            }
            return list;
        }

        @Override
        public Collection<String> getFrameSources() {
            return new ArrayList<>(0);
        }

        @Override
        public Collection<String> getScriptSources() {
            List<String> list = new ArrayList<>(1);
            list.add(null);
            return list;
        }

        @Override
        public Collection<String> getStyleSources() {
            List<String> list = new ArrayList<>(1);
            list.add(null);
            return list;
        }

        @Override
        public Collection<String> getConnectSources() {
            List<String> list = new ArrayList<>(2);
            list.add("www.itrustu.com/");
            list.add("www.also.com/other");
            return list;
        }

        @Override
        public Collection<String> getFontSources() {
            return null;
        }

        @Override
        public Collection<String> getDefaultSources() {
            List<String> list = new ArrayList<>(1);
            list.add(null);
            return list;
        }

        @Override
        public Collection<String> getImageSources() {
            return null;
        }

        @Override
        public Collection<String> getObjectSources() {
            return new ArrayList<>(0);
        }

        @Override
        public Collection<String> getMediaSources() {
            return null;
        }

        @Override
        public String getReportUrl() {
            return "http://doesnt.matter.com/";
        }
    }

    /**
     * Test for W-2063110 this test is to verify the order of actions and context in the response we used to have
     * context before actions, now it's the opposite
     */
    @Test
    public void testPostRawResponseSimpleAction() throws Exception {
        Map<String, Object> actionParams = new HashMap<>();
        actionParams.put("param", "some string");
        ServerAction a = new ServerAction(
                "java://org.auraframework.components.test.java.controller.JavaTestController/ACTION$getString",
                actionParams);
        a.run();
        String rawRes = a.getrawResponse();
        Integer posActions = rawRes.indexOf("actions");
        Integer posContex = rawRes.indexOf("context");
        assertTrue(posActions < posContex);
    }

    /**
     * When we request a component from the server we should get back it's component class, but only a single occurrence
     * of it to minimize payload.
     */
    @Test
    public void testGetComponentActionReturnsSingleComponentClass() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component></aura:component>");

        Map<String, Object> actionParams = new HashMap<>();
        actionParams.put("name", cmpDesc.getQualifiedName());
        ServerAction a = new ServerAction(
                "java://org.auraframework.impl.controller.ComponentController/ACTION$getComponent", actionParams);
        a.run();
        String rawRes = a.getrawResponse();

        int firstOccurrence = rawRes.indexOf("componentClass");
        int lastOccurrence = rawRes.lastIndexOf("componentClass");
        assertTrue("Component class should be returned in server response when requesting component",
                firstOccurrence != -1);
        assertTrue("Server response should only return a single componentClass for a component, but got <" + rawRes
                + ">", firstOccurrence == lastOccurrence);
    }

    @Test
    public void testMulitpleActionsInOnePost() {
        ArrayList<String> qNameList = new ArrayList<>();
        ArrayList<Map<String, Object>> actionParamsArrayList = new ArrayList<>();

        Map<String, Object> actionParams = new HashMap<>();
        actionParams.put("param", "some string");
        qNameList.add(
                "java://org.auraframework.components.test.java.controller.JavaTestController/ACTION$getString");
        actionParamsArrayList.add(actionParams);

        Map<String, Object> actionParams1 = new HashMap<>();
        actionParams1.put("param", 6);
        qNameList.add("java://org.auraframework.components.test.java.controller.JavaTestController/ACTION$getInt");
        actionParamsArrayList.add(actionParams1);

        ServerAction a = new ServerAction(qNameList, actionParamsArrayList);
        a.run();
        assertTrue("The response does not have the expected number of actions", a.getReturnValueList().size() == 2);
        assertTrue(a.getReturnValueList().get(0).equals("some string")
                && a.getReturnValueList().get(1).equals(new BigDecimal(6)));

    }

    /**
     * Check a post context.
     */
    @Test
    public void testPostContext() throws Exception {
        Map<String, Object> message = new HashMap<>();
        Map<String, Object> actionInstance = new HashMap<>();
        actionInstance.put("descriptor",
                "java://org.auraframework.components.test.java.controller.JavaTestController/ACTION$getString");
        Map<String, Object> actionParams = new HashMap<>();
        actionParams.put("param", "some string");
        actionInstance.put("params", actionParams);
        @SuppressWarnings("rawtypes")
        Map[] actions = { actionInstance };
        message.put("actions", actions);

        String jsonMessage = JsonEncoder.serialize(message);

        Map<String, String> params = new HashMap<>();
        params.put("message", jsonMessage);
        params.put("aura.token", getCsrfToken());

        DefDescriptor<ApplicationDef> app = definitionService
                .getDefDescriptor("auratest:test_SimpleServerRenderedPage", ApplicationDef.class);
        params.put("aura.context", getAuraTestingUtil().buildContextForPost(Mode.DEV, app));

        HttpPost post = obtainPostMethod("/aura", params);
        HttpResponse httpResponse = perform(post);
        int statusCode = getStatusCode(httpResponse);
        String response = getResponseBody(httpResponse);
        post.releaseConnection();

        if (HttpStatus.SC_OK != statusCode) {
            fail(String.format("Unexpected status code <%s>, expected <%s>, response:%n%s", statusCode,
                    HttpStatus.SC_OK, response));
        }
        new JsonReader().read(response.substring(AuraBaseServlet.CSRF_PROTECT.length()));
    }

    /**
     * This is actually an invalid test.
     *
     */
    @Test
    public void testPostWithOldLastMod() throws Exception {
        Map<String, Object> message = new HashMap<>();
        Map<String, Object> actionInstance = new HashMap<>();
        actionInstance.put("descriptor",
                "java://org.auraframework.components.test.java.controller.JavaTestController/ACTION$getString");
        Map<String, Object> actionParams = new HashMap<>();
        actionParams.put("param", "some string");
        actionInstance.put("params", actionParams);
        @SuppressWarnings("rawtypes")
        Map[] actions = { actionInstance };
        message.put("actions", actions);

        String jsonMessage = JsonEncoder.serialize(message);

        Map<String, String> params = new HashMap<>();
        params.put("message", jsonMessage);
        params.put("aura.token", getCsrfToken());
        DefDescriptor<ApplicationDef> app = definitionService
                .getDefDescriptor("auratest:test_SimpleServerRenderedPage", ApplicationDef.class);
        String fwuid = getAuraTestingUtil().modifyUID(configAdapter.getAuraFrameworkNonce());
        params.put("aura.context",
                getAuraTestingUtil().buildContextForPost(Mode.DEV, app, null, fwuid, null, null));

        HttpPost post = obtainPostMethod("/aura", params);
        HttpResponse httpResponse = perform(post);
        int statusCode = getStatusCode(httpResponse);
        String response = getResponseBody(httpResponse);
        post.releaseConnection();

        assertEquals("Status code should be 200", HttpStatus.SC_OK, statusCode);

        assertTrue("response not wrapped with ERROR marker: " + response,
                response.startsWith(AuraBaseServlet.CSRF_PROTECT + "*/") && response.endsWith("/*ERROR*/"));
        response = response.substring(AuraBaseServlet.CSRF_PROTECT.length() + 2,
                response.length() - "/*ERROR*/".length());
        @SuppressWarnings("unchecked")
        Map<String, Object> json = (Map<String, Object>) new JsonReader().read(response);
        assertEquals(true, json.get("exceptionEvent"));
        @SuppressWarnings("unchecked")
        Map<String, Object> eventJson = (Map<String, Object>) json.get("event");
        assertEquals("markup://aura:clientOutOfSync", eventJson.get("descriptor"));
        Object f = json.get("defaultHandler");
        assertEquals(JsFunction.class, f.getClass());
        assertEquals("$A.clientService.setOutdated()", ((JsFunction) f).getBody());
    }

    private void assertNoCacheRequest(String inputUrl, String expectedRedirect) throws Exception {
        HttpGet get = obtainGetMethod(inputUrl, false);
        HttpResponse response = perform(get);
        EntityUtils.consume(response.getEntity());
        get.releaseConnection();

        assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, getStatusCode(response));
        String location = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
        assertTrue("Location is not absolute (but should be by spec): " + location,
                location.matches("^https?://[a-zA-Z0-9_\\-.]*:[0-9]*(/.*)?$"));
        int index = location.indexOf(':');
        index = location.indexOf(':', index + 1) + 1; // find the port-separating colon
        while (location.charAt(index) >= '0' && location.charAt(index) <= '9') {
            index++;
        }
        assertEquals("Wrong URI path", expectedRedirect, location.substring(index));
        assertEquals("no-cache, no-store", response.getFirstHeader(HttpHeaders.CACHE_CONTROL).getValue());
        assertEquals("no-cache", response.getFirstHeader(HttpHeaders.PRAGMA).getValue());
        assertDefaultAntiClickjacking(response, false, false); // Redirects don't have XFO/CSP guarding
    }

    /**
     * nocache in the request will redirect to the input url (minus the protocol and host)
     */
    @Test
    public void testNoCache() throws Exception {
        assertNoCacheRequest(String.format("/aura?aura.tag&nocache=%s", URLEncoder.encode(
                "scheme://user:password@any.host:port/m?aura.mode=PROD&aura.format=HTML&something=this+that#someidinhere?has=someparam",
                "UTF-8")), "/m?aura.mode=PROD&aura.format=HTML&something=this+that#someidinhere?has=someparam");
    }

    @Test
    public void testHTMLTemplateCaching() throws Exception {
        // An application with isOnePageApp set to true
        DefDescriptor<ApplicationDef> desc = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application isOnePageApp='true'></aura:application>");

        // Expect the get request to be set for long cache
        assertResponseSetToLongCache(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));

        // An application with isOnePageApp set to false
        desc = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application isOnePageApp='false'></aura:application>");
        // Expect the get request to be set for no caching
        assertResponseSetToNoCache(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));

        // An application with no specification
        desc = addSourceAutoCleanup(ApplicationDef.class, "<aura:application></aura:application>");
        // Expect the get request to be set for no caching
        assertResponseSetToNoCache(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));

        // A component and AuraBaseServlet.isManifestEnabled() is false because
        // UserAgent is not "AppleWebKit" based
        setHttpUserAgent(UserAgent.EMPTY.getUserAgentString());
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component ></aura:component>");
        // Expect the get request to be set for long cache
        assertResponseSetToLongCache(String.format("/%s/%s.cmp", cmpDesc.getNamespace(), cmpDesc.getName()));
    }

    // following 5 tests are mainly for mapping between custom CSP to X-FRAME-OPTIONS
    // the logic is in AuraBaseServlet.setBasicHeaders()
    // note these tests are for aura stand alone only, when running in core, it has different CSP (with more script-src
    // etc)

    // 1.if we set ancestor resources with more than one url('self' counts as url), we won't create X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @UnAdaptableTest("CSP is different between aura-stand-alone and core")
    @Test
    public void testSpecialCspMultipleAncestors() throws Exception {
        Header[] headers = doSpecialCspTest("'self' www.itrustu.com/frame www.also.com/other", null,
                "www.itrustu.com/frame", "www.also.com/other");
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 0, headers.length);
    }

    // 2.if we set ancestor resources with one url (without wildcard), that url will get written into X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspSingleAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("www.itrustu.com/frame", "www.itrustu.com/frame");
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 1, headers.length);
        assertEquals("ALLOW-FROM www.itrustu.com/frame", headers[0].getValue());
    }

    // 3.if we set ancestor resources with protocal like url, we set ALLOWALL for X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspProtocolAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("https:", "https:");
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 1, headers.length);
        assertEquals("ALLOWALL", headers[0].getValue());
    }

    // 4.if we set ancestor with one wildcard url, we set ALLOWALL for X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspWildcardAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("https://*.foo.com", "https://*.foo.com");
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 1, headers.length);
        assertEquals("ALLOWALL", headers[0].getValue());
    }

    // 5.if we set ancestor resources with null, DENY get written into X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspDeniedAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("'none'");
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 1, headers.length);
        assertEquals("DENY", headers[0].getValue());
    }

    // 6.if we set ancestor resources with [null], SAMEORIGIN get written into X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspSameOriginAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("'self'", (String) null);
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 1, headers.length);
        assertEquals("SAMEORIGIN", headers[0].getValue());
    }

    // 7.if we set ancestor resources with null [], we won't change X-FRAME-OPTIONS
    @ThreadHostileTest("swaps config adapter")
    @Test
    public void testSpecialCspAnyAncestor() throws Exception {
        Header[] headers = doSpecialCspTest("*", (String[]) null);
        assertEquals("wrong number of X-FRAME-OPTIONS header lines", 0, headers.length);
    }

    @Test
    public void testHTMLTemplateCachingWhenAppCacheIsEnable() throws Exception {
        setHttpUserAgent(UserAgent.GOOGLE_CHROME.getUserAgentString());

        // An application with isOnePageApp set to true and useAppcache set to
        // true
        // isOnePageApp overrides useAppCache specification
        DefDescriptor<ApplicationDef> desc = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application isOnePageApp='true' useAppcache='true'></aura:application>");
        // Expect the get request to be set for long cache
        assertResponseSetToLongCache(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));

        // An application with useAppcache set to true and no specification for
        // isOnePageApp
        desc = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application useAppcache='true'></aura:application>");
        // Expect the get request to be set for no caching
        assertResponseSetToNoCache(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));

        // A component and AuraBaseServlet.isManifestEnabled() is false
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component ></aura:component>");
        // Expect the get request to be set for long cache
        assertResponseSetToLongCache(String.format("/%s/%s.cmp", cmpDesc.getNamespace(), cmpDesc.getName()));
    }

    /**
     * Wiggle factor.
     *
     * This is intended to allow for variance between the local date and the server date, along with any latency that
     * might occur. Currently it is set to 1 hour, which should be more than enough to account for offsets, but short
     * enough so that we don't really care.
     */
    private final static long WIGGLE_FACTOR = (1000L * 60 * 60 * 1);

    /**
     * Submit a request and check that the 'long cache' is set correctly.
     *
     * See documentation for {@link #WIGGLE_FACTOR}.
     *
     * @param url the url
     */
    private void assertResponseSetToLongCache(String url) throws Exception {
        Date expected = new Date(System.currentTimeMillis() + AuraBaseServlet.LONG_EXPIRE - WIGGLE_FACTOR);

        HttpGet get = obtainGetMethod(url);
        HttpResponse response = perform(get);

        assertEquals("Failed to execute request successfully.", HttpStatus.SC_OK, getStatusCode(response));

        assertEquals("Expected response to be marked for long cache",
                String.format("max-age=%s, public", AuraBaseServlet.LONG_EXPIRE / 1000),
                response.getFirstHeader(HttpHeaders.CACHE_CONTROL).getValue());
        assertDefaultAntiClickjacking(response, true, false);
        String expiresHdr = response.getFirstHeader(HttpHeaders.EXPIRES).getValue();
        Date expires = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH).parse(expiresHdr);
        //
        // We show all of the related dates/strings to help with debugging.
        //
        assertTrue(String.format("Expires header is earlier than expected. Expected !before %s, got %s (%s).",
                expected, expires, expiresHdr), !expires.before(expected));

        get.releaseConnection();
    }

    /**
     * Submit a request and check that the 'no cache' is set correctly.
     *
     * We are very generous with the expires time here, as we really don't care other than to have it well in the past.
     *
     * @param url the url path.
     */
    private void assertResponseSetToNoCache(String url) throws Exception {
        Date expected = new Date(System.currentTimeMillis());
        HttpGet get = obtainGetMethod(url);
        HttpResponse response = perform(get);
        assertEquals("Failed to execute request successfully.", HttpStatus.SC_OK, getStatusCode(response));

        assertEquals("Expected response to be marked for no-cache", "no-cache, no-store",
                response.getFirstHeader(HttpHeaders.CACHE_CONTROL).getValue());
        assertEquals("no-cache", response.getFirstHeader(HttpHeaders.PRAGMA).getValue());
        assertDefaultAntiClickjacking(response, true, false);

        String expiresHdr = response.getFirstHeader(HttpHeaders.EXPIRES).getValue();
        Date expires = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH).parse(expiresHdr);
        //
        // We show all of the related dates/strings to help with debugging.
        //
        assertTrue(String.format("Expires header should be in the past. Expected before %s, got %s (%s).", expected,
                expires, expiresHdr), expires.before(expected));

        EntityUtils.consume(response.getEntity());
        get.releaseConnection();
    }

    /** Runs a test with special CSP */
    private Header[] doSpecialCspTest(String expectCspAncestors, String... ancestors) throws Exception {
        ContentSecurityPolicy mockCsp = new MockCsp(ancestors);

        MockConfigAdapter mci = getMockConfigAdapter();

        try {
            mci.setContentSecurityPolicy(mockCsp);

            // An application with isOnePageApp set to true
            DefDescriptor<ApplicationDef> desc = addSourceAutoCleanup(ApplicationDef.class,
                    "<aura:application isOnePageApp='true'></aura:application>");

            HttpGet get = obtainGetMethod(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));
            HttpResponse response = perform(get);

            // Check X-FRAME-OPTIONS
            Header[] headers = response.getHeaders("X-FRAME-OPTIONS");

            // And CSP
            Map<String, String> csp = getCSP(response);
            assertEquals("frame-ancestors is wrong", expectCspAncestors, csp.get("frame-ancestors"));
            assertEquals("script-src is wrong", "'self'", csp.get("script-src"));
            assertEquals("style-src is wrong", "'self'", csp.get("style-src"));
            assertEquals("connect-src is wrong", "www.itrustu.com/ www.also.com/other", csp.get("connect-src"));
            assertEquals("font-src is wrong", "*", csp.get("font-src"));
            assertEquals("img-src is wrong", "*", csp.get("img-src"));
            assertEquals("object-src is wrong", "'none'", csp.get("object-src"));
            assertEquals("media-src is wrong", "*", csp.get("media-src"));
            assertEquals("default-src is wrong", "'self'", csp.get("default-src"));

            return headers;
        } finally {
            mci.setContentSecurityPolicy(null);
        }

    }

    /**
     * Verify the Script tag to fetch the Aura Framework JS has nonce. The initial get request for an application gets a
     * template as response. Part of the template response should be a script tag which fetches the Aura FW JS. The URL
     * for the js file should have nonce indicating the last mod of the JS group.
     *
     * @throws Exception
     */
    @Test
    public void testJSFrameworkUrlHasNonce() throws Exception {
        DefDescriptor<ApplicationDef> desc = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application render='client'></aura:application>");
        HttpGet get = obtainGetMethod(String.format("/%s/%s.app", desc.getNamespace(), desc.getName()));
        HttpResponse response = perform(get);
        assertEquals(HttpStatus.SC_OK, getStatusCode(response));
        // Fetch the latest timestamp of the JS group and construct URL for DEV mode.
        String expectedFWUrl = String.format("/auraFW/javascript/%s/aura_dev.js",
                configAdapter.getAuraFrameworkNonce());
        String scriptTag = String.format("<script src=\"%s\"", expectedFWUrl);
        assertTrue("Expected Aura FW Script tag not found. Expected to see: " + scriptTag,
                getResponseBody(response).contains(scriptTag));

        assertDefaultAntiClickjacking(response, true, false);
        get.releaseConnection();
    }

    /**
     * Verify providing invalid DefDescriptor format to the aura.tag param results in the proper handled Exception and
     * not an AuraUnhandledException, which results in a Gack on SFDC.
     */
    @Test
    public void testInvalidDefDescriptorFormat() throws Exception {
        String url = String.format("/aura?aura.tag=foo:bar:baz");
        HttpGet get = obtainGetMethod(url);
        HttpResponse httpResponse = perform(get);
        assertEquals(HttpStatus.SC_OK, getStatusCode(httpResponse));
        String response = getResponseBody(httpResponse);
        assertTrue("Expected 'SystemErrorException: Invalid Descriptor Format' but got: " + response,
                response.contains("SystemErrorException: Invalid Descriptor Format: foo:bar:baz"));
        assertFalse("Invalid aura.tag input should not result in an AuraUnhandledException. " + response,
                response.contains("AuraUnhandledException: Unable to process your request"));
        get.releaseConnection();
    }

    /**
     * Verify providing invalid DefDescriptor format to the aura.tag param results in the proper handled Exception and
     * not an AuraUnhandledException.
     */
    @UnAdaptableTest("PROD mode will likely be handled differently by the ExceptionAdapter")
    @Test
    public void testInvalidDefDescriptorFormatExploitInProdMode() throws Exception {
        String url = "/aura?aura.tag=any:thing%3Csvg%3E%3Cscript%3E0%3C1%3Ealert(document.domain)%3C%2Fscript%3E.app";
        HttpGet get = obtainGetMethod(url + "&aura.mode=PROD");
        HttpResponse httpResponse = perform(get);

        assertEquals(HttpStatus.SC_OK, getStatusCode(httpResponse));
        String response = getResponseBody(httpResponse);
        assertTrue("Expected 'Invalid Descriptor Format' but got: " + response,
                response.contains("Invalid Descriptor Format: any:thing&lt;svg&gt;&lt;script&gt;"));
        assertFalse("Invalid aura.tag input should not result in an AuraUnhandledException. " + response,
                response.contains("AuraUnhandledException: Unable to process your request"));
        get.releaseConnection();
    }

    @Test
    public void testInvalidDefDescriptorFormatExploitInDevMode() throws Exception {
        String url = "/aura?aura.tag=any:thing%3Csvg%3E%3Cscript%3E0%3C1%3Ealert(document.domain)%3C%2Fscript%3E.app";
        HttpGet get = obtainGetMethod(url + "&aura.mode=DEV");
        HttpResponse httpResponse = perform(get);

        assertEquals(HttpStatus.SC_OK, getStatusCode(httpResponse));
        String response = getResponseBody(httpResponse);
        assertTrue("Expected 'Invalid Descriptor Format' but got: " + response,
                response.contains("Invalid Descriptor Format: any:thing&lt;svg&gt;&lt;script&gt;"));
        assertFalse("Invalid aura.tag input should not result in an AuraUnhandledException. " + response,
                response.contains("AuraUnhandledException: Unable to process your request"));
        get.releaseConnection();
    }
}