npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

malshare-sdk

v1.0.1

Published

JavaScript/TypeScript SDK for the MalShare API — free malware sample repository. Works on Bun, Node.js, Deno, Cloudflare Workers.

Readme

MalShare SDK

JavaScript/TypeScript client for the MalShare API — a free malware sample repository with 1M+ samples. Works on Bun, Node.js, Deno, and Cloudflare Workers. Zero dependencies.

npm version License: MIT Bun compatible Tests Zero deps


Quick Start

bun add malshare-sdk           # or: npm install malshare-sdk

Get a free API key at malshare.com/register.php. Free accounts get 2,000 API calls/day.

export MALSHARE_KEY=your-key-here
import { MalShare } from 'malshare-sdk';

const ms = new MalShare(process.env.MALSHARE_KEY);

// Latest sample hashes (24h)
const hashes = await ms.listSamples();
console.log(`${hashes.length} samples`);

// File metadata
const info = await ms.details('46faab8ab153...');
// → { MD5, SHA1, SHA256, F_TYPE: 'PE32 executable', F_SIZE: 123456, ... }

// Download a sample (live malware! handle with care)
const bytes = await ms.download('46faab8ab153...');
await Bun.write('/tmp/malware.bin', bytes);

CLI Usage

# Install globally
bun install -g malshare-sdk

# List today's samples
malshare list

# Sample details
malshare info 46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09

# Download to file
malshare save 46faab8ab153... ./malware.zip

# Quota check
malshare quota
# → Daily limit:   2000
# → Remaining:     1523

# Search by file type
malshare type "PE32 executable"

API Reference

Constructor

new MalShare(apiKey, config?)

| Param | Type | Default | Description | |-------|------|---------|-------------| | apiKey | string | required | API key from malshare.com/register.php | | config.baseUrl | string | https://malshare.com/api.php | API endpoint (mirror support) | | config.timeoutMs | number | 60000 | Request timeout (AbortController) | | config.fetch | function | globalThis.fetch | Custom fetch (CF Workers, proxy, etc.) |

Sample Listing (5 methods)

| Method | Returns | Description | |--------|---------|-------------| | listSamples() | string[] | SHA256 hashes from past 24h | | listSamplesRaw() | string | Same, raw text (one per line) | | listSources() | object[] | Sample sources with counts {source, count} | | listFileNames() | string[] | Original file names | | listTypes() | object[] | File types + counts {type, count} |

Sample Info & Search (4 methods)

| Method | Returns | Description | |--------|---------|-------------| | details(hash) | FileDetails \| null | Full metadata: MD5, SHA1, SHA256, type, size, name, sources, first/last seen | | hashLookup(hashes) | FileDetails[] | Bulk lookup via POST (max ~100 hashes) | | search(query) | string \| object | Search by hash, source, or file name | | searchByType(type) | string[] | Hashes by file type from past 24h |

Download & Upload (5 methods)

| Method | Returns | Description | |--------|---------|-------------| | download(hash) | Uint8Array | Raw sample bytes ⚠️ live malware | | downloadTo(hash, path) | {path, size} | Download + save to disk | | upload(file, name?) | UploadResult | Upload a sample (increases quota temporarily) | | downloadUrl(url, recursive?) | DownloadUrlResult | Submit URL for MalShare to crawl + add | | downloadUrlStatus(guid) | DownloadUrlStatus | Check URL download task progress |

Quota (1 method)

| Method | Returns | Description | |--------|---------|-------------| | getQuota() | QuotaInfo | {limit, remaining} |


Type Definitions

interface FileDetails {
  MD5: string;              // 32-char hex
  SHA1: string;             // 40-char hex
  SHA256: string;           // 64-char hex
  F_TYPE: string;           // 'PE32 executable', 'ELF 64-bit', 'PDF document'...
  F_SIZE: number;           // bytes
  F_NAME?: string;          // original filename
  SOURCES: string[];        // ['US', 'DE', 'JP', ...]
  FIRST_SEEN?: string;      // ISO timestamp
  LAST_SEEN?: string;
}

interface QuotaInfo {
  limit: number;            // allocated per day (default: 2000)
  remaining: number;        // left today
}

interface UploadResult {
  status: 'OK' | 'ERROR';
  guid?: string;            // upload tracking GUID
  error?: string;
}

interface DownloadUrlResult {
  status: 'OK' | 'ERROR';
  guid?: string;            // download task GUID
  error?: string;
}

interface DownloadUrlStatus {
  status: 'missing' | 'pending' | 'processing' | 'finished';
  guid?: string;
}

Usage Patterns

Batch Download by Type

const ms = new MalShare(process.env.MALSHARE_KEY);

// Get all PE32 samples from today
const peHashes = await ms.searchByType('PE32 executable');
console.log(`${peHashes.length} PE32 samples today`);

// Download first 10 (be careful!)
for (const hash of peHashes.slice(0, 10)) {
  const details = await ms.details(hash);
  const bytes = await ms.download(hash);
  await Bun.write(`./samples/${hash}.bin`, bytes);
  console.log(`Downloaded: ${details.F_NAME || hash} (${details.F_SIZE} bytes)`);
}

Daily Quota Monitor

const ms = new MalShare(process.env.MALSHARE_KEY);

// Check before heavy operations
const { limit, remaining } = await ms.getQuota();
if (remaining < 100) {
  console.warn(`Low quota: ${remaining}/${limit} — reset at midnight UTC`);
}
const used = (1 - remaining / limit) * 100;
console.log(`Quota: ${remaining}/${limit} (${used.toFixed(1)}% used)`);

Bulk Hash Lookup

const ms = new MalShare(process.env.MALSHARE_KEY);

// Look up up to 100 hashes at once
const hashes = ['abc123...', 'def456...', /* ... up to ~100 */];
const results = await ms.hashLookup(hashes);

for (const r of results) {
  console.log(`${r.SHA256?.substring(0, 12)} | ${r.F_TYPE} | ${r.F_NAME}`);
}

URL Submission for Crawling

const ms = new MalShare(process.env.MALSHARE_KEY);

// Submit a URL for MalShare to download
const { guid } = await ms.downloadUrl('http://evil.com/malware.exe');

// Poll until finished
let status = await ms.downloadUrlStatus(guid);
while (status.status === 'pending' || status.status === 'processing') {
  await new Promise(r => setTimeout(r, 5000));
  status = await ms.downloadUrlStatus(guid);
}
console.log('Task finished:', status.status);

Cloudflare Workers

import { MalShare } from 'malshare-sdk';

export default {
  async scheduled(event, env) {
    const ms = new MalShare(env.MALSHARE_KEY, {
      fetch: globalThis.fetch, // Workers-native fetch
    });
    const hashes = await ms.listSamples();
    console.log(`${hashes.length} new samples today`);
  }
};

Error Handling

All methods throw on HTTP errors (4xx/5xx), network failures, and timeouts.

try {
  const info = await ms.details(hash);
} catch (err) {
  if (err.message.includes('404')) {
    // Sample not found
  } else if (err.message.includes('429')) {
    // Rate limited — back off
    await new Promise(r => setTimeout(r, 60000));
  } else if (err.name === 'AbortError') {
    // Timeout — retry with longer timeout
  }
}

details() returns null for unknown hashes (doesn't throw).
listSamples() returns [] for empty responses.
searchByType() returns [] for unknown types.


Rate Limits & Fair Use

  • Free accounts: 2,000 API calls/day (resets at midnight UTC)
  • Uploading samples temporarily increases your quota
  • MalShare is an open-source project — be respectful
  • Honored: Retry-After headers on 429 responses
  • Recommended: 1-2 requests/second

Platform Compatibility

| Platform | Support | Notes | |----------|---------|-------| | Bun | ✅ Full | bun add malshare-sdk | | Node.js | ✅ Full | >=18.0.0 (global fetch) | | Deno | ✅ Full | import { MalShare } from 'npm:malshare-sdk' | | Cloudflare Workers | ✅ | Custom fetch, no downloadTo() | | Browser | ✅ | window.fetch, no downloadTo() | | Bun compile | ✅ | Single binary with bun build --compile |


Security

⚠️ WARNING: Samples from MalShare are live, unmodified malware.
Always work in sandboxed VMs. Never execute on your host.

  • Samples are stored as raw bytes — you choose the container
  • Password-protected zip convention: infected (MalwareBazaar standard)
  • No network requests during download (the binary is never executed by this SDK)
  • API key: store in env vars only, never commit

Architecture

┌──────────────────────────────────────────────┐
│                MalShare API                  │
│           malshare.com/api.php               │
└──────────────────────────────────────────────┘
                     ▲
                     │ fetch() + AbortController
┌────────────────────┴─────────────────────────┐
│              MalShare class                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐  │
│  │ Listing  │ │ Info     │ │ Download     │  │
│  │ list*()  │ │ details()│ │ download()   │  │
│  │          │ │ hashLook │ │ downloadTo() │  │
│  │          │ │ search() │ │ upload()     │  │
│  └──────────┘ └──────────┘ └──────────────┘  │
│  ┌──────────────────────────────────────┐    │
│  │         _request() — core            │    │
│  │  URL params → fetch → parse → return │    │
│  └──────────────────────────────────────┘    │
└──────────────────────────────────────────────┘

| Component | Responsibility | |-----------|---------------| | _request() | Build URL, fetch with AbortController, parse response | | MalShare class | Public API surface — all 14 methods delegate to _request() | | CLI (cli.js) | Thin wrapper: parse args → call SDK → format output |

Design decisions: See ADR-001 (TBD).


Tests

# Unit tests (mocked fetch, no API key needed)
bun test

# Integration tests (real API calls)
MALSHARE_KEY=your-key bun test tests/malshare.int.js

| Suite | Tests | Coverage | |-------|-------|----------| | Unit | 48 | All 14 public methods, error boundaries, regression | | Integration | 14 | Real API: list, details, search, download, quota | | URL construction | 10 | All action + parameter combinations |


License

MIT © gutem

MalShare is a free community service by @mal_share.