Correct display of warning header (#10594)

Backports PR #10470

**Commit 1:**
Correct display of warning header

Elasticsearch produces warning headers for the use of deprecated
features. These warning headers can contain commas which breaks the
splitting of multiple values on commas. Upstream Elasticsearch has
changed the warning headers to be specification compliant so that these
headers can be safely split (the warning text and warning date must be
quoted, so splits should only occur on commas that are not contained in
quotes). Additionally, the upstream change includes additional details
that are not needed for display in Console (a warning code, the
Elasticsearch version that produced the warning, and the warning
date). This commit corrects the splitting logic in Console to only split
on commas not contained in quotes, and to extract the warning text from
each warning header.

* Original sha: 02896eaed7
* Authored by Jason Tedor <jason@tedor.me> on 2017-02-20T20:44:14Z

**Commit 2:**
Safer regex handling

If the warning header from Elasticsearch comes back in the wrong format,
this could lead to the regex not matching which would lead to a
blow-up. While Elasticsearch should not do this, let us be defensive
here.

* Original sha: 7f98c48828
* Authored by Jason Tedor <jason@tedor.me> on 2017-02-22T20:23:35Z

**Commit 3:**
Stricter handling of warning headers

* Original sha: f14055cb8d
* Authored by Jason Tedor <jason@tedor.me> on 2017-02-24T19:08:28Z

**Commit 4:**
Add tests for deprecation messages

* Original sha: 8a9f4323b9
* Authored by Jason Tedor <jason@tedor.me> on 2017-02-27T15:42:56Z
This commit is contained in:
jasper 2017-02-27 12:51:21 -05:00 committed by Jonathan Budzenski
parent 406a79438e
commit 2a52a9d668
3 changed files with 74 additions and 5 deletions

View file

@ -178,13 +178,10 @@ export function initializeInput($el, $actionsEl, $copyAsCurlEl, output) {
var warnings = xhr.getResponseHeader("warning");
if (warnings) {
warnings = _.map(warnings.split(", "), function (warning) {
return "#! Deprecation: " + warning;
});
value = warnings.join("\n") + "\n" + value;
var deprecationMessages = utils.extractDeprecationMessages(warnings);
value = deprecationMessages.join("\n") + "\n" + value;
}
if (isMultiRequest) {
value = "# " + req.method + " " + req.url + "\n" + value;
}

View file

@ -56,4 +56,44 @@ utils.expandLiteralStrings = function (data) {
});
}
utils.extractDeprecationMessages = function (warnings) {
// pattern for valid warning header
var re = /\d{3} [0-9a-zA-Z!#$%&'*+-.^_`|~]+ \"((?:\t| |!|[\x23-\x5b]|[\x5d-\x7e]|[\x80-\xff]|\\\\|\\")*)\"(?: \"[^"]*\")/
// split on any comma that is followed by an even number of quotes
return _.map(utils.splitOnUnquotedCommaSpace(warnings), function (warning) {
var match = re.exec(warning)
// extract the actual warning if there was a match
return "#! Deprecation: " + (match !== null ? utils.unescape(match[1]) : warning)
});
}
utils.unescape = function (s) {
return s.replace(/\\\\/g, "\\").replace(/\\"/g, "\"")
}
utils.splitOnUnquotedCommaSpace = function (s) {
var quoted = false;
var arr = [];
var buffer = '';
var i = 0
while (i < s.length) {
var token = s.charAt(i++)
if (token == '\\' && i < s.length) {
token += s.charAt(i++)
} else if (token == ',' && i < s.length && s.charAt(i) == ' ') {
token += s.charAt(i++);
}
if (token == '"') {
quoted = !quoted
} else if (!quoted && token == ', ') {
arr.push(buffer);
buffer = '';
continue
}
buffer += token;
}
arr.push(buffer)
return arr;
}
module.exports = utils;

View file

@ -33,4 +33,36 @@ _.each(expandingTests.split(/^=+$/m), function (fixture) {
test("Literal expand - " + name, function () {
deepEqual(utils.expandLiteralStrings(collapsed), expanded);
});
test("extract deprecation messages", function () {
deepEqual(utils.extractDeprecationMessages(
'299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT"'),
['#! Deprecation: this is a warning']);
deepEqual(utils.extractDeprecationMessages(
'299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning" "Mon, 27 Feb 2017 14:52:14 GMT"'),
['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']);
deepEqual(utils.extractDeprecationMessages(
'299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma" "Mon, 27 Feb 2017 14:52:14 GMT"'),
['#! Deprecation: this is a warning, and it includes a comma']);
deepEqual(utils.extractDeprecationMessages(
'299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\\"escaped quotes\\\"" "Mon, 27 Feb 2017 14:52:14 GMT"'),
['#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"']);
});
test("unescape", function () {
deepEqual(utils.unescape('escaped backslash \\\\'), 'escaped backslash \\');
deepEqual(utils.unescape('a pair of \\\"escaped quotes\\\"'), 'a pair of "escaped quotes"');
deepEqual(utils.unescape('escaped quotes do not have to come in pairs: \\\"'), 'escaped quotes do not have to come in pairs: "');
});
test("split on unquoted comma followed by space", function () {
deepEqual(utils.splitOnUnquotedCommaSpace('a, b'), ['a', 'b']);
deepEqual(utils.splitOnUnquotedCommaSpace('a,b, c'), ['a,b', 'c']);
deepEqual(utils.splitOnUnquotedCommaSpace('"a, b"'), ['"a, b"']);
deepEqual(utils.splitOnUnquotedCommaSpace('"a, b", c'), ['"a, b"', 'c']);
deepEqual(utils.splitOnUnquotedCommaSpace('"a, b\\", c"'), ['"a, b\\", c"']);
deepEqual(utils.splitOnUnquotedCommaSpace(', a, b'), ['', 'a', 'b']);
deepEqual(utils.splitOnUnquotedCommaSpace('a, b, '), ['a', 'b', '']);
deepEqual(utils.splitOnUnquotedCommaSpace('\\"a, b", "c, d\\", e", f"'), ['\\"a', 'b", "c', 'd\\"', 'e", f"']);
});
});