mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-25 09:39:52 +08:00
Authenticate connections to web_config service
- Require all requests to use a session path. - Use a redirect file to avoid exposing the URL on the command line, as it contains the session path. Fix for CVE-2014-2914. Closes #1438.
This commit is contained in:
parent
2aac8e5dde
commit
1e68508b0c
|
@ -556,7 +556,7 @@ function switch_tab(new_tab) {
|
||||||
if (new_tab == 'tab_colors') {
|
if (new_tab == 'tab_colors') {
|
||||||
/* Keep track of whether this is the first element */
|
/* Keep track of whether this is the first element */
|
||||||
var first = true
|
var first = true
|
||||||
run_get_request('/colors/', function(key_and_values){
|
run_get_request('colors/', function(key_and_values){
|
||||||
/* Result is name, description, value */
|
/* Result is name, description, value */
|
||||||
var key = key_and_values[0]
|
var key = key_and_values[0]
|
||||||
var description = key_and_values[1]
|
var description = key_and_values[1]
|
||||||
|
@ -577,7 +577,7 @@ function switch_tab(new_tab) {
|
||||||
sample_prompts.length = 0
|
sample_prompts.length = 0
|
||||||
/* Color the first one blue */
|
/* Color the first one blue */
|
||||||
var first = true;
|
var first = true;
|
||||||
run_get_request('/sample_prompts/', function(sample_prompt){
|
run_get_request('sample_prompts/', function(sample_prompt){
|
||||||
var name = sample_prompt['name']
|
var name = sample_prompt['name']
|
||||||
sample_prompts[name] = sample_prompt
|
sample_prompts[name] = sample_prompt
|
||||||
var color = first ? '66F' : 'AAA'
|
var color = first ? '66F' : 'AAA'
|
||||||
|
@ -594,7 +594,7 @@ function switch_tab(new_tab) {
|
||||||
} else if (new_tab == 'tab_functions') {
|
} else if (new_tab == 'tab_functions') {
|
||||||
/* Keep track of whether this is the first element */
|
/* Keep track of whether this is the first element */
|
||||||
var first = true
|
var first = true
|
||||||
run_get_request('/functions/', function(contents){
|
run_get_request('functions/', function(contents){
|
||||||
var elem = create_master_element(contents, false/* description */, 'AAAAAA', '11pt', select_function_master_element)
|
var elem = create_master_element(contents, false/* description */, 'AAAAAA', '11pt', select_function_master_element)
|
||||||
if (first) {
|
if (first) {
|
||||||
/* It's the first element, so select it, so something gets selected */
|
/* It's the first element, so select it, so something gets selected */
|
||||||
|
@ -606,7 +606,7 @@ function switch_tab(new_tab) {
|
||||||
$('#master_detail_table').show()
|
$('#master_detail_table').show()
|
||||||
wants_data_table = false
|
wants_data_table = false
|
||||||
} else if (new_tab == 'tab_variables') {
|
} else if (new_tab == 'tab_variables') {
|
||||||
run_get_request_with_bulk_handler('/variables/', function(json_contents){
|
run_get_request_with_bulk_handler('variables/', function(json_contents){
|
||||||
var rows = new Array()
|
var rows = new Array()
|
||||||
for (var i = 0; i < json_contents.length; i++) {
|
for (var i = 0; i < json_contents.length; i++) {
|
||||||
var contents = json_contents[i]
|
var contents = json_contents[i]
|
||||||
|
@ -622,7 +622,7 @@ function switch_tab(new_tab) {
|
||||||
} else if (new_tab == 'tab_history') {
|
} else if (new_tab == 'tab_history') {
|
||||||
// Clear the history map
|
// Clear the history map
|
||||||
history_element_map.length = 0
|
history_element_map.length = 0
|
||||||
run_get_request_with_bulk_handler('/history/', function(json_contents){
|
run_get_request_with_bulk_handler('history/', function(json_contents){
|
||||||
start = new Date().getTime()
|
start = new Date().getTime()
|
||||||
var rows = new Array()
|
var rows = new Array()
|
||||||
for (var i = 0; i < json_contents.length; i++) {
|
for (var i = 0; i < json_contents.length; i++) {
|
||||||
|
@ -757,7 +757,7 @@ function select_color_master_element(elem) {
|
||||||
function select_function_master_element(elem) {
|
function select_function_master_element(elem) {
|
||||||
select_master_element(elem)
|
select_master_element(elem)
|
||||||
|
|
||||||
run_post_request('/get_function/', {
|
run_post_request('get_function/', {
|
||||||
what: current_master_element_name()
|
what: current_master_element_name()
|
||||||
}, function(contents){
|
}, function(contents){
|
||||||
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
||||||
|
@ -773,7 +773,7 @@ function select_sample_prompt_master_element(elem) {
|
||||||
select_master_element(elem)
|
select_master_element(elem)
|
||||||
var name = current_master_element_name()
|
var name = current_master_element_name()
|
||||||
sample_prompt = sample_prompts[name]
|
sample_prompt = sample_prompts[name]
|
||||||
run_post_request('/get_sample_prompt/', {
|
run_post_request('get_sample_prompt/', {
|
||||||
what: sample_prompt['function']
|
what: sample_prompt['function']
|
||||||
}, function(keys_and_values){
|
}, function(keys_and_values){
|
||||||
var prompt_func = keys_and_values['function']
|
var prompt_func = keys_and_values['function']
|
||||||
|
@ -788,7 +788,7 @@ function select_sample_prompt_master_element(elem) {
|
||||||
function select_current_prompt_master_element(elem) {
|
function select_current_prompt_master_element(elem) {
|
||||||
$('.prompt_save_button').hide()
|
$('.prompt_save_button').hide()
|
||||||
select_master_element(elem)
|
select_master_element(elem)
|
||||||
run_get_request_with_bulk_handler('/current_prompt/', function(keys_and_values){
|
run_get_request_with_bulk_handler('current_prompt/', function(keys_and_values){
|
||||||
var prompt_func = keys_and_values['function']
|
var prompt_func = keys_and_values['function']
|
||||||
var prompt_demo = keys_and_values['demo']
|
var prompt_demo = keys_and_values['demo']
|
||||||
var prompt_font_size = keys_and_values['font_size']
|
var prompt_font_size = keys_and_values['font_size']
|
||||||
|
@ -801,7 +801,7 @@ function select_current_prompt_master_element(elem) {
|
||||||
function save_current_prompt() {
|
function save_current_prompt() {
|
||||||
var name = current_master_element_name()
|
var name = current_master_element_name()
|
||||||
var sample_prompt = sample_prompts[name]
|
var sample_prompt = sample_prompts[name]
|
||||||
run_post_request('/set_prompt/', {
|
run_post_request('set_prompt/', {
|
||||||
what: sample_prompt['function']
|
what: sample_prompt['function']
|
||||||
}, function(contents){
|
}, function(contents){
|
||||||
if (contents == "OK") {
|
if (contents == "OK") {
|
||||||
|
@ -817,7 +817,7 @@ function post_style_to_server() {
|
||||||
if (! style)
|
if (! style)
|
||||||
return
|
return
|
||||||
|
|
||||||
run_post_request('/set_color/', {
|
run_post_request('set_color/', {
|
||||||
what: current_master_element_name(),
|
what: current_master_element_name(),
|
||||||
color: style.color,
|
color: style.color,
|
||||||
background_color: style.background_color,
|
background_color: style.background_color,
|
||||||
|
@ -1221,7 +1221,7 @@ function escape_HTML(foo) {
|
||||||
function tell_fish_to_delete_element(idx) {
|
function tell_fish_to_delete_element(idx) {
|
||||||
var row_elem = $('#data_table_row_' + idx)
|
var row_elem = $('#data_table_row_' + idx)
|
||||||
var txt = history_element_map[idx]
|
var txt = history_element_map[idx]
|
||||||
run_post_request('/delete_history_item/', {
|
run_post_request('delete_history_item/', {
|
||||||
what: txt
|
what: txt
|
||||||
}, function(contents){
|
}, function(contents){
|
||||||
if (contents == "OK") {
|
if (contents == "OK") {
|
||||||
|
|
|
@ -17,7 +17,7 @@ else:
|
||||||
from urllib.parse import parse_qs
|
from urllib.parse import parse_qs
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import subprocess
|
import subprocess
|
||||||
import re, socket, os, sys, cgi, select, time, glob
|
import re, socket, os, sys, cgi, select, time, glob, random, string
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -487,9 +487,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||||
else: font_size = '18pt'
|
else: font_size = '18pt'
|
||||||
return font_size
|
return font_size
|
||||||
|
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
p = self.path
|
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/':
|
if p == '/colors/':
|
||||||
output = self.do_get_colors()
|
output = self.do_get_colors()
|
||||||
elif p == '/functions/':
|
elif p == '/functions/':
|
||||||
|
@ -521,6 +528,14 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
p = self.path
|
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:
|
if IS_PY2:
|
||||||
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
|
ctype, pdict = cgi.parse_header(self.headers.getheader('content-type'))
|
||||||
else: # Python 3
|
else: # Python 3
|
||||||
|
@ -585,6 +600,18 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||||
""" Disable request logging """
|
""" Disable request logging """
|
||||||
pass
|
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
|
# find fish
|
||||||
fish_bin_dir = os.environ.get('__fish_bin_dir')
|
fish_bin_dir = os.environ.get('__fish_bin_dir')
|
||||||
fish_bin_path = None
|
fish_bin_path = None
|
||||||
|
@ -620,6 +647,9 @@ initial_wd = os.getcwd()
|
||||||
where = os.path.dirname(sys.argv[0])
|
where = os.path.dirname(sys.argv[0])
|
||||||
os.chdir(where)
|
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
|
# Try to find a suitable port
|
||||||
PORT = 8000
|
PORT = 8000
|
||||||
while PORT <= 9000:
|
while PORT <= 9000:
|
||||||
|
@ -649,9 +679,36 @@ if len(sys.argv) > 1:
|
||||||
initial_tab = '#' + tab
|
initial_tab = '#' + tab
|
||||||
break
|
break
|
||||||
|
|
||||||
url = 'http://localhost:%d/%s' % (PORT, initial_tab)
|
url = 'http://localhost:%d/%s/%s' % (PORT, authkey, initial_tab)
|
||||||
print("Web config started at '%s'. Hit enter to stop." % url)
|
|
||||||
webbrowser.open(url)
|
# 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
|
# Select on stdin and httpd
|
||||||
stdin_no = sys.stdin.fileno()
|
stdin_no = sys.stdin.fileno()
|
||||||
|
@ -668,3 +725,5 @@ try:
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nShutting down.")
|
print("\nShutting down.")
|
||||||
|
|
||||||
|
# Clean up temporary file
|
||||||
|
os.remove(filename)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user