Watcher: Throw exception when empty URL is handed in http requests

This ensures that invalid watches are not even added and rejected on
index time.

Closes elastic/elasticsearch#1510

Original commit: elastic/x-pack-elasticsearch@d18e0c8ef6
This commit is contained in:
Alexander Reelsen 2016-02-25 17:29:36 -08:00
parent b97fea44d7
commit 47f1c2daa5
5 changed files with 219 additions and 1 deletions

View File

@ -242,6 +242,10 @@ public class HttpRequest implements ToXContent {
return new Builder(host, port);
}
static Builder builder() {
return new Builder();
}
public static class Parser {
private final HttpAuthRegistry httpAuthRegistry;
@ -443,8 +447,15 @@ public class HttpRequest implements ToXContent {
}
public Builder fromUrl(String supposedUrl) {
if (Strings.hasLength(supposedUrl) == false) {
throw new ElasticsearchParseException("Configured URL is empty, please configure a valid URL");
}
try {
URI uri = new URI(supposedUrl);
if (Strings.hasLength(uri.getScheme()) == false) {
throw new ElasticsearchParseException("URL [{}] does not contain a scheme", uri);
}
scheme = Scheme.parse(uri.getScheme());
port = uri.getPort() > 0 ? uri.getPort() : scheme.defaultPort();
host = uri.getHost();

View File

@ -248,6 +248,10 @@ public class HttpRequestTemplate implements ToXContent {
return new Builder(host, port);
}
static Builder builder() {
return new Builder();
}
public static class Parser {
private final HttpAuthRegistry httpAuthRegistry;
@ -477,8 +481,15 @@ public class HttpRequestTemplate implements ToXContent {
}
public Builder fromUrl(String supposedUrl) {
if (Strings.hasLength(supposedUrl) == false) {
throw new ElasticsearchParseException("Configured URL is empty, please configure a valid URL");
}
try {
URI uri = new URI(supposedUrl);
if (Strings.hasLength(uri.getScheme()) == false) {
throw new ElasticsearchParseException("URL [{}] does not contain a scheme", uri);
}
scheme = Scheme.parse(uri.getScheme());
port = uri.getPort() > 0 ? uri.getPort() : scheme.defaultPort();
host = uri.getHost();
@ -495,7 +506,7 @@ public class HttpRequestTemplate implements ToXContent {
}
}
} catch (URISyntaxException e) {
throw new ElasticsearchParseException("Malformed URI [{}]", supposedUrl);
throw new ElasticsearchParseException("Malformed URL [{}]", supposedUrl);
}
return this;
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.watcher.support.http;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -25,6 +26,7 @@ import java.util.Collections;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
@ -168,6 +170,33 @@ public class HttpRequestTemplateTests extends ESTestCase {
assertThatManualBuilderEqualsParsingFromUrl("http://www.example.org?foo=%20white%20space", builder);
}
public void testParsingEmptyUrl() throws Exception {
try {
HttpRequestTemplate.builder().fromUrl("");
fail("Expected exception due to empty URL");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("Configured URL is empty, please configure a valid URL"));
}
}
public void testInvalidUrlsWithMissingScheme() throws Exception {
try {
HttpRequestTemplate.builder().fromUrl("www.test.de");
fail("Expected exception due to missing scheme");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("URL [www.test.de] does not contain a scheme"));
}
}
public void testInvalidUrlsWithHost() throws Exception {
try {
HttpRequestTemplate.builder().fromUrl("https://");
fail("Expected exception due to missing host");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("Malformed URL [https://]"));
}
}
private void assertThatManualBuilderEqualsParsingFromUrl(String url, HttpRequestTemplate.Builder builder) throws Exception {
XContentBuilder urlContentBuilder = jsonBuilder().startObject().field("url", url).endObject();
XContentParser urlContentParser = JsonXContent.jsonXContent.createParser(urlContentBuilder.bytes());

View File

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.support.http;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
public class HttpRequestTests extends ESTestCase {
public void testParsingFromUrl() throws Exception {
HttpRequest.Builder builder = HttpRequest.builder("www.example.org", 1234);
builder.path("/foo/bar/org");
builder.setParam("param", "test");
builder.scheme(Scheme.HTTPS);
assertThatManualBuilderEqualsParsingFromUrl("https://www.example.org:1234/foo/bar/org?param=test", builder);
// test without specifying port
builder = HttpRequest.builder("www.example.org", 80);
assertThatManualBuilderEqualsParsingFromUrl("http://www.example.org", builder);
// encoded values
builder = HttpRequest.builder("www.example.org", 80).setParam("foo", " white space");
assertThatManualBuilderEqualsParsingFromUrl("http://www.example.org?foo=%20white%20space", builder);
}
public void testParsingEmptyUrl() throws Exception {
try {
HttpRequest.builder().fromUrl("");
fail("Expected exception due to empty URL");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("Configured URL is empty, please configure a valid URL"));
}
}
public void testInvalidUrlsWithMissingScheme() throws Exception {
try {
HttpRequest.builder().fromUrl("www.test.de");
fail("Expected exception due to missing scheme");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("URL [www.test.de] does not contain a scheme"));
}
}
public void testInvalidUrlsWithHost() throws Exception {
try {
HttpRequest.builder().fromUrl("https://");
fail("Expected exception due to missing host");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("Malformed URL [https://]"));
}
}
private void assertThatManualBuilderEqualsParsingFromUrl(String url, HttpRequest.Builder builder) throws Exception {
XContentBuilder urlContentBuilder = jsonBuilder().startObject().field("url", url).endObject();
XContentParser urlContentParser = JsonXContent.jsonXContent.createParser(urlContentBuilder.bytes());
urlContentParser.nextToken();
HttpRequest.Parser parser = new HttpRequest.Parser(mock(HttpAuthRegistry.class));
HttpRequest urlParsedRequest = parser.parse(urlContentParser);
XContentBuilder xContentBuilder = builder.build().toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS);
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(xContentBuilder.bytes());
xContentParser.nextToken();
HttpRequest parsedRequest = parser.parse(xContentParser);
assertThat(parsedRequest, is(urlParsedRequest));
}
}

View File

@ -0,0 +1,86 @@
---
"Test invalid urls in email attachments reject put watch":
- do:
cluster.health:
wait_for_status: yellow
- do:
catch: /Configured URL is empty/
watcher.put_watch:
id: "my_watch"
master_timeout: "40s"
body: >
{
"trigger": {
"schedule": {
"hourly": {
"minute": [ 0, 5 ]
}
}
},
"input": {
"simple": {
"payload": {}
}
},
"condition": {
"always": {}
},
"actions": {
"send_email": {
"email": {
"to": "test.account@elastic.co",
"subject": "Cluster Status Warning",
"body": "hello",
"attachments": {
"my_id": {
"http": {
"request": { "url": "" }
}
}
}
}
}
}
}
- do:
catch: /Malformed URL/
watcher.put_watch:
id: "my_watch"
master_timeout: "40s"
body: >
{
"trigger": {
"schedule": {
"hourly": {
"minute": [ 0, 5 ]
}
}
},
"input": {
"simple": {
"payload": {}
}
},
"condition": {
"always": {}
},
"actions": {
"send_email": {
"email": {
"to": "test.account@elastic.co",
"subject": "Cluster Status Warning",
"body": "hello",
"attachments": {
"my_id": {
"http": {
"request": { "url": "https://" }
}
}
}
}
}
}
}