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
Ben Busby 2021-03-07 14:04:05 -05:00 committed by Ben Busby
parent b7b6fb7c04
commit dcb80ac250
7 changed files with 39 additions and 29 deletions

View File

@ -40,6 +40,13 @@ app.config['BANG_PATH'] = os.getenv(
app.config['BANG_FILE'] = os.path.join(
app.config['BANG_PATH'],
'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']):
os.makedirs(app.config['CONFIG_PATH'])

View File

@ -87,7 +87,7 @@ def before_request_func():
@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:
# Regenerate element key if all elements have been served to user
session['fernet_keys'][
@ -108,7 +108,11 @@ def after_request_func(response):
for key in session_list:
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)
@ -122,15 +126,17 @@ def unknown_page(e):
def index():
# Reset keys
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'] = ''
return render_template('error.html', error_message=error_message)
return render_template('index.html',
languages=app.config['LANGUAGES'],
countries=app.config['COUNTRIES'],
config=g.user_config,
error_message=error_message,
tor_available=int(os.environ.get('TOR_AVAILABLE')),
version_number=app.config['VERSION_NUMBER'])
@ -286,7 +292,9 @@ def url():
if len(q) > 0 and 'http' in q:
return redirect(q)
else:
return render_template('error.html', query=q)
return render_template(
'error.html',
error_message='Unable to resolve query: ' + q)
@app.route('/imgres')

11
app/static/js/header.js Normal file
View File

@ -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();
}
});
});

View File

@ -28,7 +28,7 @@ const checkForTracking = () => {
/^[0-9]{15}$/
]
}
}
};
// Creates a link to a UPS/USPS/FedEx tracking page
const createTrackingLink = href => {
@ -37,7 +37,7 @@ const checkForTracking = () => {
link.innerHTML = "View Tracking Info";
link.href = href;
mainDiv.prepend(link);
}
};
// Compares the query against a set of regex patterns
// for tracking numbers
@ -48,12 +48,12 @@ const checkForTracking = () => {
return true;
}
});
}
};
for (const key of Object.keys(matchTracking)) {
compareQuery(matchTracking[key]);
}
}
};
document.addEventListener("DOMContentLoaded", function() {
checkForTracking();

View File

@ -1,6 +1,6 @@
<h1>Error</h1>
<hr>
<p>
Error parsing "{{ query }}"
Error: "{{ error_message|safe }}"
</p>
<a href="/">Return Home</a>

View File

@ -48,14 +48,4 @@
</header>
{% endif %}
<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>
<script type="text/javascript" src="static/js/header.js"></script>

View File

@ -36,12 +36,6 @@
<title>Whoogle Search</title>
</head>
<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">
<img class="logo" src="static/img/logo.png">
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">