Bots that prefetch should not destroy the secret (#100)

* Create preview view, remove sneaky-user-agents logic

* unit tests

* rename openSecret to viewSecret

* code clean-up and style

* rename view secret to reveal secret

* update authors list

* bump version to 1.5.0
This commit is contained in:
Lauri Lubi 2019-03-05 16:47:07 +01:00 committed by Jon Parise
parent d4c96cf58a
commit 1ac262e34e
9 changed files with 59 additions and 39 deletions

View file

@ -2,4 +2,5 @@
files = setup.py
commit = True
tag = True
current_version = 0.1.0
current_version = 1.5.0

View file

@ -19,3 +19,4 @@ Thanks a lot for the contributions of:
* Donny Winston
* James Barclay
* Thomas Decaux
* Lauri Lubi

View file

@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.4.0
current_version = 1.5.0
commit = True
tag = True
files = setup.py snappass/__init__.py

View file

@ -2,7 +2,7 @@ from setuptools import setup
setup(
name='snappass',
version='1.4.1',
version='1.5.0',
description="It's like SnapChat... for Passwords.",
long_description=(open('README.rst').read() + '\n\n' +
open('AUTHORS.rst').read()),

View file

@ -1 +1 @@
__version__ = '1.4.1'
__version__ = '1.5.0'

View file

@ -11,10 +11,6 @@ from redis.exceptions import ConnectionError
from werkzeug.urls import url_quote_plus
from werkzeug.urls import url_unquote_plus
SNEAKY_USER_AGENTS = ('Slackbot', 'facebookexternalhit', 'Twitterbot',
'Facebot', 'WhatsApp', 'SkypeUriPreview', 'Iframely')
SNEAKY_USER_AGENTS_RE = re.compile('|'.join(SNEAKY_USER_AGENTS))
NO_SSL = os.environ.get('NO_SSL', False)
TOKEN_SEPARATOR = '~'
@ -127,6 +123,11 @@ def get_password(token):
return password.decode('utf-8')
@check_redis_alive
def password_exists(token):
storage_key, decryption_key = parse_token(token)
return redis_client.exists(storage_key)
def empty(value):
if not value:
return True
@ -150,14 +151,6 @@ def clean_input():
return TIME_CONVERSION[time_period], request.form['password']
def request_is_valid(request):
"""
Ensure the request validates the following:
- not made by some specific User-Agents (to avoid chat's preview feature issue)
"""
return not SNEAKY_USER_AGENTS_RE.search(request.headers.get('User-Agent', ''))
@app.route('/', methods=['GET'])
def index():
return render_template('set_password.html')
@ -177,9 +170,16 @@ def handle_password():
@app.route('/<password_key>', methods=['GET'])
def show_password(password_key):
if not request_is_valid(request):
def preview_password(password_key):
password_key = url_unquote_plus(password_key)
if not password_exists(password_key):
abort(404)
return render_template('preview.html')
@app.route('/<password_key>', methods=['POST'])
def show_password(password_key):
password_key = url_unquote_plus(password_key)
password = get_password(password_key)
if not password:

View file

@ -0,0 +1,10 @@
(function () {
$('#revealSecret').click(function () {
var form = $('<form/>')
.attr('id', 'revealSecretForm')
.attr('method', 'post');
form.appendTo($('body'));
form.submit();
});
})();

View file

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block content %}
<div class="container">
<section>
<div class="page-header">
<h1>Secret</h1>
</div>
<p class="lead">You can only reveal the secret once!</p>
<div class="row">
<div class="col-sm-6 margin-bottom-10">
<button id="revealSecret" type="button" class="btn-lg btn-primary">Reveal secret</button>
</div>
</div>
</section>
</div>
{% endblock %}
{% block js %}
<script src="{{ config.STATIC_URL }}/clipboardjs/clipboard.min.js"></script>
<script src="{{ config.STATIC_URL }}/snappass/scripts/clipboard_button.js"></script>
<script src="{{ config.STATIC_URL }}/snappass/scripts/preview.js"></script>
{% endblock %}

View file

@ -106,32 +106,17 @@ class SnapPassRoutesTestCase(TestCase):
snappass.app.config['TESTING'] = True
self.app = snappass.app.test_client()
def test_show_password(self):
def test_preview_password(self):
password = "I like novelty kitten statues!"
key = snappass.set_password(password, 30)
rv = self.app.get('/{0}'.format(key))
self.assertIn(password, rv.get_data(as_text=True))
self.assertNotIn(password, rv.get_data(as_text=True))
def test_bots_denial(self):
"""
Main known bots User-Agent should be denied access
"""
password = "Bots can't access this"
def test_show_password(self):
password = "I like novelty kitten statues!"
key = snappass.set_password(password, 30)
a_few_sneaky_bots = [
"Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)",
"facebookexternalhit/1.1",
"Facebot/1.0",
"Twitterbot/1.0",
"_WhatsApp/2.12.81 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00",
"WhatsApp/2.16.6/i",
"SkypeUriPreview Preview/0.5",
"Iframely/0.8.5 (+http://iframely.com/;)",
]
for ua in a_few_sneaky_bots:
rv = self.app.get('/{0}'.format(key), headers={'User-Agent': ua})
self.assertEqual(404, rv.status_code)
rv = self.app.post('/{0}'.format(key))
self.assertIn(password, rv.get_data(as_text=True))
if __name__ == '__main__':