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:
parent
d4c96cf58a
commit
1ac262e34e
9 changed files with 59 additions and 39 deletions
|
@ -2,4 +2,5 @@
|
|||
files = setup.py
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 0.1.0
|
||||
current_version = 1.5.0
|
||||
|
||||
|
|
|
@ -19,3 +19,4 @@ Thanks a lot for the contributions of:
|
|||
* Donny Winston
|
||||
* James Barclay
|
||||
* Thomas Decaux
|
||||
* Lauri Lubi
|
||||
|
|
|
@ -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
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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()),
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = '1.4.1'
|
||||
__version__ = '1.5.0'
|
||||
|
|
|
@ -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:
|
||||
|
|
10
snappass/static/snappass/scripts/preview.js
Normal file
10
snappass/static/snappass/scripts/preview.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
(function () {
|
||||
|
||||
$('#revealSecret').click(function () {
|
||||
var form = $('<form/>')
|
||||
.attr('id', 'revealSecretForm')
|
||||
.attr('method', 'post');
|
||||
form.appendTo($('body'));
|
||||
form.submit();
|
||||
});
|
||||
})();
|
23
snappass/templates/preview.html
Normal file
23
snappass/templates/preview.html
Normal 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 %}
|
27
tests.py
27
tests.py
|
@ -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__':
|
||||
|
|
Loading…
Reference in a new issue