Merge pull request #7067 from ycombinator/gh-7059

Disallow Console from functioning as an open web proxy
This commit is contained in:
Shaunak Kashyap 2016-05-11 09:19:01 -07:00
commit 5a41292a68
15 changed files with 43 additions and 195 deletions

View file

@ -16,7 +16,9 @@ module.exports = function (kibana) {
main: 'plugins/console/console',
icon: 'plugins/console/logo.svg',
injectVars: function (server, options) {
return options;
const varsToInject = options;
varsToInject.elasticsearchUrl = server.config().get('elasticsearch.url');
return varsToInject;
}
}
];
@ -33,11 +35,11 @@ module.exports = function (kibana) {
return new kibana.Plugin({
id: 'console',
require: [ 'elasticsearch' ],
config: function (Joi) {
return Joi.object({
enabled: Joi.boolean().default(true),
defaultServerUrl: Joi.string().default('http://localhost:9200'),
proxyFilter: Joi.array().items(Joi.string()).single().default(['.*']),
ssl: Joi.object({
verify: Joi.boolean(),
@ -88,10 +90,7 @@ module.exports = function (kibana) {
const proxyRouteConfig = {
validate: {
query: Joi.object().keys({
uri: Joi.string().uri({
allowRelative: false,
shema: ['http:', 'https:'],
}),
uri: Joi.string()
}).unknown(true),
},
@ -111,15 +110,23 @@ module.exports = function (kibana) {
],
handler(req, reply) {
const { uri } = req.query;
let baseUri = server.config().get('elasticsearch.url');
let { uri:path } = req.query;
baseUri = baseUri.replace(/\/+$/, '');
path = path.replace(/^\/+/, '');
const uri = baseUri + '/' + path;
const requestHeadersWhitelist = server.config().get('elasticsearch.requestHeadersWhitelist');
const filterHeaders = server.plugins.elasticsearch.filterHeaders;
reply.proxy({
uri,
mapUri: function (request, done) {
done(null, uri, filterHeaders(request.headers, requestHeadersWhitelist))
},
xforward: true,
passThrough: true,
onResponse(err, res, request, reply, settings, ttl) {
if (err != null) {
reply("Error connecting to '" + request.query.uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
reply("Error connecting to '" + uri + "':\n\n" + err.message).type("text/plain").statusCode = 502;
} else {
reply(null, res);
}

View file

@ -10,15 +10,10 @@ let utils = require('./utils');
let _ = require('lodash');
const chrome = require('ui/chrome');
const defaultServerUrl = chrome.getInjected('defaultServerUrl');
$(document.body).removeClass('fouc');
// set the value of the server and/or the input and clear the output
function resetToValues(server, content) {
if (server != null) {
es.setBaseUrl(server);
}
// set the value of the input and clear the output
function resetToValues(content) {
if (content != null) {
input.update(content);
}
@ -31,10 +26,10 @@ function loadSavedState() {
if (sourceLocation == "stored") {
if (previousSaveState) {
resetToValues(previousSaveState.server, previousSaveState.content);
resetToValues(previousSaveState.content);
}
else {
resetToValues(defaultServerUrl);
resetToValues();
input.autoIndent();
}
}
@ -44,17 +39,14 @@ function loadSavedState() {
loadFrom.headers = {Accept: "application/vnd.github.v3.raw"};
}
$.ajax(loadFrom).done(function (data) {
resetToValues(defaultServerUrl, data);
resetToValues(data);
input.moveToNextRequestEdge(true);
input.highlightCurrentRequestsAndUpdateActionBar();
input.updateActionsBar();
});
}
else if (previousSaveState) {
resetToValues(previousSaveState.server);
}
else {
resetToValues(defaultServerUrl);
resetToValues();
}
input.moveToNextRequestEdge(true);
}
@ -69,15 +61,12 @@ function setupAutosave() {
}
timer = setTimeout(saveCurrentState, saveDelay);
});
es.addServerChangeListener(saveCurrentState);
}
function saveCurrentState() {
try {
var content = input.getValue();
var server = es.getBaseUrl();
history.updateCurrentState(server, content);
history.updateCurrentState(content);
}
catch (e) {
console.log("Ignoring saving error: " + e);

View file

@ -32,13 +32,4 @@ module.controller('SenseController', function SenseController($scope, docTitle)
event.preventDefault();
input.focus();
};
this.serverUrl = es.getBaseUrl();
// read server url changes into scope
es.addServerChangeListener((server) => {
$scope.$evalAsync(() => {
this.serverUrl = server;
});
});
});

View file

@ -7,27 +7,3 @@
</span>
</div>
</kbn-top-nav>
<navbar ng-show="chrome.getVisible()" name="sense-serverInput">
<form
name="serverInput"
class="fill inline-form"
ng-submit="sense.sendSelected()"
role="form">
<input
type="text"
placeholder="http://servername:port"
aria-label="Server Name"
class="form-control"
ng-focus="navbar.updateServerUrlHistory()"
ng-blur="navbar.commitServerUrlFormModel()"
sense-uib-typeahead="url for url in navbar.serverUrlHistory"
ng-model="navbar.serverUrlFormModel"
typeahead-append-to-body="true"
typeahead-focus-first="false"
required>
</form>
</navbar>

View file

@ -14,7 +14,6 @@ require('ui/modules')
scope: {},
link($scope, $el, attrs, sense) {
$scope.sense = sense
$scope.navbar.link($scope)
},
controllerAs: 'navbar',
controller: class SenseNavbarController {
@ -49,21 +48,6 @@ require('ui/modules')
this.menu.open('welcome')
}
this.updateServerUrlHistory();
}
link($scope) {
$scope.$watch('sense.serverUrl', (url) => {
this.serverUrlFormModel = url
})
}
updateServerUrlHistory() {
this.serverUrlHistory = history.getHistoricalServers();
}
commitServerUrlFormModel() {
es.setBaseUrl(this.serverUrlFormModel);
}
}
};

View file

@ -1,13 +1,8 @@
let _ = require('lodash');
let $ = require('jquery');
let baseUrl;
let serverChangeListeners = [];
let esVersion = [];
module.exports.getBaseUrl = function () {
return baseUrl;
};
module.exports.getVersion = function () {
return esVersion;
};
@ -15,15 +10,7 @@ module.exports.getVersion = function () {
module.exports.send = function (method, path, data, server, disable_auth_alert) {
var wrappedDfd = $.Deferred();
server = server || exports.getBaseUrl();
path = exports.constructESUrl(server, path);
var uname_password_re = /^(https?:\/\/)?(?:(?:([^\/]*):)?([^\/]*?)@)?(.*)$/;
var url_parts = path.match(uname_password_re);
var uname = url_parts[2];
var password = url_parts[3];
path = url_parts[1] + url_parts[4];
console.log("Calling " + path + " (uname: " + uname + " pwd: " + password + ")");
console.log("Calling " + path);
if (data && method == "GET") {
method = "POST";
}
@ -37,8 +24,6 @@ module.exports.send = function (method, path, data, server, disable_auth_alert)
cache: false,
crossDomain: true,
type: method,
password: password,
username: uname,
dataType: "text", // disable automatic guessing
};
@ -56,61 +41,8 @@ module.exports.send = function (method, path, data, server, disable_auth_alert)
return wrappedDfd;
};
module.exports.constructESUrl = function (server, path) {
if (!path) {
path = server;
server = exports.getBaseUrl();
}
if (path.indexOf("://") >= 0) {
return path;
}
if (server.indexOf("://") < 0) {
server = (document.location.protocol || "http:") + "//" + server;
}
if (server.substr(-1) == "/") {
server = server.substr(0, server.length - 1);
}
if (path.charAt(0) === "/") {
path = path.substr(1);
}
return server + "/" + path;
};
module.exports.forceRefresh = function () {
exports.setBaseUrl(baseUrl, true)
};
module.exports.setBaseUrl = function (base, force) {
if (baseUrl !== base || force) {
var old = baseUrl;
baseUrl = base;
exports.send("GET", "/").done(function (data, status, xhr) {
if (xhr.status === 200) {
// parse for version
var value = xhr.responseText;
try {
value = JSON.parse(value);
if (value.version && value.version.number) {
esVersion = value.version.number.split(".");
}
}
catch (e) {
}
}
_.each(serverChangeListeners, function (cb) {
cb(base, old)
});
}).fail(function () {
esVersion = []; // unknown
_.each(serverChangeListeners, function (cb) {
cb(base, old)
});
});
}
};
module.exports.addServerChangeListener = function (cb) {
serverChangeListeners.push(cb);
module.exports.constructESUrl = function (baseUri, path) {
baseUri = baseUri.replace(/\/+$/, '');
path = path.replace(/^\/+/, '');
return baseUri + '/' + path;
};

View file

@ -3,8 +3,6 @@ const { uniq } = require('lodash');
const storage = require('./storage');
const chrome = require('ui/chrome');
const defaultServerUrl = chrome.getInjected('defaultServerUrl');
const history = module.exports = {
restoreFromHistory() {
// default method for history.restoreFromHistory
@ -26,11 +24,7 @@ const history = module.exports = {
.map(key => storage.get(key));
},
getHistoricalServers() {
return uniq(history.getHistory().map(req => req.server));
},
addToHistory(server, endpoint, method, data) {
addToHistory(endpoint, method, data) {
var keys = history.getHistoryKeys();
keys.splice(0, 500); // only maintain most recent X;
$.each(keys, function (i, k) {
@ -41,18 +35,16 @@ const history = module.exports = {
var k = "hist_elem_" + timestamp;
storage.set(k, {
time: timestamp,
server: server,
endpoint: endpoint,
method: method,
data: data
});
},
updateCurrentState(server, content) {
updateCurrentState(content) {
var timestamp = new Date().getTime();
storage.set("editor_state", {
time: timestamp,
server: server === defaultServerUrl ? undefined : server,
content: content
});
},
@ -60,8 +52,8 @@ const history = module.exports = {
getSavedEditorState() {
const saved = storage.get('editor_state');
if (!saved) return;
const { time, server = defaultServerUrl, content } = saved;
return { time, server, content };
const { time, content } = saved;
return { time, content };
},
clearHistory($el) {

View file

@ -158,7 +158,7 @@ function sendCurrentRequestToES() {
((xhr.status >= 200 && xhr.status < 300) || xhr.status == 404)
) {
// we have someone on the other side. Add to history
history.addToHistory(es.getBaseUrl(), es_path, es_method, es_data);
history.addToHistory(es_path, es_method, es_data);
let value = xhr.responseText;

View file

@ -238,27 +238,7 @@ function setActiveApi(api) {
ACTIVE_API = api;
}
es.addServerChangeListener(function () {
var version = es.getVersion() || [];
var api;
switch (version[0]) {
case '5':
api = 'es_5_0';
break;
case '2':
api = 'es_2_0';
break;
case '1':
default:
api = 'es_1_0';
}
if (api) {
setActiveApi(api);
}
});
setActiveApi('es_5_0');
module.exports.setActiveApi = setActiveApi;
module.exports.getGlobalAutocompleteComponents = getGlobalAutocompleteComponents;

View file

@ -279,8 +279,6 @@ function autocomplete_retriever() {
}, 60000);
}
es.addServerChangeListener(retrieveAutocompleteInfoFromServer);
module.exports = _.assign(mappingObj, {
getFields: getFields,
getIndices: getIndices,

View file

@ -6,6 +6,8 @@ let RowParser = require('./row_parser');
let InputMode = require('./mode/input');
let utils = require('../utils');
let es = require('../es');
import chrome from 'ui/chrome';
const smartResize = require('../smart_resize');
function isInt(x) {
@ -538,7 +540,8 @@ function SenseEditor($el) {
es_method = req.method,
es_data = req.data;
var url = es.constructESUrl(es.getBaseUrl() || "localhost:9200", es_path);
const elasticsearchBaseUrl = chrome.getInjected('elasticsearchUrl');
var url = es.constructESUrl(elasticsearchBaseUrl, es_path);
var ret = 'curl -X' + es_method + ' "' + url + '"';
if (es_data && es_data.length) {

View file

@ -61,7 +61,6 @@ function updateSettings({ fontSize, wrapMode, autocomplete}) {
setWrapMode(wrapMode);
setAutocomplete(autocomplete);
require('./input').focus();
es.forceRefresh();
return getCurrentSettings();
}

View file

@ -9,7 +9,6 @@ var {test, module, ok, fail, asyncTest, deepEqual, equal, start} = QUnit;
module("Editor", {
setup: function () {
es.setBaseUrl("http://localhost:9200");
input.$el.show();
input.autocomplete._test.removeChangeListener();

View file

@ -5,6 +5,7 @@ const readFile = (file) => require('fs').readFileSync(file, 'utf8');
import util from 'util';
import url from 'url';
import callWithRequest from './call_with_request';
import filterHeaders from './filter_headers';
module.exports = function (server) {
const config = server.config();
@ -80,6 +81,7 @@ module.exports = function (server) {
server.expose('createClient', createClient);
server.expose('callWithRequestFactory', _.partial(callWithRequest, server));
server.expose('callWithRequest', callWithRequest(server, noAuthClient));
server.expose('filterHeaders', filterHeaders);
server.expose('errors', elasticsearch.errors);
return client;

View file

@ -42,13 +42,9 @@ import {
.catch(common.handleError(this));
});
bdd.it('default request reponse should contain .kibana' , function () {
bdd.it('default request response should contain .kibana' , function () {
var expectedResponseContains = '"_index": ".kibana",';
var elasticsearch = common.getEsHostPort();
return consolePage.setServer(elasticsearch)
.then(function () {
return consolePage.clickPlay();
})
consolePage.clickPlay()
.then(function () {
return common.try(function () {
return consolePage.getResponse()