ScanCan is a containerized FastAPI service that exposes ClamAV scanning operations over HTTP.
- Health endpoint for ClamAV status
- File path scanning
- URL content scanning
- Streaming upload scanning
- License endpoint
- OpenAPI docs at
/docs
- Python 3.8-3.10
- Docker + Docker Compose (for container workflow)
src/main.py: FastAPI app and endpointssrc/clamav.py: ClamAV client abstractionsrc/models.py: Pydantic modelssrc/logger.py: logger wrappersrc/utils.py: utility helperstests/: pytest suitedocker-compose.yml: local multi-container runtimeMakefile: build, run, lint, type-check, and test commands
Build images:
make buildStart services:
make upStop services:
make downRestart with rebuild:
make restartAPI will be available at:
http://localhost:8080- Docs:
http://localhost:8080/docs
Create and sync environment with uv:
uv sync -p 3.9
source .venv/bin/activateStart API:
uvicorn src.main:app --host 0.0.0.0 --port 8080Note: Local runtime still needs a reachable ClamAV daemon (socket or network) configured via environment variables below.
Configured in src/config.py via environment variables:
CLAMD_CONN:netorsocket(default:net)CLAMD_HOST: host for network mode (default:lab3.local)CLAMD_PORT: port for network mode (default:3310)CLAMD_SOCKET: socket path for socket mode (default:/tmp/clamd.socket)UPLOAD_SIZE_LIMIT: max upload/URL payload bytes (default:104857600)USE_AUTHENTICATION:true/false(default:false)LOG_LEVEL: logging level (default:INFO)LOG_FORMAT: logging format string
ScanCan supports a pluggable authentication module loaded from:
addon/authentication.py
The path is hard-coded in the application and resolved relative to the current working directory.
The addon module must define this function:
def authenticate(token: str):
# Return True to allow, False to reject.
# Any truthy non-False value is treated as allowed.
return token == "my-secret-token"Behavior:
- If
USE_AUTHENTICATION=false, middleware auth is disabled. - If
USE_AUTHENTICATION=trueandaddon/authentication.pyexists, ScanCan callsauthenticate(token)from that module for protected requests. - If the module is not present, ScanCan defaults to allow (request is treated as authenticated).
- If the module is present but does not expose a callable
authenticate(token), auth checks raise a runtime error.
To enable auth middleware:
export USE_AUTHENTICATION=trueCommands come from Makefile:
Run full checks (pylint + mypy + pytest):
make testRun only pylint checks:
make pylintRun only type checks:
make mypyRun only tests:
make pytestRun a single test module directly:
python -m pytest tests/test_main.py -vGET /healthPOST /scanpath/{path}GET /scanurl/?url=...POST /contscan/{path}POST /scanfileGET /license
See interactive docs at http://localhost:8080/docs for request/response schemas.
- The previous benchmarking section has been removed.
- The README now reflects the current test suite and Makefile-driven workflow.
