mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-01-19 08:42:44 +08:00
Authenticate connections to web_config service
- Require all requests to use a session path. - Use a redirect file to avoid exposing the '/start' URL on the command line, as it contains the cookie value. Fix for CVE-2014-2914. Closes #1438.
This commit is contained in:
parent
8844f0c142
commit
4ae2753025
|
@ -83,7 +83,7 @@ controllers.controller("colorsController", function($scope, $http) {
|
|||
|
||||
|
||||
$scope.getCurrentTheme = function() {
|
||||
$http.get("/colors/").success(function(data, status, headers, config) {
|
||||
$http.get("colors/").success(function(data, status, headers, config) {
|
||||
var currentScheme = { "name": "Current", "colors":[], "preferred_background": "" };
|
||||
for (var i in data) {
|
||||
currentScheme[data[i].name] = data[i].color;
|
||||
|
@ -103,7 +103,7 @@ controllers.controller("colorsController", function($scope, $http) {
|
|||
var remaining = settingNames.length;
|
||||
for (name in settingNames) {
|
||||
var postData = "what=" + settingNames[name] + "&color=" + $scope.selectedColorScheme[settingNames[name]] + "&background_color=&bold=&underline=";
|
||||
$http.post("/set_color/", postData, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$http.post("set_color/", postData, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
if (status == 200) {
|
||||
remaining -= 1;
|
||||
if (remaining == 0) {
|
||||
|
@ -124,7 +124,7 @@ controllers.controller("promptController", function($scope, $http) {
|
|||
$scope.savePromptButtonTitle = "Set Prompt";
|
||||
|
||||
$scope.fetchSamplePrompts= function() {
|
||||
$http.get("/sample_prompts/").success(function(data, status, headers, config) {
|
||||
$http.get("sample_prompts/").success(function(data, status, headers, config) {
|
||||
$scope.samplePrompts = data;
|
||||
$scope.samplePromptsArrayArray = get_colors_as_nested_array($scope.samplePrompts, 1);
|
||||
|
||||
|
@ -140,7 +140,7 @@ controllers.controller("promptController", function($scope, $http) {
|
|||
}
|
||||
|
||||
$scope.setNewPrompt = function(selectedPrompt) {
|
||||
$http.post("/set_prompt/","what=" + encodeURIComponent(selectedPrompt.function), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config){
|
||||
$http.post("set_prompt/","what=" + encodeURIComponent(selectedPrompt.function), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config){
|
||||
|
||||
// Update attributes of current prompt and select it
|
||||
$scope.samplePrompts[0].demo = selectedPrompt.demo;
|
||||
|
@ -171,7 +171,7 @@ controllers.controller("functionsController", function($scope, $http) {
|
|||
}
|
||||
|
||||
$scope.fetchFunctions= function() {
|
||||
$http.get("/functions/").success(function(data, status, headers, config) {
|
||||
$http.get("functions/").success(function(data, status, headers, config) {
|
||||
$scope.functions = data;
|
||||
$scope.selectFunction($scope.functions[0]);
|
||||
})};
|
||||
|
@ -195,7 +195,7 @@ controllers.controller("functionsController", function($scope, $http) {
|
|||
}
|
||||
|
||||
$scope.fetchFunctionDefinition = function(name) {
|
||||
$http.post("/get_function/","what=" + name, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$http.post("get_function/","what=" + name, { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$scope.functionDefinition = $scope.cleanupFishFunction(data[0]);
|
||||
})};
|
||||
|
||||
|
@ -206,7 +206,7 @@ controllers.controller("variablesController", function($scope, $http) {
|
|||
$scope.query = null;
|
||||
|
||||
$scope.fetchVariables= function() {
|
||||
$http.get("/variables/").success(function(data, status, headers, config) {
|
||||
$http.get("variables/").success(function(data, status, headers, config) {
|
||||
$scope.variables = data;
|
||||
})};
|
||||
|
||||
|
@ -247,7 +247,7 @@ controllers.controller("historyController", function($scope, $http, $timeout) {
|
|||
}
|
||||
// Get history from server
|
||||
$scope.fetchHistory = function() {
|
||||
$http.get("/history/").success(function(data, status, headers, config) {
|
||||
$http.get("history/").success(function(data, status, headers, config) {
|
||||
$scope.historySize = data.length;
|
||||
$scope.remainingItems = data;
|
||||
|
||||
|
@ -257,7 +257,7 @@ controllers.controller("historyController", function($scope, $http, $timeout) {
|
|||
|
||||
$scope.deleteHistoryItem = function(item) {
|
||||
index = $scope.historyItems.indexOf(item);
|
||||
$http.post("/delete_history_item/","what=" + encodeURIComponent(item), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$http.post("delete_history_item/","what=" + encodeURIComponent(item), { headers: {'Content-Type': 'application/x-www-form-urlencoded'} }).success(function(data, status, headers, config) {
|
||||
$scope.historyItems.splice(index, 1);
|
||||
})};
|
||||
|
||||
|
@ -278,7 +278,7 @@ controllers.controller("historyController", function($scope, $http, $timeout) {
|
|||
controllers.controller("bindingsController", function($scope, $http) {
|
||||
$scope.bindings = [];
|
||||
$scope.fetchBindings = function() {
|
||||
$http.get("/bindings/").success(function(data, status, headers, config) {
|
||||
$http.get("bindings/").success(function(data, status, headers, config) {
|
||||
$scope.bindings = data;
|
||||
})};
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ if term:
|
|||
os.environ['TERM'] = term
|
||||
|
||||
import subprocess
|
||||
import re, socket, cgi, select, time, glob
|
||||
import re, socket, cgi, select, time, glob, random, string
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
|
@ -693,9 +693,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
else: font_size = '18pt'
|
||||
return font_size
|
||||
|
||||
|
||||
def do_GET(self):
|
||||
p = self.path
|
||||
|
||||
authpath = '/' + authkey
|
||||
if p.startswith(authpath):
|
||||
p = p[len(authpath):]
|
||||
else:
|
||||
return self.send_error(403)
|
||||
self.path = p
|
||||
|
||||
if p == '/colors/':
|
||||
output = self.do_get_colors()
|
||||
elif p == '/functions/':
|
||||
|
@ -727,6 +734,14 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
|
||||
def do_POST(self):
|
||||
p = self.path
|
||||
|
||||
authpath = '/' + authkey
|
||||
if p.startswith(authpath):
|
||||
p = p[len(authpath):]
|
||||
else:
|
||||
return self.send_error(403)
|
||||
self.path = p
|
||||
|
||||
if IS_PY2:
|
||||
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
|
||||
else: # Python 3
|
||||
|
@ -788,6 +803,18 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|||
""" Disable request logging """
|
||||
pass
|
||||
|
||||
redirect_template_html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;URL='%s'" />
|
||||
</head>
|
||||
<body>
|
||||
<p><a href="%s">Start the Fish Web config</a></p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
# find fish
|
||||
fish_bin_dir = os.environ.get('__fish_bin_dir')
|
||||
fish_bin_path = None
|
||||
|
@ -823,6 +850,9 @@ initial_wd = os.getcwd()
|
|||
where = os.path.dirname(sys.argv[0])
|
||||
os.chdir(where)
|
||||
|
||||
# Generate a 16-byte random key as a hexadecimal string
|
||||
authkey = hex(random.getrandbits(16*4))[2:]
|
||||
|
||||
# Try to find a suitable port
|
||||
PORT = 8000
|
||||
while PORT <= 9000:
|
||||
|
@ -852,9 +882,36 @@ if len(sys.argv) > 1:
|
|||
initial_tab = '#' + tab
|
||||
break
|
||||
|
||||
url = 'http://localhost:%d/%s' % (PORT, initial_tab)
|
||||
print("Web config started at '%s'. Hit enter to stop." % url)
|
||||
webbrowser.open(url)
|
||||
url = 'http://localhost:%d/%s/%s' % (PORT, authkey, initial_tab)
|
||||
|
||||
# Create temporary file to hold redirect to real server
|
||||
# This prevents exposing the URL containing the authentication key on the command line
|
||||
# (see CVE-2014-2914 or https://github.com/fish-shell/fish-shell/issues/1438)
|
||||
if 'XDG_CACHE_HOME' in os.environ:
|
||||
dirname = os.path.expanduser(os.path.expandvars('$XDG_CACHE_HOME/fish/'))
|
||||
else:
|
||||
dirname = os.path.expanduser('~/.cache/fish/')
|
||||
|
||||
os.umask(0o0077)
|
||||
try:
|
||||
os.makedirs(dirname, 0o0700)
|
||||
except OSError as e:
|
||||
if e.errno == 17:
|
||||
pass
|
||||
else:
|
||||
raise e
|
||||
|
||||
randtoken = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
|
||||
filename = dirname + 'web_config-%s.html' % randtoken
|
||||
|
||||
f = open(filename, 'w')
|
||||
f.write(redirect_template_html % (url, url))
|
||||
f.close()
|
||||
|
||||
# Open temporary file as URL
|
||||
fileurl = 'file://' + filename
|
||||
print("Web config started at '%s'. Hit enter to stop." % fileurl)
|
||||
webbrowser.open(fileurl)
|
||||
|
||||
# Select on stdin and httpd
|
||||
stdin_no = sys.stdin.fileno()
|
||||
|
@ -871,3 +928,5 @@ try:
|
|||
except KeyboardInterrupt:
|
||||
print("\nShutting down.")
|
||||
|
||||
# Clean up temporary file
|
||||
os.remove(filename)
|
||||
|
|
Loading…
Reference in New Issue
Block a user