diff options
| -rw-r--r-- | main.py | 24 | ||||
| -rw-r--r-- | templates/download.html | 2 | ||||
| -rw-r--r-- | tests.py | 15 |
3 files changed, 30 insertions, 11 deletions
| @@ -1,4 +1,6 @@ | |||
| 1 | import os | 1 | import os |
| 2 | import hashlib | ||
| 3 | import hmac | ||
| 2 | 4 | ||
| 3 | from libmat2 import parser_factory | 5 | from libmat2 import parser_factory |
| 4 | 6 | ||
| @@ -13,13 +15,20 @@ app.config['SECRET_KEY'] = os.urandom(32) | |||
| 13 | app.config['UPLOAD_FOLDER'] = './uploads/' | 15 | app.config['UPLOAD_FOLDER'] = './uploads/' |
| 14 | app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB | 16 | app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB |
| 15 | 17 | ||
| 16 | mimetypes = 'image/jpeg, image/png' | 18 | def __hash_file(filepath: str) -> str: |
| 19 | sha256 = hashlib.sha256() | ||
| 20 | with open(filepath, 'rb') as f: | ||
| 21 | while True: | ||
| 22 | data = f.read(65536) # read the file by chunk of 64k | ||
| 23 | if not data: | ||
| 24 | break | ||
| 25 | sha256.update(data) | ||
| 26 | return sha256.hexdigest() | ||
| 17 | 27 | ||
| 18 | 28 | ||
| 19 | @app.route('/download/<string:filename>') | 29 | @app.route('/download/<string:key>/<string:filename>') |
| 20 | def download_file(filename:str): | 30 | def download_file(key:str, filename:str): |
| 21 | if filename != secure_filename(filename): | 31 | if filename != secure_filename(filename): |
| 22 | flash('naughty naughty') | ||
| 23 | return redirect(url_for('upload_file')) | 32 | return redirect(url_for('upload_file')) |
| 24 | 33 | ||
| 25 | filepath = secure_filename(filename) | 34 | filepath = secure_filename(filename) |
| @@ -27,6 +36,9 @@ def download_file(filename:str): | |||
| 27 | complete_path = os.path.join(app.config['UPLOAD_FOLDER'], filepath) | 36 | complete_path = os.path.join(app.config['UPLOAD_FOLDER'], filepath) |
| 28 | if not os.path.exists(complete_path): | 37 | if not os.path.exists(complete_path): |
| 29 | return redirect(url_for('upload_file')) | 38 | return redirect(url_for('upload_file')) |
| 39 | if hmac.compare_digest(__hash_file(complete_path), key) is False: | ||
| 40 | print('hash: %s, key: %s' % (__hash_file(complete_path), key)) | ||
| 41 | return redirect(url_for('upload_file')) | ||
| 30 | 42 | ||
| 31 | @after_this_request | 43 | @after_this_request |
| 32 | def remove_file(response): | 44 | def remove_file(response): |
| @@ -72,7 +84,9 @@ def upload_file(): | |||
| 72 | meta_after = parser.get_meta() | 84 | meta_after = parser.get_meta() |
| 73 | os.remove(filepath) | 85 | os.remove(filepath) |
| 74 | 86 | ||
| 75 | return render_template('download.html', mimetypes=mimetypes, meta=meta, filename=output_filename, meta_after=meta_after) | 87 | key = __hash_file(os.path.join(app.config['UPLOAD_FOLDER'], output_filename)) |
| 88 | |||
| 89 | return render_template('download.html', mimetypes=mimetypes, meta=meta, filename=output_filename, meta_after=meta_after, key=key) | ||
| 76 | 90 | ||
| 77 | return render_template('index.html', mimetypes=mimetypes) | 91 | return render_template('index.html', mimetypes=mimetypes) |
| 78 | 92 | ||
diff --git a/templates/download.html b/templates/download.html index cc7e150..c38b521 100644 --- a/templates/download.html +++ b/templates/download.html | |||
| @@ -14,7 +14,7 @@ mat2 <b>could not</b> remove all the metadata from your file, those are the rema | |||
| 14 | </ul> | 14 | </ul> |
| 15 | {%endif %} | 15 | {%endif %} |
| 16 | </p> | 16 | </p> |
| 17 | <a class="button button-primary" href='{{ url_for('download_file', filename=filename) }}'>⇩ Download cleaned file</a> | 17 | <a class="button button-primary" href='{{ url_for('download_file', key=key, filename=filename) }}'>⇩ Download cleaned file</a> |
| 18 | 18 | ||
| 19 | <hr/> | 19 | <hr/> |
| 20 | 20 | ||
| @@ -25,13 +25,18 @@ class FlaskrTestCase(unittest.TestCase): | |||
| 25 | self.assertIn(b'audio/x-flac', rv.data) | 25 | self.assertIn(b'audio/x-flac', rv.data) |
| 26 | 26 | ||
| 27 | def test_get_download_dangerous_file(self): | 27 | def test_get_download_dangerous_file(self): |
| 28 | rv = self.app.get('/download/\..\filename') | 28 | rv = self.app.get('/download/1337/\..\filename') |
| 29 | self.assertEqual(rv.status_code, 302) | 29 | self.assertEqual(rv.status_code, 302) |
| 30 | 30 | ||
| 31 | def test_get_download_nonexistant_file(self): | 31 | def test_get_download_without_key_file(self): |
| 32 | rv = self.app.get('/download/non_existant') | 32 | rv = self.app.get('/download/non_existant') |
| 33 | self.assertEqual(rv.status_code, 404) | ||
| 34 | |||
| 35 | def test_get_download_nonexistant_file(self): | ||
| 36 | rv = self.app.get('/download/1337/non_existant') | ||
| 33 | self.assertEqual(rv.status_code, 302) | 37 | self.assertEqual(rv.status_code, 302) |
| 34 | 38 | ||
| 39 | |||
| 35 | def test_get_upload_without_file(self): | 40 | def test_get_upload_without_file(self): |
| 36 | rv = self.app.post('/') | 41 | rv = self.app.post('/') |
| 37 | self.assertEqual(rv.status_code, 302) | 42 | self.assertEqual(rv.status_code, 302) |
| @@ -66,13 +71,13 @@ class FlaskrTestCase(unittest.TestCase): | |||
| 66 | data=dict( | 71 | data=dict( |
| 67 | file=(io.BytesIO(b"Some text"), 'test.txt'), | 72 | file=(io.BytesIO(b"Some text"), 'test.txt'), |
| 68 | ), follow_redirects=True) | 73 | ), follow_redirects=True) |
| 69 | self.assertIn(b'/download/test.cleaned.txt', rv.data) | 74 | self.assertIn(b'/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt', rv.data) |
| 70 | self.assertEqual(rv.status_code, 200) | 75 | self.assertEqual(rv.status_code, 200) |
| 71 | 76 | ||
| 72 | rv = self.app.get('/download/test.cleaned.txt') | 77 | rv = self.app.get('/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt') |
| 73 | self.assertEqual(rv.status_code, 200) | 78 | self.assertEqual(rv.status_code, 200) |
| 74 | 79 | ||
| 75 | rv = self.app.get('/download/test.cleaned.txt') | 80 | rv = self.app.get('/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt') |
| 76 | self.assertEqual(rv.status_code, 302) | 81 | self.assertEqual(rv.status_code, 302) |
| 77 | 82 | ||
| 78 | 83 | ||
