Skip to main content
Jahia Store
EN

ClamAV filter

community
Download 1.0.4

Information

Module ID
clamav-filter
Group ID
org.jahia.community
Status
community
Category
Security
Author
Florent BOURASSE
Developer website
http://www.jahia.com
Requires Jahia
8.2.1.0
Updated
2026-08-14
Source
scm:git:git@github.com:Jahia/clamav-filter.git
Tags
  • security

A Jahia module that scans uploaded files against a ClamAV antivirus daemon and blocks any upload containing detected malware.

Screenshots

Dependencies

Depended on by

Nothing depends on this module.

Versions

⚠️ Breaking change — GraphQL operations are now namespaced

The flat root fields were replaced by a single clamav namespace container on both Query and Mutation. Any client using the old field names must be updated.

Before (1.0.1)

Now (1.0.4)

clamavSettings

clamav { settings }

clamavPing

clamav { ping }

clamavScanTest(content:)

clamav { scanTest(content:) }

clamavSaveSettings(...)

clamav { saveSettings(...) }

query { clamav { settings { host port connectionTimeout readTimeout } ping } }

 

Flat root fields collide across modules — two bundles registering the same global field makes the entire GraphQL schema fail to build. Grouping under one container per module removes that whole class of failure and makes ownership explicit.

Security

  • Raw binary upload coverage (SEC-141). Scanning previously covered only multipart/form-data and the Forms octet-stream endpoint, so malware delivered over a non-multipart channel entered the repository unscanned. Any application/octet-stream body and any PUT carrying a body — WebDAV and raw-body JCR REST binary writes — now go through the same fail-closed path.
  • No attacker-toggleable bypass. A webflowToken-based skip was removed: a guard the uploader can flip is not a guard. Scanning is never conditional on a client-supplied value.
  • No TOCTOU gap. The buffered request wrapper is forwarded downstream, so the bytes scanned are exactly the bytes Jahia consumes.
  • Every multipart part is scanned, including multiple files posted under the same field name (previously collapsed to one, leaving the rest unscanned but replayed downstream).
  • Fail-closed. Scanner unreachable or a scanner error rejects the upload with 503; uploads are never silently passed through.
  • Fine-grained RBAC. A module-specific clamavAdmin permission gates the settings and test endpoints, plus an assignable clamav-filter-administrator role granting only administrationAccess + clamavAdmin — so this capability can be delegated without granting full server admin.
  • Host validation on every trust boundary, including configs pushed straight through ConfigurationAdmin (a .cfg file), not just the admin UI.
  • Heap-DoS ceiling. Bodies above 100 MiB are rejected with 413 before buffering; bounded daemon replies and CRLF-sanitised log output close log-injection and unbounded-read paths.
  • Early mid-stream detections are classified as real detections rather than swallowed as generic errors.

Response codes

Outcome

Status

Clean

passes downstream (wrapped request)

Infected

403 Forbidden

Body over 100 MiB

413 Payload Too Large

Scanner unreachable / error

503 Service Unavailable (fail-closed)

Unexpected parse/read failure

500 Internal Server Error

Quality

  • 133 JUnit tests plus 36 Jest tests and Cypress e2e coverage for the fail-closed core, GraphQL resolvers, permission enforcement, raw binary uploads and schema namespacing.
  • Atomic OSGi config validation — an invalid update is rejected wholesale and leaves the running config untouched.
  • Result is now an immutable value object; admin UI audited for WCAG 2.2 AAA.

Dependencies

All Dependabot alerts resolved across both Yarn workspaces (26 in this cycle, plus earlier tmp, qs, uuid and assertj-core fixes). Every one is a build-time or test-time dependency — none ships inside the OSGi bundle.

.github/dependabot.yml had shipped as the GitHub scaffold with an invalid empty package-ecosystem, so version updates never ran and routine patches only ever surfaced later as security alerts. Now configured for npm (/ and /tests) and maven, with grouped updates.

Documentation

Corrected upload-coverage claims that had been stale since SEC-141: the README still stated WebDAV PUT and raw-body JCR REST uploads were not intercepted and told operators to defend them separately — the opposite of current behaviour.

Note on versioning: 1.0.2 and 1.0.3 were bumped in pom.xml but never tagged, so this release follows 1_0_1 directly.

Full changelog: 1_0_1...1_0_4

Requires Jahia 8.2.1.0Updated 2026-09-14

Highlights

  • Security hardening of the ClamAV filter pipeline: bounded request buffering, fail-closed on scanner unavailability, SSRF input validation, and TOCTOU mitigation.
  • Full WCAG 2.1 AA audit remediation plus push to AAA 7:1 contrast across the admin UI.
  • Dependency vulnerability fixes (Dependabot/CVE) on @babel/plugin-transform-modules-systemjs and systeminformation.
  • Project guidance docs (AGENTS.md / CLAUDE.md) added for AI-assisted development.

Fixes

Security (431fe9d)

  • MultiReadHttpServletRequest: bound buffered body by configurable max size; throws RequestTooLargeException → HTTP 413 to prevent unauthenticated heap DoS.
  • ClamavFilter: scoped to scan-eligible requests, forwards the wrapped request downstream so scanned bytes equal consumed bytes (closes TOCTOU); fails-closed with HTTP 503 when ClamAV is unreachable instead of silently passing uploads.
  • clamavSaveSettings mutation: validates host charset/length, port range and timeout bounds to block scanner-rewrite SSRF that could bypass scanning.
  • clamavScanTest: caps base64 input length to prevent decode DoS.
  • ClamavServiceImpl: bounded INSTREAM reply reads, CRLF-stripped/truncated log messages, explicit US-ASCII/UTF-8, defensive bounds checks in populateVirusScanResult.
  • ClamavConfig: atomic full-update validation; fields made volatile.

Accessibility — WCAG 2.1 AA (ec95a77c275ea8)

  • aria-hidden="true" on alert icon spans (✓ / ✕) to stop AT reading Unicode names.
  • aria-live="polite" on filename display; kept always in DOM so selection is announced.
  • Replaced 3 dynamic-role live regions with 6 fixed alert+status pairs (save / ping / scan) for stable AT subscriptions.
  • Removed focus() calls on sr-only divs after async ops; aria-live announces without stealing focus.
  • 4 px left accent border on alert boxes for boundary contrast.
  • aria-describedby host-input format hint; tooltip colour darkened #767676 → #6b6b6b.
  • aria-describedby + sr-only rationale on disabled scan section.

Accessibility — WCAG 2 AAA 7:1 contrast (0833b6b3979225)

  • Alert success text: #2e7d32 → #1b5e20 → #175217 (final ~8.3:1 on #e8f5e9).
  • Alert error text: #c62828 → #9e1a1a (~7.1:1 on #fdecea).
  • Buttons/labels: #0077cc → #004a80 (~9.2:1 on white).
  • Input focus borders/outlines aligned for consistency.

Dependency / CVE

  • 2229fbc — upgrade systeminformation 5.31.5 → 5.31.6 via resolution (CVE fix).
  • c5c9a42 — upgrade @babel/plugin-transform-modules-systemjs (Dependabot alert).

Refactoring

  • ClamavFilter / MultiReadHttpServletRequest / ClamavServiceImpl / ClamavConfig substantially reworked alongside the security fix (431fe9d) — clearer scoping, explicit charsets, atomic config swaps.
  • ClamavConstants extended with new bounds/error constants.
  • GraphQL extensions (ClamavMutationExtensionClamavQueryExtension) reorganised around input validation and error reporting.
  • UI components (ClamavFilter.jsx / .scss) restructured around fixed live-region pairs and contrast-compliant tokens.
  • Locale en.json updated for new validation and accessibility messages.

Tests

  • tests/package.json and tests/yarn.lock updated (dependency bumps); no new test commits identified in range.

Docs / Tooling

  • cba8b9d — add Claude basic context.
  • 38113e3 — rename CLAUDE.md to AGENTS.md, add CLAUDE.md reference shim.

Full Changelog1_0_0...1_0_1

Requires Jahia 8.2.1.0Updated 2026-05-19
Requires Jahia 8.2.1.0Updated 2026-04-18