Commit 96c7ddac authored by david's avatar david

improvements in io handling and input validation

parent da31752e
Pipeline #68 failed with stage
in 53 seconds
......@@ -30,6 +30,10 @@ SERVER_NAME = ''
FIEX_DATA_FOLDER = '/etc/fiex/data'
FIEX_DATA_FOLDER_FILE_PERM_DEFAULT = 755
FIEX_DATA_FOLDER_DIR_PERM_DEFAULT = 644
FIEX_VALIDATOR_REGEX_PATH = r"^([\w\s\d\.\/\\\-_]+)$"
FIEX_VALIDATOR_REGEX_TEXT = r"^([\w\s\d\.\/\\\-_]+)$"
FIEX_VALIDATOR_REGEX_TOKEN = r"^([\w\s\d]+)$"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
......
......@@ -37,6 +37,13 @@ class FileNotWritableException(FiexIOException):
pass
class DirectoryNotEmptyException(FiexIOException):
"""
Raised if a directory is not empty
"""
pass
class OperationNotPermittedException(FiexException):
"""
Raised if the operation is not permitted
......
......@@ -27,4 +27,3 @@ class ProgressBarFileUploadHandler(FileUploadHandler):
def file_complete(self, file_size):
raise NotImplementedError()
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generated by Django 2.1.3 on 2018-11-17 14:31
# Generated by Django 2.1.3 on 2018-11-25 19:15
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import fiexapp.models
import fiexapp.validators
class Migration(migrations.Migration):
......@@ -26,6 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
......@@ -33,17 +20,44 @@ class Migration(migrations.Migration):
name='Exchange',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.CharField(max_length=300, verbose_name='description')),
('creation_date', models.DateTimeField(verbose_name='date created')),
('token', models.CharField(default=fiexapp.models.gen_random_token, editable=False, max_length=100, unique=True, verbose_name='access token')),
('password', models.CharField(max_length=100, null=True, verbose_name='optional password')),
('description', models.CharField(max_length=300, validators=[fiexapp.validators.validate_text], verbose_name='description')),
('creation_date', models.DateTimeField(validators=[fiexapp.validators.validate_text], verbose_name='date created')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_by', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='File',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('path', models.CharField(max_length=200, verbose_name='path stored')),
('description', models.CharField(max_length=300, null=True, verbose_name='description of file')),
('Exchange', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fiexapp.Exchange')),
('path', models.CharField(max_length=500, unique=True, validators=[fiexapp.validators.validate_path])),
('comment', models.CharField(max_length=500, null=True, validators=[fiexapp.validators.validate_text])),
('expiration_date', models.DateField(null=True, verbose_name='Date when file should be deleted')),
('belongs_to_exchange', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fiexapp.Exchange')),
('created_by_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Permission',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True, verbose_name='name of the permission')),
('description', models.CharField(max_length=300, unique=True, verbose_name='description of the permission')),
],
),
migrations.CreateModel(
name='PermissionGranted',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_granted', models.DateField(null=True)),
('exchange', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fiexapp.Exchange')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='exchange',
name='permissions',
field=models.ManyToManyField(related_name='PermissionGranted', through='fiexapp.PermissionGranted', to=settings.AUTH_USER_MODEL),
),
]
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generated by Django 2.1.3 on 2018-11-20 20:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fiexapp', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='exchange',
name='password',
field=models.CharField(max_length=100, null=True, verbose_name='password required to access the exchange'),
),
migrations.AddField(
model_name='exchange',
name='token',
field=models.CharField(default='test', max_length=100, verbose_name='access token'),
preserve_default=False,
),
]
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generated by Django 2.1.3 on 2018-11-20 20:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fiexapp', '0002_auto_20181120_2053'),
]
operations = [
migrations.AlterField(
model_name='exchange',
name='token',
field=models.CharField(max_length=100, unique=True, verbose_name='access token'),
),
]
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generated by Django 2.1.3 on 2018-11-20 21:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fiexapp', '0003_auto_20181120_2059'),
]
operations = [
migrations.AlterField(
model_name='exchange',
name='token',
field=models.CharField(default='u9cbld5r30fz', editable=False, max_length=100, unique=True, verbose_name='access token'),
),
]
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generated by Django 2.1.3 on 2018-11-20 21:27
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('fiexapp', '0004_auto_20181120_2113'),
]
operations = [
migrations.AddField(
model_name='exchange',
name='created_by',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.AlterField(
model_name='exchange',
name='token',
field=models.CharField(default='2paxgmvrir4v', editable=False, max_length=100, unique=True, verbose_name='access token'),
),
]
# Generated by Django 2.1.3 on 2018-11-22 20:32
import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('fiexapp', '0005_auto_20181120_2127'),
]
operations = [
migrations.CreateModel(
name='Permission',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, verbose_name='name of the permission')),
('description', models.CharField(max_length=300, verbose_name='description of the permission')),
],
),
migrations.CreateModel(
name='PermissionGranted',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_granted', models.DateField(default=datetime.datetime(2018, 11, 22, 20, 32, 25, 56748, tzinfo=utc), null=True)),
],
),
migrations.AlterField(
model_name='exchange',
name='created_by',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_by', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='exchange',
name='password',
field=models.CharField(max_length=100, null=True, verbose_name='optional password'),
),
migrations.AlterField(
model_name='exchange',
name='token',
field=models.CharField(default='lvrtmvkodrjw', editable=False, max_length=100, unique=True, verbose_name='access token'),
),
migrations.AddField(
model_name='permissiongranted',
name='exchange',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fiexapp.Exchange'),
),
migrations.AddField(
model_name='permissiongranted',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='exchange',
name='permissions',
field=models.ManyToManyField(related_name='PermissionGranted', through='fiexapp.PermissionGranted', to=settings.AUTH_USER_MODEL),
),
]
# Copyright (c) David Leeuwestein 2018.
#
# Fiex project site: https://anonym-online.net/fiex
# Fiex projet mail: fiex@anonym-online.net
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
This diff is collapsed.
......@@ -18,19 +18,37 @@
import re
import os.path
from django.test import TestCase
from fiexapp.models import Exchange, gen_random_token, hash_password, Permission
from fiexapp.models import Exchange, gen_random_token, hash_password, Permission, File
from fiexapp.exceptions import OperationNotPermittedException
from django.utils import timezone
from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
# Create your tests here.
from django.core.exceptions import ValidationError
FILTER_FUZZING = [ \
""""';alert(String.fromCharCode(88,83,83))//';""", \
"""alert(String.fromCharCode(88, 83, 83)) // ";""", \
""""alert(String.fromCharCode(88,83,83))//";""", \
"""'';!--"<XSS>=&{()}""", \
""""< script / src = data:, alert() >""", \
"""<IMG SRC=javascript:alert("XSS")>""", \
"""<IMG SRC=&#0000106&#0000097&#>""", \
"""<IMG SRC="jav ascript:alert('XSS');">""", \
"""¼script¾alert(¢XSS¢)¼/script¾""", \
"""<!--#exec cmd="/bin/echo '<SCR'"--><!--#exec cmd="/bin/echo 'IPT SRC=http://ha.ckers.org/xss.js></SCRIPT>'"-->""", \
"""<A HREF="http://66.102.7.147/">XSS</A>""", \
"""<w contenteditable id=x onfocus=alert()>""", \
"""alert;pg("XSS")""", \
"""<svg/onload=%26%23097lert%26lpar;1337)>""", \
]
class ExchangeTestCase(TestCase):
def setUp(self):
Permission.create_necessary_permissions()
self.test_user = User.objects.create_superuser(username='test',email='test@example.com',password='verysecretpass')
self.test_user = User.objects.create_superuser(username='test', email='test@example.com',
password='verysecretpass')
self.exchange_description = "Django Test Instance"
self.exchange_creation_date = timezone.now()
self.test_user1 = User.objects.create(username="permtest1", email="permtest1@example.com",
......@@ -45,22 +63,28 @@ class ExchangeTestCase(TestCase):
def test_object_creation(self):
Exchange.objects.create(description=self.exchange_description, creation_date=self.exchange_creation_date,
created_by=self.test_user)
test_exchange_object = Exchange.objects.get(description=self.exchange_description,creation_date=self.exchange_creation_date)
self.assertEqual(test_exchange_object.description,self.exchange_description)
test_exchange_object = Exchange.objects.get(description=self.exchange_description,
creation_date=self.exchange_creation_date)
self.assertEqual(test_exchange_object.description, self.exchange_description)
self.assertEqual(test_exchange_object.creation_date, self.exchange_creation_date)
for fuzzer in FILTER_FUZZING:
with self.assertRaises(ValidationError):
Exchange(description=fuzzer,
creation_date=self.exchange_creation_date, created_by=self.test_user1).full_clean()
def test_gen_random_token(self):
letters_token = "abc123"
random_token_regex_test = re.compile("^([" + letters_token + "]+)$")
length_token = 10
random_token = gen_random_token(length_token,letters_token)
self.assertEqual(len(random_token),length_token)
self.assertTrue(re.match(random_token_regex_test,random_token))
random_token = gen_random_token(length_token, letters_token)
self.assertEqual(len(random_token), length_token)
self.assertTrue(re.match(random_token_regex_test, random_token))
def test_hash_password(self):
password = "very1secretpass!"
hash = hash_password(password)
self.assertTrue(check_password(password,hash))
self.assertTrue(check_password(password, hash))
def test_has_permission(self):
self.assertTrue(self.test_exchange1.has_permission(self.test_user1, Permission.READ_PERMISSION))
......@@ -74,7 +98,7 @@ class ExchangeTestCase(TestCase):
self.assertFalse(self.test_exchange1.has_permission(self.test_user2, Permission.DELETE_PERMISSION))
def test_grant_permission(self):
self.test_exchange1.change_permission(self.test_user2,Permission.READ_PERMISSION,True)
self.test_exchange1.change_permission(self.test_user2, Permission.READ_PERMISSION, True)
self.assertTrue(self.test_exchange1.has_permission(self.test_user2, Permission.READ_PERMISSION))
self.test_exchange1.change_permission(self.test_user2, Permission.WRITE_PERMISSION, True)
......@@ -87,20 +111,34 @@ class ExchangeTestCase(TestCase):
self.assertTrue(self.test_exchange1.has_permission(self.test_user2, Permission.DELETE_PERMISSION))
def test_exchange_data_dir_path(self):
exchange_data_dir_path = self.test_exchange1.exchange_data_dir_path()
exchange_data_dir_path = self.test_exchange1.data_store_dir_path()
self.assertTrue(os.path.commonprefix([settings.FIEX_DATA_FOLDER, exchange_data_dir_path]))
def test_path_from_exchange_data_dir_root(self):
self.assertEqual(self.test_exchange1.path_from_exchange_data_dir_root('test/a'),
os.path.join(self.test_exchange1.exchange_data_dir_path(), 'test/a'))
self.assertEqual(self.test_exchange1.path_from_exchange_data_dir_root('test' + os.sep + 'a'),
os.path.join(self.test_exchange1.data_store_dir_path(), 'test' + os.sep + 'a'))
with self.assertRaises(OperationNotPermittedException):
self.test_exchange1.path_from_exchange_data_dir_root('/test/a')
self.test_exchange1.path_from_exchange_data_dir_root('/test' + os.sep + 'a')
with self.assertRaises(OperationNotPermittedException):
self.test_exchange1.path_from_exchange_data_dir_root('test/../../../a')
self.test_exchange1.path_from_exchange_data_dir_root('test' + os.sep + '..' + os.sep + '..' + os.sep + 'a')
class PermissionTestCase(TestCase):
def setUp(self):
pass
class FileTest(TestCase):
def setUp(self):
self.test_user1 = User.objects.create(username="permtest1", email="permtest1@example.com",
password="verysecretpass")
self.test_exchange1 = Exchange.objects.create(description="permtest", creation_date=timezone.now(),
created_by=self.test_user1)
self.test_file1 = File.objects.create(path=('test' + os.sep + 'test.txt'), created_by_user=self.test_user1,
belongs_to_exchange=self.test_exchange1)
def test_object_destruction(self):
self.test_file1.delete()
import re
from django.conf import settings
from django.core.exceptions import ValidationError
def validate_text(value):
"""
validates a given text against a regex specified in django settings
:param value: the text to validate
:return: bool
:returns True if the validation has been successfull
:raise ValidationError
:raises if the validation fails
"""
if not isinstance(settings.FIEX_VALIDATOR_REGEX_TEXT, str):
raise ValidationError('regex not correctly set')
if not isinstance(value, str):
raise ValidationError('type not supported')
if re.match(settings.FIEX_VALIDATOR_REGEX_TEXT,value):
return True
else:
raise ValidationError('the given text isn\'t valid')
def validate_path(value):
"""
validates a given path against a regex specified in django settings
:param value: the path to validate
:return: bool
:returns True if the validation has been successfull
:raise ValidationError
:raises if the validation fails
"""
if not isinstance(settings.FIEX_VALIDATOR_REGEX_PATH, str):
raise ValidationError('regex not correctly set')
if not isinstance(value, str):
raise ValidationError('type not supported')
if re.match(settings.FIEX_VALIDATOR_REGEX_PATH, value):
return True
else:
raise ValidationError('the given path isn\t valid')
def validate_token(value):
"""
validates a given path value a regex specified in django settings
:param value: the value to validate
:return: bool
:returns True if the validation has been successfull
:raise ValidationError
:raises if the validation fails
"""
if not isinstance(settings.FIEX_VALIDATOR_REGEX_TOKEN, str):
raise ValidationError('regex not correctly set')
if not isinstance(value, str):
raise ValidationError('type not supported')
if re.match(settings.FIEX_VALIDATOR_REGEX_TOKEN, value):
return True
else:
raise ValidationError('the given token is not valid')
......@@ -14,10 +14,21 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
@login_required()
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
\ No newline at end of file
return HttpResponse("Hello, world. You're at the index.")
@login_required()
def upload(request):
return HttpResponse("Here you can upload a file")
@login_required()
def download(request):
return HttpResponse("Here you can download a file")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment