Scan for Malware with Node.js / TypeScript

Use AttachmentScanner to scan files and URLs for viruses and malware using Node's built-in fetch API.

Scan a URL

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

const response = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://www.attachmentscanner.com/eicar.com" }),
});

const result = await response.json();
console.log(result.status);  // "found"
console.log(result.matches); // ["Eicar-Test-File-Signature"]

Scan a File

Upload a file directly from disk:

const fs = require("fs");

const formData = new FormData();
formData.append("file", new Blob([fs.readFileSync("/path/to/file.pdf")]), "file.pdf");

const response = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_TOKEN}`,
  },
  body: formData,
});

const result = await response.json();
console.log(result.status); // "ok" if clean, "found" if malware detected

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
const response = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/large-file.zip",
    async: true,
    callback: "https://your-app.com/webhooks/scan-complete",
  }),
});

const { id, status } = await response.json();
console.log(id);     // "a1b2c3d4-..."
console.log(status); // "pending"
// 2. Receive the callback (Express example)
app.post("/webhooks/scan-complete", express.json(), (req, res) => {
  const { id, status, matches } = req.body;

  if (status === "found") {
    console.log(`Malware detected in scan ${id}:`, matches);
    // quarantine or delete the file
  } else if (status === "ok") {
    // file is clean — move to final storage
  }

  res.json({ received: true });
});

You can also poll for results instead of using a callback:

// Poll for results
const result = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans/${id}`, {
  headers: { Authorization: `Bearer ${API_TOKEN}` },
});

const scan = await result.json();
console.log(scan.status); // "ok", "found", "pending", etc.

Express Integration

A full Express app that accepts file uploads and scans them:

const express = require("express");
const multer = require("multer");
const upload = multer({ storage: multer.memoryStorage() });

const app = express();
const { ATTACHMENT_SCANNER_URL, ATTACHMENT_SCANNER_API_TOKEN } = process.env;

// Scan by URL
app.post("/scan/url", express.json(), async (req, res) => {
  const response = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${ATTACHMENT_SCANNER_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url: req.body.url }),
  });

  const result = await response.json();
  res.json(result);
});

// Scan by file upload
app.post("/scan/file", upload.single("file"), async (req, res) => {
  const formData = new FormData();
  formData.append("file", new Blob([req.file.buffer]), req.file.originalname);

  const response = await fetch(`${ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${ATTACHMENT_SCANNER_API_TOKEN}`,
    },
    body: formData,
  });

  const result = await response.json();
  res.json(result);
});

// Webhook callback handler
app.post("/webhooks/scan-complete", express.json(), (req, res) => {
  const { status, matches } = req.body;

  if (status === "found") {
    // quarantine or delete the file
  }

  res.json({ received: true });
});

app.listen(3000, () => console.log("Listening on :3000"));

TypeScript

Type-safe wrapper with full response types:

interface ScanResult {
  id: string;
  status: "pending" | "ok" | "found" | "failed";
  callback: string | null;
  url: string | null;
  filename: string | null;
  content_length: number | null;
  md5: string | null;
  sha256: string | null;
  matches: string[];
  created_at: string;
  updated_at: string;
}

class AttachmentScanner {
  constructor(
    private baseUrl: string,
    private apiToken: string
  ) {}

  private get headers() {
    return { Authorization: `Bearer ${this.apiToken}` };
  }

  async scanUrl(url: string): Promise<ScanResult> {
    const response = await fetch(`${this.baseUrl}/v1.0/scans`, {
      method: "POST",
      headers: { ...this.headers, "Content-Type": "application/json" },
      body: JSON.stringify({ url }),
    });
    return response.json() as Promise<ScanResult>;
  }

  async scanFile(file: Blob, filename: string): Promise<ScanResult> {
    const formData = new FormData();
    formData.append("file", file, filename);

    const response = await fetch(`${this.baseUrl}/v1.0/scans`, {
      method: "POST",
      headers: this.headers,
      body: formData,
    });
    return response.json() as Promise<ScanResult>;
  }

  async scanAsync(
    url: string,
    callback: string
  ): Promise<ScanResult> {
    const response = await fetch(`${this.baseUrl}/v1.0/scans`, {
      method: "POST",
      headers: { ...this.headers, "Content-Type": "application/json" },
      body: JSON.stringify({ url, async: true, callback }),
    });
    return response.json() as Promise<ScanResult>;
  }

  async getScan(id: string): Promise<ScanResult> {
    const response = await fetch(`${this.baseUrl}/v1.0/scans/${id}`, {
      headers: this.headers,
    });
    return response.json() as Promise<ScanResult>;
  }
}
// Usage
const scanner = new AttachmentScanner(
  process.env.ATTACHMENT_SCANNER_URL!,
  process.env.ATTACHMENT_SCANNER_API_TOKEN!
);

const result = await scanner.scanUrl("https://www.attachmentscanner.com/eicar.com");

if (result.status === "found") {
  console.log("Malware detected:", result.matches);
}

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"}'