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.
Maintainers
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.
Quick Start
bun add malshare-sdk # or: npm install malshare-sdkGet a free API key at malshare.com/register.php. Free accounts get 2,000 API calls/day.
export MALSHARE_KEY=your-key-hereimport { 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-Afterheaders 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.
