Send CSP header in all responses
Introduces a new content security policy header for responses to all requests to reduce the possibility of ip leaks to outside connections. By default blocks all inline scripts, and only allows content loaded from Whoogle. Refactors a few small inline scripting cases in the project to their own individual scripts.main
parent
b7b6fb7c04
commit
dcb80ac250
|
@ -40,6 +40,13 @@ app.config['BANG_PATH'] = os.getenv(
|
||||||
app.config['BANG_FILE'] = os.path.join(
|
app.config['BANG_FILE'] = os.path.join(
|
||||||
app.config['BANG_PATH'],
|
app.config['BANG_PATH'],
|
||||||
'bangs.json')
|
'bangs.json')
|
||||||
|
app.config['CSP'] = 'default-src \'none\';' \
|
||||||
|
'img-src \'self\';' \
|
||||||
|
'style-src \'self\' \'unsafe-inline\';' \
|
||||||
|
'script-src \'self\';' \
|
||||||
|
'media-src \'self\';' \
|
||||||
|
'connect-src \'self\';' \
|
||||||
|
'form-action \'self\';'
|
||||||
|
|
||||||
if not os.path.exists(app.config['CONFIG_PATH']):
|
if not os.path.exists(app.config['CONFIG_PATH']):
|
||||||
os.makedirs(app.config['CONFIG_PATH'])
|
os.makedirs(app.config['CONFIG_PATH'])
|
||||||
|
|
|
@ -87,7 +87,7 @@ def before_request_func():
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def after_request_func(response):
|
def after_request_func(resp):
|
||||||
if app.user_elements[session['uuid']] <= 0 and '/element' in request.url:
|
if app.user_elements[session['uuid']] <= 0 and '/element' in request.url:
|
||||||
# Regenerate element key if all elements have been served to user
|
# Regenerate element key if all elements have been served to user
|
||||||
session['fernet_keys'][
|
session['fernet_keys'][
|
||||||
|
@ -108,7 +108,11 @@ def after_request_func(response):
|
||||||
for key in session_list:
|
for key in session_list:
|
||||||
session.pop(key)
|
session.pop(key)
|
||||||
|
|
||||||
return response
|
resp.headers['Content-Security-Policy'] = app.config['CSP']
|
||||||
|
if os.environ.get('HTTPS_ONLY', False):
|
||||||
|
resp.headers['Content-Security-Policy'] += 'upgrade-insecure-requests'
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
|
@ -122,15 +126,17 @@ def unknown_page(e):
|
||||||
def index():
|
def index():
|
||||||
# Reset keys
|
# Reset keys
|
||||||
session['fernet_keys'] = generate_user_keys(g.cookies_disabled)
|
session['fernet_keys'] = generate_user_keys(g.cookies_disabled)
|
||||||
error_message = session[
|
|
||||||
'error_message'] if 'error_message' in session else ''
|
# Redirect if an error was raised
|
||||||
|
if 'error_message' in session and session['error_message']:
|
||||||
|
error_message = session['error_message']
|
||||||
session['error_message'] = ''
|
session['error_message'] = ''
|
||||||
|
return render_template('error.html', error_message=error_message)
|
||||||
|
|
||||||
return render_template('index.html',
|
return render_template('index.html',
|
||||||
languages=app.config['LANGUAGES'],
|
languages=app.config['LANGUAGES'],
|
||||||
countries=app.config['COUNTRIES'],
|
countries=app.config['COUNTRIES'],
|
||||||
config=g.user_config,
|
config=g.user_config,
|
||||||
error_message=error_message,
|
|
||||||
tor_available=int(os.environ.get('TOR_AVAILABLE')),
|
tor_available=int(os.environ.get('TOR_AVAILABLE')),
|
||||||
version_number=app.config['VERSION_NUMBER'])
|
version_number=app.config['VERSION_NUMBER'])
|
||||||
|
|
||||||
|
@ -286,7 +292,9 @@ def url():
|
||||||
if len(q) > 0 and 'http' in q:
|
if len(q) > 0 and 'http' in q:
|
||||||
return redirect(q)
|
return redirect(q)
|
||||||
else:
|
else:
|
||||||
return render_template('error.html', query=q)
|
return render_template(
|
||||||
|
'error.html',
|
||||||
|
error_message='Unable to resolve query: ' + q)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/imgres')
|
@app.route('/imgres')
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const searchBar = document.getElementById("search-bar");
|
||||||
|
|
||||||
|
searchBar.addEventListener("keyup", function (event) {
|
||||||
|
if (event.keyCode !== 13) {
|
||||||
|
handleUserInput(searchBar);
|
||||||
|
} else {
|
||||||
|
document.getElementById("search-form").submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -28,7 +28,7 @@ const checkForTracking = () => {
|
||||||
/^[0-9]{15}$/
|
/^[0-9]{15}$/
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Creates a link to a UPS/USPS/FedEx tracking page
|
// Creates a link to a UPS/USPS/FedEx tracking page
|
||||||
const createTrackingLink = href => {
|
const createTrackingLink = href => {
|
||||||
|
@ -37,7 +37,7 @@ const checkForTracking = () => {
|
||||||
link.innerHTML = "View Tracking Info";
|
link.innerHTML = "View Tracking Info";
|
||||||
link.href = href;
|
link.href = href;
|
||||||
mainDiv.prepend(link);
|
mainDiv.prepend(link);
|
||||||
}
|
};
|
||||||
|
|
||||||
// Compares the query against a set of regex patterns
|
// Compares the query against a set of regex patterns
|
||||||
// for tracking numbers
|
// for tracking numbers
|
||||||
|
@ -48,12 +48,12 @@ const checkForTracking = () => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
for (const key of Object.keys(matchTracking)) {
|
for (const key of Object.keys(matchTracking)) {
|
||||||
compareQuery(matchTracking[key]);
|
compareQuery(matchTracking[key]);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
checkForTracking();
|
checkForTracking();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<h1>Error</h1>
|
<h1>Error</h1>
|
||||||
<hr>
|
<hr>
|
||||||
<p>
|
<p>
|
||||||
Error parsing "{{ query }}"
|
Error: "{{ error_message|safe }}"
|
||||||
</p>
|
</p>
|
||||||
<a href="/">Return Home</a>
|
<a href="/">Return Home</a>
|
||||||
|
|
|
@ -48,14 +48,4 @@
|
||||||
</header>
|
</header>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script>
|
<script type="text/javascript" src="static/js/header.js"></script>
|
||||||
const searchBar = document.getElementById("search-bar");
|
|
||||||
|
|
||||||
searchBar.addEventListener("keyup", function (event) {
|
|
||||||
if (event.keyCode !== 13) {
|
|
||||||
handleUserInput(searchBar);
|
|
||||||
} else {
|
|
||||||
document.getElementById("search-form").submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -36,12 +36,6 @@
|
||||||
<title>Whoogle Search</title>
|
<title>Whoogle Search</title>
|
||||||
</head>
|
</head>
|
||||||
<body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}">
|
<body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}">
|
||||||
<script>
|
|
||||||
{% if error_message|length > 0 %}
|
|
||||||
let error = "{{ error_message|safe }}";
|
|
||||||
alert(error);
|
|
||||||
{% endif %}
|
|
||||||
</script>
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<img class="logo" src="static/img/logo.png">
|
<img class="logo" src="static/img/logo.png">
|
||||||
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
||||||
|
|
Loading…
Reference in New Issue