summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--main.py24
-rw-r--r--templates/download.html2
-rw-r--r--tests.py15
3 files changed, 30 insertions, 11 deletions
diff --git a/main.py b/main.py
index 82a6442..59c3791 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,6 @@
1import os 1import os
2import hashlib
3import hmac
2 4
3from libmat2 import parser_factory 5from libmat2 import parser_factory
4 6
@@ -13,13 +15,20 @@ app.config['SECRET_KEY'] = os.urandom(32)
13app.config['UPLOAD_FOLDER'] = './uploads/' 15app.config['UPLOAD_FOLDER'] = './uploads/'
14app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB 16app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB
15 17
16mimetypes = 'image/jpeg, image/png' 18def __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>')
20def download_file(filename:str): 30def 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
diff --git a/tests.py b/tests.py
index 8ce7d7e..0289755 100644
--- a/tests.py
+++ b/tests.py
@@ -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