Skip to main content
Malware scan

Malware scan

fremforge runs a ClamAV-based scanner in front of every binary-upload surface. If a signature matches, the upload is rejected with HTTP 422 and a finding row lands in the per-tenant malware-scan log.

The scanner runs without Cisco Talos and without abuse.ch. Both gate commercial use behind a paid licence and we don’t want a sub-processor on the scanning path that we can’t fully attribute to.

Signature stack

FeedWhat it coversCadenceLicence
SaneSecurityCommodity email and macro malware, phishing, JS droppersPulled every 4h from an EU mirrorFree, donation-supported
YARA-Forge CoreThreat-actor TTPs, packers, web shells, native payloads. Aggregator of 45+ vetted YARA-rule repos (Florian Roth signature-base, DidierStevens, etc.)Pulled every 4h, upstream releases weeklyDRL 1.1, commercial use OK with attribution
Linux Malware Detect (LMD)Linux server-side payloads: web shells, cryptominers, backdoors. rfxn.com static datasetPulled every 4h, upstream dailyGPLv2
EICAR test signatureThe standard malware-scan smoke-test signature, re-seeded on every refreshStatic, ConfigMap-mountedPublic domain

Feed freshness is monitored by fremverk operators; customers don’t see signature freshness directly. The SLA covers scanner availability.

Surfaces

Scanned, fail-closed (upload rejected if scanner unreachable):

  • LFS object PUT
  • Issue and PR attachment POST
  • Package registry PUT / POST (npm, Maven, container, generic)

NOT scanned:

  • Git protocol pushes (git objects). Push-time secret detection is handled separately by push protection.
  • Workflow artifacts. Those are short-lived, scoped to the tenant, and policy says “don’t accept artifacts you don’t trust” already.

What happens when a match fires

  1. The proxy rejects the upload with HTTP 422 and a body that names the signature, the SHA-256 of the rejected bytes, and a pointer to the override flow.
  2. A finding row is written to malware_scan_findings via the api’s /internal/malware-scan/ingest endpoint (mutual auth, bearer + IP-allowlist).
  3. The finding appears immediately in tenant admin, Security, Malware scan with the upload path, the matched signature, who attempted the upload, and the file size.

Overrides (false positives)

Some signatures fire on legitimate test fixtures, security-research samples, or harmless artefacts that happen to match an old pattern. Tenant admins can add an override that whitelists a specific SHA-256 hash + repository pair.

  1. Tenant admin → Security → Malware scan. Expand Active overrides and click Add override.
  2. Paste the SHA-256 of the rejected file (shown in the 422 response).
  3. Pick the scope: this repo only, or all repos in the org.
  4. Set an expiry (default 90 days; max 1 year).
  5. Add a justification (free text, audited in the org audit log).

An override only short-circuits the matched-by-hash decision. If a different signature matches the same bytes the upload is still rejected. fremverk operators review overrides quarterly as part of the security review.

Via REST API

Malware-scan findings and overrides are exposed on the public API surface. Use this when wiring upload-rejection alerts into an external ticketing system, scripting override lifecycle, or driving from an AI agent.

  • List findings GET /api/v1/orgs/:slug/malware-scan/findings
  • Dismiss a finding POST /api/v1/orgs/:slug/malware-scan/findings/:id/dismiss
  • List overrides GET /api/v1/orgs/:slug/malware-scan/overrides
  • Create override POST /api/v1/orgs/:slug/malware-scan/overrides body: { "kind": "file", "signature": "sha256:...", "reason": "test fixture", "expires_at": "2027-01-01T00:00:00Z" }
  • Revoke override DELETE /api/v1/orgs/:slug/malware-scan/overrides/:id

Listing requires the findings:read scope; mutations require findings:write. Use a Personal Access Token (PAT):

curl -X POST \
  -H "Authorization: Bearer ${FREMFORGE_PAT}" \
  -H "Content-Type: application/json" \
  -d '{"kind":"file","signature":"sha256:abc123...","reason":"known-clean test artefact","expires_at":"2027-01-01T00:00:00Z"}' \
  https://frem.sh/api/v1/orgs/acme/malware-scan/overrides

See the public REST API reference for the full OpenAPI specification.

SLA and fail-closed posture

If the ClamAV daemon is unreachable, uploads are rejected with HTTP 503 and a Retry-After. That’s the fail-closed decision per our Acceptable Use Policy. The alternative (fail-open during a scanner outage) opens a window where the AUP commitment is silently bypassed; we don’t think that trade is worth it. Operators are paged on scanner-down within 5 minutes.

EICAR smoke test

A canary CronJob uploads the standard EICAR test file every 30 minutes and verifies the upload is rejected. If three consecutive canary runs succeed (i.e. the rejection stops happening), operators are paged. That covers the case where the signature DB shipped clean but failed to load.