/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you 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.elasticsearch.plugins; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.rest.client.http.HttpRequestBuilder; import org.elasticsearch.test.rest.client.http.HttpResponse; import org.junit.Test; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Locale; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.elasticsearch.rest.RestStatus.*; import static org.elasticsearch.test.ESIntegTestCase.Scope; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasStatus; import static org.hamcrest.Matchers.containsString; /** * We want to test site plugins */ @ClusterScope(scope = Scope.SUITE, numDataNodes = 1) public class SitePluginIT extends ESIntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { Path pluginDir = getDataPath("/org/elasticsearch/test_plugins"); return settingsBuilder() .put(super.nodeSettings(nodeOrdinal)) .put("path.plugins", pluginDir.toAbsolutePath()) .put("force.http.enabled", true) .build(); } public HttpRequestBuilder httpClient() { RequestConfig.Builder builder = RequestConfig.custom().setRedirectsEnabled(false); CloseableHttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(builder.build()).build(); return new HttpRequestBuilder(httpClient).httpTransport(internalCluster().getDataNodeInstance(HttpServerTransport.class)); } @Test public void testRedirectSitePlugin() throws Exception { // We use an HTTP Client to test redirection HttpResponse response = httpClient().method("GET").path("/_plugin/dummy").execute(); assertThat(response, hasStatus(MOVED_PERMANENTLY)); assertThat(response.getBody(), containsString("/_plugin/dummy/")); // We test the real URL response = httpClient().method("GET").path("/_plugin/dummy/").execute(); assertThat(response, hasStatus(OK)); assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>")); } /** * Test direct access to an existing file (index.html) */ @Test public void testAnyPage() throws Exception { HttpResponse response = httpClient().path("/_plugin/dummy/index.html").execute(); assertThat(response, hasStatus(OK)); assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>")); } /** * Test normalizing of path */ @Test public void testThatPathsAreNormalized() throws Exception { // more info: https://www.owasp.org/index.php/Path_Traversal List<String> notFoundUris = new ArrayList<>(); notFoundUris.add("/_plugin/dummy/../../../../../log4j.properties"); notFoundUris.add("/_plugin/dummy/../../../../../%00log4j.properties"); notFoundUris.add("/_plugin/dummy/..%c0%af..%c0%af..%c0%af..%c0%af..%c0%aflog4j.properties"); notFoundUris.add("/_plugin/dummy/%2E%2E/%2E%2E/%2E%2E/%2E%2E/index.html"); notFoundUris.add("/_plugin/dummy/%2e%2e/%2e%2e/%2e%2e/%2e%2e/index.html"); notFoundUris.add("/_plugin/dummy/%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2findex.html"); notFoundUris.add("/_plugin/dummy/%2E%2E/%2E%2E/%2E%2E/%2E%2E/index.html"); notFoundUris.add("/_plugin/dummy/..%5C..%5C..%5C..%5C..%5Clog4j.properties"); for (String uri : notFoundUris) { HttpResponse response = httpClient().path(uri).execute(); String message = String.format(Locale.ROOT, "URI [%s] expected to be not found", uri); assertThat(message, response, hasStatus(NOT_FOUND)); } // using relative path inside of the plugin should work HttpResponse response = httpClient().path("/_plugin/dummy/dir1/../dir1/../index.html").execute(); assertThat(response, hasStatus(OK)); assertThat(response.getBody(), containsString("<title>Dummy Site Plugin</title>")); } /** * Test case for #4845: https://github.com/elasticsearch/elasticsearch/issues/4845 * Serving _site plugins do not pick up on index.html for sub directories */ @Test public void testWelcomePageInSubDirs() throws Exception { HttpResponse response = httpClient().path("/_plugin/subdir/dir/").execute(); assertThat(response, hasStatus(OK)); assertThat(response.getBody(), containsString("<title>Dummy Site Plugin (subdir)</title>")); response = httpClient().path("/_plugin/subdir/dir_without_index/").execute(); assertThat(response, hasStatus(FORBIDDEN)); response = httpClient().path("/_plugin/subdir/dir_without_index/page.html").execute(); assertThat(response, hasStatus(OK)); assertThat(response.getBody(), containsString("<title>Dummy Site Plugin (page)</title>")); } }