The Security: Attachment Scan plugin protects downloadable uploads. When enabled at the server scope it inspects attachments as soon as they are uploaded (and again right before a visitor downloads the bundle) by calling the Multiforum security-scan service — a standalone Python/FastAPI microservice.
Why a separate service? Multiforum plugins run in-process inside the Node backend, so heavy or risky file handling (downloading untrusted uploads, unpacking ZIPs) is better kept out of that process. The service runs the VirusTotal reputation lookup and static ZIP analysis (dangerous file types, zip-bomb ratio, path-traversal entries, README/LICENSE checks) and returns a single verdict. The VirusTotal API key lives on the service, not in the forum backend.
- A creator uploads a downloadable file to a channel.
- Multiforum emits
downloadableFile.created/.updated/.downloaded. - For each attachment URL, the plugin
POSTs to the service's/scanendpoint with theX-API-Keyheader. - The service returns a verdict —
clean,suspicious,malicious, orerror. - The upload is blocked if any attachment is a blocking result:
- a finding at or above the
blockOnthreshold (suspiciousormalicious), or - a scan
error, whenonErrorisblock(the default — see below). Blocking makes the plugin returnsuccess: false, which fails the pipeline step. Any non-clean verdict is also recorded viactx.storeFlag()so moderators see it.
- a finding at or above the
On scan errors (fail closed vs open). A scan
error— a service failure, an unreachable service, or an unreadable file — is an operational state, not a point on the malice scale, so it is governed by its ownonErrortoggle rather than byblockOn. The default isblock(fail closed): if we can't determine a file is safe, we don't let it through — which also means an attacker can't bypass the scan by deliberately making it error. SetonError: allowonly if availability matters more than safety for your server.
| Key | Scope | Description |
|---|---|---|
SCAN_SERVICE_API_KEY |
Server | Shared secret sent as X-API-Key; must match the service's SCAN_API_KEY. |
| Key | Scope | Default | Description |
|---|---|---|---|
serviceUrl |
Server | — | Base URL of the deployed Cloud Run scan service. |
blockOn |
Server | malicious |
Malice threshold that blocks the upload: suspicious or malicious. |
onError |
Server | block |
What to do when the scan can't complete: block (fail closed) or allow (fail open). |
policy.require_readme |
Channel | false |
Require a README at the ZIP root. |
policy.require_license |
Channel | false |
Require a LICENSE at the ZIP root. |
The manifest ships UI metadata so administrators can configure all of this
directly inside Multiforum. See plugin.json for the full schema.
This repository is the source of truth for security-attachment-scan. Plugin
releases are versioned with Git tags in the form v<plugin.json version>.
npm install
npm run cinpm run ci validates plugin.json, runs Vitest, builds TypeScript, and
creates a release bundle under out/.
- Update
plugin.jsonandpackage.jsonto the same version. - Commit the change.
- Tag the commit with
v<version>. - Push the tag.
The Publish Release workflow builds security-attachment-scan-<version>.tgz,
writes a SHA-256 checksum, and uploads both artifacts (plus plugin.json) to the
GitHub Release.
Use this source URL in the Multiforum plugin registry:
{
"sourceRepoUrl": "https://github.com/gennit-project/multiforum-plugin-security-attachment-scan",
"releaseNotesUrl": "https://github.com/gennit-project/multiforum-plugin-security-attachment-scan/releases/tag/v0.4.0"
}