Security attachment scanning
Multiforum's first-party download scanner is split into two independently deployed parts:
security-attachment-scanis a TypeScript plugin that runs in the Multiforum backend and participates in download pipelines;multiforum-plugin-security-scan-serviceis a Python service that downloads and inspects the untrusted bytes outside the main application process.
The service combines VirusTotal reputation with static ZIP checks for dangerous extensions, path traversal, excessive compression, and optional channel rules such as requiring a root README or LICENSE.
Authentication model
The plugin sends POST /scan with an X-API-Key header. The two sides must
share the same value:
| Location | Name |
|---|---|
| Multiforum plugin secret | SCAN_SERVICE_API_KEY |
| Scan service secret | SCAN_API_KEY |
The VirusTotal key is a separate scan-service secret named
SCAN_VIRUSTOTAL_API_KEY. Do not enter it in the Multiforum plugin settings.
The supported Cloud Run configuration allows requests to reach the service and
protects /scan with the shared API key. This lets a Multiforum backend hosted
outside Google Cloud call the service without a Google identity token. The
unauthenticated /health response contains no secret data.
Supported production deployment
The scan-service repository contains the supported infrastructure-as-code workflow. It uses Terraform for Cloud Run, Artifact Registry, Secret Manager, IAM, probes, and scaling; GitHub Actions authenticates to Google Cloud with Workload Identity Federation and deploys an immutable commit-tagged image.
The deployment then runs mfctl with a narrowly scoped OAuth machine identity
to reconcile all of the application-side state:
- install and enable
security-attachment-scanversion0.5.1; - save the Cloud Run service URL;
- copy the shared API key into
SCAN_SERVICE_API_KEYwithout putting it in the manifest or logs; - configure the blocking policy;
- configure the
downloadableFile.created,.updated, and.downloadedpipelines.
Follow the scan service's production deployment runbook for bootstrap variables, GitHub environment values, deployment verification, rotation, and rollback. Avoid making lasting changes by hand in Cloud Run; Terraform will treat them as drift.
Manual Multiforum configuration
If the scan service is already deployed and you are configuring Multiforum in the UI:
- Install and enable
security-attachment-scanversion0.5.1. - Set
SCAN_SERVICE_API_KEYto the same value as the service'sSCAN_API_KEY. - Set Service URL to the Cloud Run base URL, without
/scan. - Choose
blockOn:maliciousblocks malicious findings;suspiciousalso blocks suspicious findings. - Keep
onError: blockto fail closed unless availability is deliberately more important than an inconclusive security result. - Add the plugin to the desired download pipelines. Enabling the plugin alone does not schedule it.
For a server-wide requirement, include the scanner in all three
downloadableFile.* pipelines. A channel cannot bypass a server pipeline. If
scanning is optional by channel, omit the server upload requirement and add the
scanner to that channel's discussionChannel.created pipeline. A server-wide
upload scan takes precedence if both policies select the scanner, so the same
bytes are not scanned twice merely because the discussion joins a channel.
Channel plugin settings can additionally require a README or LICENSE at the root of ZIP uploads.
Quarantine behavior
With the default fail-closed policy, a file remains unavailable until the
current file version has a clean result. PENDING, SUSPICIOUS, INFECTED,
and FAILED are logical quarantine states; the file and discussion remain in
the database, but the download route refuses to serve the bytes.
Replacing a file creates a new version and returns it to quarantine. A clean result or human release for an older version never approves the replacement.
Diagnose failures
Start with the public diagnostic on the download's Pipelines tab:
| Code | Meaning |
|---|---|
SCAN_COMPLETE | The scan completed without finding a threat. |
SCAN_NOT_APPLICABLE | The event contained no attachment to scan. |
SCAN_SUSPICIOUS | The scan needs human review. |
SCAN_MALWARE_DETECTED | The scan detected a threat. |
SCAN_PROVIDER_ERROR | The service or one of its providers could not complete. |
SCAN_CONFIGURATION_REQUIRED | The service URL or shared plugin secret is missing. |
Completed diagnostics include a correlationId. Search for that value in the
private Multiforum backend and Cloud Run logs to follow the same request. The
public diagnostic intentionally omits signed attachment URLs, credentials,
provider bodies, and stack traces.
For SCAN_PROVIDER_ERROR, verify in this order:
- the service
/healthendpoint responds; - the plugin Service URL is the current deployment URL;
SCAN_SERVICE_API_KEYandSCAN_API_KEYmatch;- Cloud Run logs contain the diagnostic correlation ID;
- the service can fetch the private, time-limited attachment URL before it expires;
- VirusTotal or another provider is not returning a transient error.
Retry only after correcting a configuration problem or allowing a transient failure to clear. Pipeline retry limits still apply.
See Plugin pipelines for rollout, retry, and review behavior and Declarative plugin configuration for plan/apply automation.