Resolve "Use a HMAC instead of a hash"

This commit is contained in:
jfriedli 2020-04-26 09:50:14 -07:00
parent e1bac8b6a7
commit c301e472bd
9 changed files with 148 additions and 91 deletions

View file

@ -6,12 +6,13 @@ import io
import os
from unittest.mock import patch
from flask_testing import TestCase
import main
class Mat2WebTestCase(unittest.TestCase):
def setUp(self):
class Mat2WebTestCase(TestCase):
def create_app(self):
os.environ.setdefault('MAT2_ALLOW_ORIGIN_WHITELIST', 'origin1.gnu origin2.gnu')
self.upload_folder = tempfile.mkdtemp()
app = main.create_app(
@ -20,45 +21,45 @@ class Mat2WebTestCase(unittest.TestCase):
'UPLOAD_FOLDER': self.upload_folder
}
)
self.app = app.test_client()
return app
def tearDown(self):
shutil.rmtree(self.upload_folder)
def test_get_root(self):
rv = self.app.get('/')
rv = self.client.get('/')
self.assertIn(b'mat2-web', rv.data)
def test_check_mimetypes(self):
rv = self.app.get('/')
rv = self.client.get('/')
self.assertIn(b'.torrent', rv.data)
self.assertIn(b'.ods', rv.data)
def test_get_download_dangerous_file(self):
rv = self.app.get('/download/1337/\..\filename')
rv = self.client.get('/download/1337/aabb/\..\filename')
self.assertEqual(rv.status_code, 302)
def test_get_download_without_key_file(self):
rv = self.app.get('/download/non_existant')
rv = self.client.get('/download/non_existant')
self.assertEqual(rv.status_code, 404)
def test_get_download_nonexistant_file(self):
rv = self.app.get('/download/1337/non_existant')
rv = self.client.get('/download/1337/aabb/non_existant')
self.assertEqual(rv.status_code, 302)
def test_get_upload_without_file(self):
rv = self.app.post('/')
rv = self.client.post('/')
self.assertEqual(rv.status_code, 302)
def test_get_upload_empty_file(self):
rv = self.app.post('/',
rv = self.client.post('/',
data=dict(
file=(io.BytesIO(b""), 'test.pdf'),
), follow_redirects=False)
self.assertEqual(rv.status_code, 302)
def test_get_upload_empty_file_redir(self):
rv = self.app.post('/',
rv = self.client.post('/',
data=dict(
file=(io.BytesIO(b""), 'test.pdf'),
), follow_redirects=True)
@ -67,7 +68,7 @@ class Mat2WebTestCase(unittest.TestCase):
self.assertEqual(rv.status_code, 200)
def test_get_upload_no_selected_file(self):
rv = self.app.post('/',
rv = self.client.post('/',
data=dict(
file=(io.BytesIO(b""), ''),
), follow_redirects=True)
@ -86,7 +87,7 @@ class Mat2WebTestCase(unittest.TestCase):
'AAAAAAAAAAApIFnAAAAdGVzdC5qc29uVVQNAAfomo9d6JqPXeiaj111eAsAAQTpAwAABOkDAAB'
'QSwUGAAAAAAIAAgC8AAAAwAAAAAAA'
)
rv = self.app.post('/',
rv = self.client.post('/',
data=dict(
file=(io.BytesIO(zip_file_bytes), 'test.zip'),
), follow_redirects=True)
@ -94,7 +95,7 @@ class Mat2WebTestCase(unittest.TestCase):
self.assertEqual(rv.status_code, 200)
def test_get_upload_no_file_name(self):
rv = self.app.post('/',
rv = self.client.post('/',
data=dict(
file=(io.BytesIO(b"aaa")),
), follow_redirects=True)
@ -102,30 +103,51 @@ class Mat2WebTestCase(unittest.TestCase):
self.assertEqual(rv.status_code, 200)
def test_get_upload_harmless_file(self):
rv = self.app.post('/',
data=dict(
file=(io.BytesIO(b"Some text"), 'test.txt'),
), follow_redirects=True)
self.assertIn(b'/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt', rv.data)
rv = self.client.post(
'/',
data=dict(
file=(io.BytesIO(b"Some text"), 'test.txt'),
),
follow_redirects=True
)
download_uri = self.get_context_variable('download_uri')
self.assertIn('/test.cleaned.txt', download_uri)
self.assertEqual(rv.status_code, 200)
self.assertNotIn('Access-Control-Allow-Origin', rv.headers)
rv = self.app.get('/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt')
rv = self.client.get(download_uri)
self.assertEqual(rv.status_code, 200)
rv = self.app.get('/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt')
rv = self.client.get(download_uri)
self.assertEqual(rv.status_code, 302)
def test_upload_wrong_hash(self):
rv = self.app.post('/',
data=dict(
file=(io.BytesIO(b"Some text"), 'test.txt'),
), follow_redirects=True)
self.assertIn(b'/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt',
rv.data)
def test_upload_wrong_hash_or_secret(self):
rv = self.client.post(
'/',
data=dict(
file=(io.BytesIO(b"Some text"), 'test.txt'),
),
follow_redirects=True
)
download_uri = self.get_context_variable('download_uri')
self.assertIn('/test.cleaned.txt', download_uri)
self.assertIn('/download', download_uri)
self.assertEqual(rv.status_code, 200)
rv = self.app.get('/download/70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt')
uri_parts = download_uri.split("/")
self.assertEqual(len(uri_parts[2]), len(uri_parts[3]))
self.assertEqual(64, len(uri_parts[2]))
key_uri_parts = uri_parts
key_uri_parts[2] = '70623619c'
rv = self.client.get("/".join(key_uri_parts))
self.assertEqual(rv.status_code, 302)
key_uri_parts = uri_parts
key_uri_parts[3] = '70623619c'
rv = self.client.get("/".join(key_uri_parts))
self.assertEqual(rv.status_code, 302)
@patch('matweb.file_removal_scheduler.random.randint')
@ -140,19 +162,18 @@ class Mat2WebTestCase(unittest.TestCase):
)
app = app.test_client()
request = self.app.post('/',
request = self.client.post('/',
data=dict(
file=(io.BytesIO(b"Some text"), 'test.txt'),
), follow_redirects=True)
self.assertEqual(request.status_code, 200)
request = app.get(
b'/download/4c2e9e6da31a64c70623619c449a040968cdbea85945bf384fa30ed2d5d24fa3/test.cleaned.txt'
)
request = app.get(self.get_context_variable('download_uri'))
self.assertEqual(302, request.status_code)
os.environ['MAT2_MAX_FILE_AGE_FOR_REMOVAL'] = '9999'
def test_info_page(self):
rv = self.app.get('/info')
rv = self.client.get('/info')
self.assertIn(b'What are metadata?', rv.data)
self.assertIn(b'.asc', rv.data)
self.assertIn(b'.mp2', rv.data)

View file

@ -30,33 +30,26 @@ class Mat2APITestCase(unittest.TestCase):
del os.environ['MAT2_ALLOW_ORIGIN_WHITELIST']
def test_api_upload_valid(self):
request = self.app.post('/api/upload',
data='{"file_name": "test_name.jpg", '
'"file": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAf'
'FcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="}',
headers={'content-type': 'application/json'}
)
request = self.app.post(
'/api/upload',
data='{"file_name": "test_name.jpg", '
'"file": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAf'
'FcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="}',
headers={'content-type': 'application/json'}
)
self.assertEqual(request.headers['Content-Type'], 'application/json')
self.assertEqual(request.headers['Access-Control-Allow-Origin'], 'origin1.gnu')
self.assertEqual(request.status_code, 200)
data = request.get_json()
expected = {
'output_filename': 'test_name.cleaned.jpg',
'mime': 'image/jpeg',
'key': '81a541f9ebc0233d419d25ed39908b16f82be26a783f32d56c381559e84e6161',
'meta': {
'BitDepth': 8,
'ColorType': 'RGB with Alpha',
'Compression': 'Deflate/Inflate',
'Filter': 'Adaptive',
'Interlace': 'Noninterlaced'
},
'meta_after': {},
'download_link': 'http://localhost/api/download/'
'81a541f9ebc0233d419d25ed39908b16f82be26a783f32d56c381559e84e6161/test_name.cleaned.jpg'
}
self.assertEqual(data, expected)
self.assertEqual(data['output_filename'], 'test_name.cleaned.jpg')
self.assertEqual(data['output_filename'], 'test_name.cleaned.jpg')
self.assertEqual(data['mime'], 'image/jpeg')
self.assertEqual(len(data['secret']), 64)
self.assertEqual(len(data['key']), 64)
self.assertNotEqual(data['key'], data['secret'])
self.assertTrue('http://localhost/api/download/' in data['download_link'])
self.assertTrue('test_name.cleaned.jpg' in data['download_link'])
def test_api_upload_missing_params(self):
request = self.app.post('/api/upload',
@ -141,7 +134,6 @@ class Mat2APITestCase(unittest.TestCase):
error = request.get_json()['message']
self.assertEqual(error, 'Unable to clean application/zip')
def test_api_download(self):
request = self.app.post('/api/upload',
data='{"file_name": "test_name.jpg", '
@ -152,25 +144,36 @@ class Mat2APITestCase(unittest.TestCase):
self.assertEqual(request.status_code, 200)
data = request.get_json()
request = self.app.get('http://localhost/api/download/'
request = self.app.get('http://localhost/api/download/161/'
'81a541f9ebc0233d419d25ed39908b16f82be26a783f32d56c381559e84e6161/test name.cleaned.jpg')
self.assertEqual(request.status_code, 400)
error = request.get_json()['message']
self.assertEqual(error, 'Insecure filename')
request = self.app.get('http://localhost/api/download/'
'81a541f9ebc0233d419d25ed39908b16f82be26a783f32d56c381559e84e6161/'
'wrong_file_name.jpg')
request = self.app.get(data['download_link'].replace('test_name', 'wrong_test'))
self.assertEqual(request.status_code, 404)
error = request.get_json()['message']
self.assertEqual(error, 'File not found')
request = self.app.get('http://localhost/api/download/81a541f9e/test_name.cleaned.jpg')
uri_parts = data['download_link'].split("/")
self.assertEqual(len(uri_parts[5]), len(uri_parts[6]))
self.assertEqual(64, len(uri_parts[5]))
key_uri_parts = uri_parts
key_uri_parts[5] = '70623619c'
request = self.app.get("/".join(key_uri_parts))
self.assertEqual(request.status_code, 400)
error = request.get_json()['message']
self.assertEqual(error, 'The file hash does not match')
key_uri_parts = uri_parts
key_uri_parts[6] = '70623619c'
request = self.app.get("/".join(key_uri_parts))
self.assertEqual(request.status_code, 400)
error = request.get_json()['message']
self.assertEqual(error, 'The file hash does not match')
request = self.app.head(data['download_link'])
self.assertEqual(request.status_code, 200)
self.assertEqual(request.headers['Content-Length'], '633')
@ -205,11 +208,13 @@ class Mat2APITestCase(unittest.TestCase):
u'download_list': [
{
u'file_name': upload_one['output_filename'],
u'key': upload_one['key']
u'key': upload_one['key'],
u'secret': upload_one['secret']
},
{
u'file_name': upload_two['output_filename'],
u'key': upload_two['key']
u'key': upload_two['key'],
u'secret': upload_two['secret']
}
]
}
@ -261,7 +266,8 @@ class Mat2APITestCase(unittest.TestCase):
u'download_list': [
{
u'file_name': 'invalid_file_name',
u'key': 'invalid_key'
u'key': 'invalid_key',
u'secret': 'invalid_secret'
}
]
}
@ -348,11 +354,13 @@ class Mat2APITestCase(unittest.TestCase):
u'download_list': [
{
u'file_name': 'invalid_file_name1',
u'key': 'invalid_key1'
u'key': 'invalid_key1',
u'secret': 'invalid_secret1'
},
{
u'file_name': 'invalid_file_name2',
u'key': 'invalid_key2'
u'key': 'invalid_key2',
u'secret': 'invalid_secret2'
}
]
}