Skip to content

Commit 3b46d2a

Browse files
committed
feat(security): add ClamAV integration
This adds django_clamd for AV scanning capabilites with ClamAV. The scanning can be enabled through the settings.
1 parent b0d4b5a commit 3b46d2a

File tree

8 files changed

+247
-195
lines changed

8 files changed

+247
-195
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ A list of configuration options which you need
111111
The development setup features a minio service, implementing the S3 protocol.
112112
To use SSE-C in development make sure to generate a certificate for the minio container and set `ALEXANDRIA_S3_VERIFY` to `false`.
113113

114+
- ClamAV
115+
- `ALEXANDRIA_CLAMD_ENABLED`: Set this to `True` to enable ClamAV (virus scanner).
116+
- `ALEXANDRIA_CLAMD_SOCKET`: ClamAV socket
117+
- `ALEXANDRIA_CLAMD_USE_TCP`: Use TCP to connect to ClamAV service
118+
- `ALEXANDRIA_CLAMD_TCP_SOCKET`: ClamAV service socket
119+
- `ALEXANDRIA_CLAMD_TCP_ADDR`: ClamAV service address
120+
114121
For development, you can also set the following environemnt variables to help you:
115122

116123
- `ALEXANDRIA_DEV_AUTH_BACKEND`: Set this to "true" to enable a fake auth backend that simulates an authenticated user. Requires `DEBUG` to be set to `True` as well.

alexandria/conftest.py

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ def _make_clean_media_dir(settings):
4444
shutil.rmtree(test_media_root)
4545

4646

47+
@pytest.fixture(autouse=True)
48+
def mock_clamd(mocker):
49+
mocker.patch("django_clamd.validators.validate_file_infection", return_value=None)
50+
51+
4752
@pytest.fixture
4853
def admin_groups():
4954
return ["admin"]

alexandria/core/serializers.py

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.contrib.auth.models import AnonymousUser
33
from django.template.defaultfilters import slugify
44
from django.utils import translation
5+
from django_clamd.validators import validate_file_infection
56
from generic_permissions.validation import ValidatorMixin
67
from generic_permissions.visibilities import (
78
VisibilityResourceRelatedField,
@@ -231,6 +232,10 @@ def is_valid(self, *args, raise_exception=False, **kwargs):
231232
self._prepare_multipart()
232233
return super().is_valid(*args, raise_exception=raise_exception, **kwargs)
233234

235+
def validate_content(self, value):
236+
validate_file_infection(value)
237+
return value
238+
234239
class Meta:
235240
model = models.File
236241
fields = BaseSerializer.Meta.fields + (

alexandria/settings/alexandria.py

+7
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,10 @@ def default(default_dev=env.NOTSET, default_prod=env.NOTSET):
160160

161161
# Checksums
162162
ALEXANDRIA_ENABLE_CHECKSUM = env.bool("ALEXANDRIA_ENABLE_CHECKSUM", default=True)
163+
164+
# Clamav service
165+
CLAMD_SOCKET = env.str("ALEXANDRIA_CLAMD_SOCKET", default="/var/run/clamav/clamd.ctl")
166+
CLAMD_USE_TCP = env.bool("ALEXANDRIA_CLAMD_USE_TCP", default=True)
167+
CLAMD_TCP_SOCKET = env.str("ALEXANDRIA_CLAMD_TCP_SOCKET", default=3310)
168+
CLAMD_TCP_ADDR = env.str("ALEXANDRIA_CLAMD_TCP_ADDR", default="localhost")
169+
CLAMD_ENABLED = env.bool("ALEXANDRIA_CLAMD_ENABLED", default=False)

alexandria/settings/django.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"django.contrib.postgres",
1717
"localized_fields",
1818
"psqlextra",
19+
"django_clamd",
1920
"django.contrib.contenttypes",
2021
"django.contrib.auth",
2122
"alexandria.core.apps.DefaultConfig",

docker-compose.override.yml

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ services:
3838
- DEBUG=true
3939
- ALEXANDRIA_DEV_AUTH_BACKEND=true
4040
- ALEXANDRIA_ALLOW_ANONYMOUS_WRITE=true
41+
- ALEXANDRIA_CLAMD_ENABLED=false
4142

4243
minio:
4344
environment:

poetry.lock

+214-189
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+7-6
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,25 @@ exclude = ["alexandria/conftest.py", "alexandria/**/tests"]
1717
packages = [{ include = "alexandria" }]
1818

1919
[tool.poetry.dependencies]
20-
python = ">=3.8.1,<4.0"
20+
boto3 = { extras = ["s3"], version = "^1.29.7" }
2121
django = "~3.2"
22+
django-clamd = "^0.4.0"
2223
django-environ = ">=0.9.0,<0.12.0"
2324
django-filter = ">=22.1,<24.0"
25+
django-generic-api-permissions = "^0.4.2"
2426
django-localized-fields = "^6.6"
27+
django-storages = { extras = ["s3"], version = "^1.14.2" }
2528
djangorestframework = "^3.13.0"
2629
djangorestframework-jsonapi = ">=5.0.0,<7.0.0"
2730
minio = "^7.1.14"
2831
mozilla-django-oidc = ">=2,<5"
2932
preview-generator = "^0.29"
33+
psutil = "^5.9.8"
3034
psycopg2-binary = "~2.9"
35+
python = ">=3.8.1,<4.0"
3136
requests = "^2.31.0"
32-
uwsgi = "^2.0.20"
33-
django-generic-api-permissions = "^0.4.2"
34-
django-storages = { extras = ['s3'], version = "^1.14.2" }
35-
boto3 = "^1.29.7"
3637
tqdm = "^4.66.1"
38+
uwsgi = "^2.0.20"
3739

3840
[tool.poetry.group.dev.dependencies]
3941
black = "24.2.0"
@@ -64,7 +66,6 @@ python-semantic-release = "7.33.3"
6466
requests-mock = "1.11.0"
6567
syrupy = "4.6.1"
6668
werkzeug = "3.0.1"
67-
psutil = "^5.9.8"
6869

6970
[tool.isort]
7071
skip = ["migrations"]

0 commit comments

Comments
 (0)