From 47cacc50d205f1006c006cd974324897bc5a995e Mon Sep 17 00:00:00 2001 From: Cizz22 Date: Tue, 3 Feb 2026 16:11:25 +0700 Subject: [PATCH] feat: integrate ClamAV for virus scanning of uploaded files and update pagination schema alias. --- poetry.lock | 14 +++++++++++++- pyproject.toml | 1 + requirements.txt | 1 + src/aeros_project/service.py | 28 +++++++++++++++++++++++++++- src/config.py | 5 ++++- src/database/schema.py | 2 +- 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8bccc1a..187d26a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -393,6 +393,18 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] +[[package]] +name = "clamd" +version = "1.0.2" +description = "Clamd is a python interface to Clamd (Clamav daemon)." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "clamd-1.0.2-py2.py3-none-any.whl", hash = "sha256:5c32546b7d1eb00fd6be00a889d79e00fbf980ed082826ccfa369bce3dcff5e7"}, + {file = "clamd-1.0.2.tar.gz", hash = "sha256:d82a2fd814684a35a1b31feadafb2e69c8ebde9403613f6bdaa5d877c0f29560"}, +] + [[package]] name = "click" version = "8.1.7" @@ -2811,4 +2823,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = "^3.11" -content-hash = "9d879fd1a129aee9afcf93e7a1933a8d5b511a3bc3a09cb8b598a22835085716" +content-hash = "9d61d2415a02d2e9a0be0850394cd0d8dbf178f7a419e726cc9b3b1c37d8d4d1" diff --git a/pyproject.toml b/pyproject.toml index 8bc34b8..ef343a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dotenv = "^0.9.9" aiohttp = "^3.12.14" ijson = "^3.4.0" redis = "^7.1.0" +clamd = "^1.0.2" [build-system] diff --git a/requirements.txt b/requirements.txt index d51449d..bc37607 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1639,3 +1639,4 @@ yarl==1.20.1 ; python_version >= "3.11" and python_version < "4.0" \ --hash=sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce \ --hash=sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e \ --hash=sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5 +clamd==1.0.2 diff --git a/src/aeros_project/service.py b/src/aeros_project/service.py index b8e8063..e421c10 100644 --- a/src/aeros_project/service.py +++ b/src/aeros_project/service.py @@ -9,9 +9,11 @@ from sqlalchemy.orm import selectinload from src.aeros_equipment.service import save_default_equipment from src.aeros_simulation.service import save_default_simulation_node from src.auth.service import CurrentUser -from src.config import WINDOWS_AEROS_BASE_URL, AEROS_BASE_URL +from src.config import WINDOWS_AEROS_BASE_URL, AEROS_BASE_URL, CLAMAV_HOST, CLAMAV_PORT from src.database.core import DbSession from src.database.service import search_filter_sort_paginate +import clamd +import io from .model import AerosProject from .schema import AerosProjectInput @@ -49,6 +51,30 @@ async def import_aro_project(*, db_session: DbSession, aeros_project_in: AerosPr detail="File too large. Max size: 100Mb" ) + # ClamAV Scan + try: + cd = clamd.ClamdNetworkSocket(CLAMAV_HOST, CLAMAV_PORT) + scan_result = cd.instream(io.BytesIO(content)) + # Result format: {'stream': ('FOUND', 'Eicar-Test-Signature')} or {'stream': ('OK', None)} + if scan_result and scan_result.get('stream') and scan_result['stream'][0] == 'FOUND': + raise HTTPException( + status_code=400, + detail=f"Virus detected: {scan_result['stream'][1]}" + ) + except clamd.ConnectionError: + raise HTTPException( + status_code=500, + detail="Antivirus service unavailable" + ) + except HTTPException: + raise + except Exception as e: + print(f"ClamAV error: {e}") + raise HTTPException( + status_code=500, + detail=f"Antivirus check failed: {str(e)}" + ) + # Project name hardcode # project_name = "trialapi" diff --git a/src/config.py b/src/config.py index ae0dd8c..d9b5a5a 100644 --- a/src/config.py +++ b/src/config.py @@ -93,4 +93,7 @@ WINDOWS_AEROS_BASE_URL = config("WINDOWS_AEROS_BASE_URL", default="http://192.16 DEFAULT_PROJECT_NAME = config("DEFAULT_PROJECT_NAME", default="RBD TJB") TEMPORAL_URL = config("TEMPORAL_URL", default="http://192.168.1.86:7233") -RELIABILITY_SERVICE_API = config("RELIABILITY_SERVICE_API", default="http://192.168.1.82:8000/reliability") \ No newline at end of file +RELIABILITY_SERVICE_API = config("RELIABILITY_SERVICE_API", default="http://192.168.1.82:8000/reliability") + +CLAMAV_HOST = config("CLAMAV_HOST", default="192.168.1.82") +CLAMAV_PORT = config("CLAMAV_PORT", cast=int, default=3310) \ No newline at end of file diff --git a/src/database/schema.py b/src/database/schema.py index 67cb3c7..37f6671 100644 --- a/src/database/schema.py +++ b/src/database/schema.py @@ -8,7 +8,7 @@ class CommonParams(DefultBase): # This ensures no extra query params are allowed current_user: Optional[str] = Field(None, alias="currentUser") page: int = Field(1, gt=0, lt=2147483647) - items_per_page: int = Field(5, gt=-2, lt=2147483647) + items_per_page: int = Field(5, gt=-2, lt=2147483647, alias="itemsPerPage") query_str: Optional[str] = Field(None, alias="q") filter_spec: Optional[str] = Field(None, alias="filter") sort_by: List[str] = Field(default_factory=list, alias="sortBy[]")