Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Clamav Laravel Package

sineflow/clamav

Laravel integration for ClamAV virus scanning. Scan uploaded files and local paths through clamd using a simple API or validation rule, with configurable Unix socket or TCP connections for reliable malware detection.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • sineflow/clamav is a Laravel-oriented wrapper around the ClamAV daemon (clamd). It fits the framework's idioms: a service provider, a publishable config file, a facade, and container-bound scanner instances. This is a low-friction fit for an existing Laravel codebase — no bespoke bootstrapping required.
  • The package is a thin client over a network/socket protocol, so it does not add a PHP extension or native compilation step. That keeps it portable across the standard Laravel deployment shapes (VM, container, serverless-with-caveats).
  • v2.1.0's Scanner::scanStream() is architecturally significant beyond a convenience method: it allows scanning a PHP stream resource rather than requiring a fully materialized file on disk. That unlocks three patterns we currently handle awkwardly — (a) large uploads where writing to a temp file is wasteful or disallowed, (b) objects streamed from S3/object storage before persistence, and (c) php://input-style request body inspection. clamd's INSTREAM command is the underlying primitive, so this is the correct abstraction level.
  • Caveat: it remains a synchronous, blocking call. The package does not introduce queues, async scanning, or result callbacks. Any "scan on upload" flow still consumes a PHP-FPM worker for the duration of the transfer + scan.

Integration feasibility

  • Integration is a standard composer require + vendor:publish + config step against the clamav connection settings (socket path or TCP host/port). The real work is infrastructure, not code: a reachable clamd with current signatures.
  • Local development and CI both need a ClamAV instance. A container sidecar/CI service is the pragmatic answer; without it the feature is untestable and will be mocked, which hides the failure modes that matter most.
  • Two integration details need verification against the actual source before we commit: the exception/return contract for scanStream() (does it mirror scanFile(), and does it distinguish "clean" from "scan error"?) and whether the stream is fully buffered or chunked to clamd.

Technical risk

  • Fail-open risk (highest). A scanner wrapper that returns a boolean or throws a generic exception invites try { scan() } catch { /* continue */ }. If clamd is unreachable, the safe default is to reject the upload. We must own that policy explicitly in our own adapter layer, not inherit the package's default.
  • Size limits. clamd.conf's StreamMaxLength (commonly 25 MB) applies to INSTREAM. scanStream() will surface a clamd error for anything larger unless the daemon is tuned. This is a silent mismatch waiting to happen between app-level upload limits and daemon-level limits.
  • Worker starvation. Streaming a 2 GB object through PHP to clamd holds a worker for minutes. Under concurrency this is a availability incident, not a performance nit. Needs a size cap, a timeout, and ideally a queue path.
  • Maintainer/bus factor. This is a small, single-maintainer OSS package. It is a security-adjacent dependency; we should be prepared to vendor/fork it. Version pinning and a review of the changelog before each bump are warranted — this release is additive and non-breaking, but that is not guaranteed going forward.
  • Support matrix. Confirm PHP and Laravel version constraints in composer.json against our current runtime before scheduling work.

Key questions

  1. What is our maximum permitted upload size, and what is StreamMaxLength on the target clamd?
  2. Does scanStream() chunk the stream, and does it rewind/close the resource? Can we reuse the stream afterwards (e.g. to persist the file after a clean verdict)?
  3. What is the exact exception surface, and can we reliably distinguish "infected" from "scan failed"?
  4. Are timeouts configurable at the package level, or only on the daemon?
  5. What PHP/Laravel versions are supported, and is there a test suite we can run against in CI?
  6. License terms and our obligations if we fork.

Integration Approach

Stack fit

  • Assumes Laravel 9+ / PHP 8.1+; verify against the package's declared constraints. The facade + config pattern composes cleanly with our existing upload pipeline and with Laravel's filesystem abstraction (Storage::readStream() yields a resource, which is precisely what scanStream() wants).
  • clamd deployed as a sidecar (Kubernetes) or a dedicated internal service, with freshclam running alongside or as an init/scheduled job.

Migration path

  • Greenfield: straightforward — config, facade, one guard clause in the upload path.
  • Replacing an existing exec('clamscan ...') or a homegrown wrapper: the migration is not purely mechanical. clamscan (one-shot CLI) and clamd (daemon) differ in startup cost, timeout behavior, and error output. Expect to rewrite error handling and to re-baseline expected latencies. Keep the old path behind a flag until parity is demonstrated.
  • Adopting scanStream() specifically: treat as an additive path, not a replacement. Keep scanFile() for anything already on local disk; use scanStream() only for large or remote objects. This bounds the blast radius of a new code path.

Compatibility

  • Pin the version (2.1.x) rather than floating. Verify transitive dependency constraints don't conflict with the framework version.
  • Confirm the package behaves with both unix-socket and TCP transports — socket permissions in containers are a recurring source of "works on my machine."
  • No breaking changes are implied by the v2.1.0 notes (additive method, minor bump). Low compatibility risk from this specific release.

Sequencing

  1. Spike (0.5–1 day): stand up clamd in Docker, run a clean file, an EICAR test file, and a >StreamMaxLength file through scanStream(). Capture exact exception types and messages.
  2. Infrastructure: sidecar/service definition, freshclam update schedule, health/readiness probe, resource requests (signature DB needs roughly 1–1.5 GB RAM resident), log shipping.
  3. Policy decision: define fail-closed behavior, size cap, and timeout; encode these in our own thin wrapper service rather than calling the facade directly from controllers. This wrapper is the seam we can later swap to a queue if needed.
  4. Integrate scanFile() into the existing upload path first. Lowest risk, immediate value.
  5. Integrate scanStream() for the S3/large-object path behind a feature flag.
  6. Observability: emit scan duration, verdict, and error class as metrics; alert on error rate and on clamd unreachability. A silent scanner is indistinguishable from a working one without this.
  7. Load test at expected peak upload concurrency before full rollout; size the clamd replica count and PHP-FPM pool from the result.
  8. Rollout with the flag, then remove the legacy path.

Operational Impact

Maintenance

  • The dominant ongoing cost is the ClamAV runtime, not the PHP package: signature freshness (freshclam, typically several updates per day), daemon restarts to pick up major signature DB reloads, and RAM headroom.
  • The package itself is small and low-churn. Expect occasional minor releases; budget a routine dependency-review cadence rather than continuous attention. Pin and review.
  • Pin the ClamAV engine version too — engine upgrades occasionally change INSTREAM behavior and memory profile.

Support

  • Single-maintainer risk is real for a security-adjacent dependency. Mitigations: pin the version, keep our wrapper thin and interface-based, and confirm we can fork under the license if upstream stalls.
  • Internal support burden lands on platform/infra for the daemon and on application teams for the wrapper. Document which team owns the fail-closed policy — this is the question that gets escalated at 3 a.m.

Scaling

  • clamd is multithreaded but memory-bound; it scales vertically well and horizontally with replicas behind a load balancer or per-node sidecars. It will become the bottleneck before the Laravel app does for scan-heavy workloads.
  • The real scaling constraint is PHP worker occupancy. A synchronous stream scan holds a worker for transfer + scan time. At 100 concurrent 500 MB uploads, this is an outage regardless of how many clamd replicas exist. Mitigate with strict size caps, per-request timeouts, and a queue-based path for anything above a threshold.
  • Sidecar-per-pod duplicates the signature DB in memory across every replica — cheap operationally, expensive in aggregate RAM. Weigh against a shared clamd service.

Failure modes

  • clamd down or socket unreachable → must fail closed; otherwise the scanner becomes a no-op that silently approves everything. This is the failure that matters and the one most likely to be introduced by careless exception handling.
  • StreamMaxLength exceeded → clamd error surfaced as a scan failure; must map to a clear 4xx ("file too large to scan"), not a 500.
  • Scan timeout on large streams → worker tied up, then a failure with an ambiguous verdict. Requires an explicit timeout well below the PHP max_execution_time.
  • Corrupted or stale signature DB → false negatives with no visible symptom. Detectable only via a canary scan (periodic EICAR check) and signature-age metric. Recommend implementing both.
  • Stream already consumed/rewound incorrectly → zero-byte scan that reports clean. Verify stream handling in the spike; this is the subtle bug worth a regression test.

Ramp-up

  • For a Laravel developer: low. Facade + config is idiomatic and the API surface is one or two methods. Half a day to first working scan.
  • For operations: moderate. Daemon deployment, signature update scheduling, memory sizing, and health checking are all standard but non-trivial; budget a day for a first-class sidecar setup with probes and metrics.
  • Total estimate: roughly 2–3 engineering days end-to-end for a production-quality integration including the wrapper, policy, observability, and load test — assuming infrastructure support is available. scanStream() itself adds little effort on top of scanFile(); its value is avoiding a temp-file round trip and enabling the object-storage path, not reducing integration work.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin