Scan for Malware with Go

Use AttachmentScanner to scan files and URLs for viruses and malware using Go's standard library net/http — no external dependencies required. These snippets use map[string]interface{} for brevity — see the Typed section for a ScanResult struct.

Scan a URL

The simplest way to scan — pass a URL and get back a result:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

func main() {
	scannerURL := os.Getenv("ATTACHMENT_SCANNER_URL")
	apiToken := os.Getenv("ATTACHMENT_SCANNER_API_TOKEN")

	payload, _ := json.Marshal(map[string]string{
		"url": "https://www.attachmentscanner.com/eicar.com",
	})
	req, _ := http.NewRequest("POST", scannerURL+"/v1.0/scans", bytes.NewReader(payload))
	req.Header.Set("Authorization", "Bearer "+apiToken)
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var result map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&result)
	fmt.Println(result["status"])  // "found"
	fmt.Println(result["matches"]) // [Eicar-Test-File-Signature]
}

Scan a File

Upload a file directly from disk:

func scanFile(filePath string) (map[string]interface{}, error) {
	file, _ := os.Open(filePath)
	defer file.Close()

	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)
	part, _ := writer.CreateFormFile("file", filepath.Base(filePath))
	io.Copy(part, file)
	writer.Close()

	req, _ := http.NewRequest("POST", scannerURL+"/v1.0/scans", &buf)
	req.Header.Set("Authorization", "Bearer "+apiToken)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&result)
	return result, nil // result["status"] is "ok" if clean, "found" if malware
}

Async Scan with Callback

For large files or non-blocking workflows, use async: true with a callback URL. The API returns immediately with a pending status and POSTs the result to your callback when the scan completes:

// 1. Submit the scan
payload, _ := json.Marshal(map[string]interface{}{
	"url":      "https://example.com/large-file.zip",
	"async":    true,
	"callback": "https://your-app.com/webhooks/scan-complete",
})
req, _ := http.NewRequest("POST", scannerURL+"/v1.0/scans", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+apiToken)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var scan map[string]interface{}
json.NewDecoder(resp.Body).Decode(&scan)
fmt.Println(scan["id"])     // "a1b2c3d4-..."
fmt.Println(scan["status"]) // "pending"
// 2. Receive the callback
http.HandleFunc("POST /webhooks/scan-complete", func(w http.ResponseWriter, r *http.Request) {
	var result map[string]interface{}
	json.NewDecoder(r.Body).Decode(&result)

	switch result["status"] {
	case "found":
		fmt.Printf("Malware detected: %v\n", result["matches"])
		// quarantine or delete the file
	case "ok":
		// file is clean — move to final storage
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"received": true}`))
})
// 3. Poll for results (alternative to callback)
scanID := scan["id"].(string)
req, _ := http.NewRequest("GET", scannerURL+"/v1.0/scans/"+scanID, nil)
req.Header.Set("Authorization", "Bearer "+apiToken)

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
fmt.Println(result["status"]) // "ok", "found", "pending", etc.

Typed

Type-safe wrapper with full response types:

type ScanResult struct {
	ID            string   `json:"id"`
	Status        string   `json:"status"`
	Callback      *string  `json:"callback"`
	URL           *string  `json:"url"`
	Filename      *string  `json:"filename"`
	ContentLength *int64   `json:"content_length"`
	MD5           *string  `json:"md5"`
	SHA256        *string  `json:"sha256"`
	Matches       []string `json:"matches"`
	CreatedAt     string   `json:"created_at"`
	UpdatedAt     string   `json:"updated_at"`
}

func scanURL(apiURL, token, targetURL string) (*ScanResult, error) {
	payload, _ := json.Marshal(map[string]string{"url": targetURL})
	req, _ := http.NewRequest("POST", apiURL+"/v1.0/scans", bytes.NewReader(payload))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result ScanResult
	json.NewDecoder(resp.Body).Decode(&result)
	return &result, nil
}

func scanFile(apiURL, token, filePath string) (*ScanResult, error) {
	file, _ := os.Open(filePath)
	defer file.Close()

	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)
	part, _ := writer.CreateFormFile("file", filepath.Base(filePath))
	io.Copy(part, file)
	writer.Close()

	req, _ := http.NewRequest("POST", apiURL+"/v1.0/scans", &buf)
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var result ScanResult
	json.NewDecoder(resp.Body).Decode(&result)
	return &result, nil
}
// Usage
result, err := scanURL(scannerURL, apiToken, "https://www.attachmentscanner.com/eicar.com")
if err != nil {
	log.Fatal(err)
}

if result.Status == "found" {
	fmt.Println("Malware detected:", result.Matches)
}

HTTP Server Integration

A full Go HTTP server that accepts file uploads and scans them:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

type ScanResult struct {
	ID            string   `json:"id"`
	Status        string   `json:"status"`
	Callback      *string  `json:"callback"`
	URL           *string  `json:"url"`
	Filename      *string  `json:"filename"`
	ContentLength *int64   `json:"content_length"`
	MD5           *string  `json:"md5"`
	SHA256        *string  `json:"sha256"`
	Matches       []string `json:"matches"`
	CreatedAt     string   `json:"created_at"`
	UpdatedAt     string   `json:"updated_at"`
}

var (
	scannerURL = os.Getenv("ATTACHMENT_SCANNER_URL")
	apiToken   = os.Getenv("ATTACHMENT_SCANNER_API_TOKEN")
)

func scanURL(w http.ResponseWriter, r *http.Request) {
	var body struct {
		URL string `json:"url"`
	}
	json.NewDecoder(r.Body).Decode(&body)

	payload, _ := json.Marshal(map[string]string{"url": body.URL})
	req, _ := http.NewRequest("POST", scannerURL+"/v1.0/scans", bytes.NewReader(payload))
	req.Header.Set("Authorization", "Bearer "+apiToken)
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	w.Header().Set("Content-Type", "application/json")
	io.Copy(w, resp.Body)
}

func scanFile(w http.ResponseWriter, r *http.Request) {
	file, header, err := r.FormFile("file")
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}
	defer file.Close()

	var buf bytes.Buffer
	writer := multipart.NewWriter(&buf)
	part, _ := writer.CreateFormFile("file", header.Filename)
	io.Copy(part, file)
	writer.Close()

	req, _ := http.NewRequest("POST", scannerURL+"/v1.0/scans", &buf)
	req.Header.Set("Authorization", "Bearer "+apiToken)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer resp.Body.Close()

	w.Header().Set("Content-Type", "application/json")
	io.Copy(w, resp.Body)
}

// Webhook callback handler
func scanCallback(w http.ResponseWriter, r *http.Request) {
	var result ScanResult
	json.NewDecoder(r.Body).Decode(&result)

	switch result.Status {
	case "found":
		fmt.Printf("Malware detected: %v\n", result.Matches)
		// quarantine or delete the file
	case "ok":
		// file is clean — move to final storage
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"received": true}`))
}

func main() {
	http.HandleFunc("POST /scan/url", scanURL)
	http.HandleFunc("POST /scan/file", scanFile)
	http.HandleFunc("POST /webhooks/scan-complete", scanCallback)
	fmt.Println("Go example listening on :3000")
	http.ListenAndServe(":3000", nil)
}

curl Equivalent

# Scan by URL
curl -X POST https://YOUR_SCANNER_URL/v1.0/scans \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.attachmentscanner.com/eicar.com"}'

# Scan by file upload
curl -X POST https://YOUR_SCANNER_URL/v1.0/scans \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -F "file=@/path/to/file.pdf"

# Async scan with callback
curl -X POST https://YOUR_SCANNER_URL/v1.0/scans \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/file.zip", "async": true, "callback": "https://your-app.com/webhooks/scan-complete"}'