New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

@ismalicious/sdk

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ismalicious/sdk

Official JavaScript/TypeScript SDK for the isMalicious threat intelligence API

latest
Source
npmnpm
Version
0.5.0
Version published
Weekly downloads
320
10566.67%
Maintainers
1
Weekly downloads
 
Created
Source

@ismalicious/sdk

npm version npm downloads TypeScript License: MIT

Official JavaScript/TypeScript SDK for the isMalicious threat intelligence API.

Installation

npm install @ismalicious/sdk
# or
yarn add @ismalicious/sdk
# or
pnpm add @ismalicious/sdk

Quick Start

import { IsMalicious } from "@ismalicious/sdk";

const client = new IsMalicious({
  apiKey: "your-api-key",
  apiSecret: "your-api-secret",
});

// Check if a domain is malicious
const result = await client.check("suspicious-domain.com");
console.log(result.malicious); // true or false
console.log(result.reputation); // { malicious, suspicious, harmless, undetected } source counts
console.log(result.riskScore?.score); // 0-100 risk score

Note: Get your API key and secret from your isMalicious dashboard. The credentials are automatically Base64 encoded by the SDK for authentication.

Features

  • Full TypeScript support with comprehensive type definitions
  • Automatic retry with exponential backoff
  • Rate limit handling with built-in tracking
  • Multiple entity types: domains, IPs, file hashes, emails, phones, crypto wallets, and passwords (breach exposure, k-anonymity)
  • Enrichment levels: basic, standard, and full intelligence
  • Gate for AI agents: prompt-injection scan and link reputation (client.gate), on a separate scan meter
  • Mail scan: one inbound email message read for phishing and malware (client.mail), one scan of the same meter per message

Usage

Check a Domain or IP

// Basic check
const result = await client.check("example.com");

// With full enrichment
const enriched = await client.check("8.8.8.8", {
  enrichment: "full",
});

// Access detailed information
console.log(enriched.riskScore?.score); // Risk score 0-100
console.log(enriched.classification?.primary); // Threat category
console.log(enriched.confidence?.level); // 'low' | 'medium' | 'high'
console.log(enriched.evidence?.recommendedAction); // 'allow' | 'monitor' | 'review' | 'escalate' | 'block'

Explain a Verdict

const result = await client.check("example.com", {
  enrichment: "standard",
});

console.log(result.dataTrust?.freshness); // 'fresh' | 'recent' | 'stale' | 'unknown'
console.log(result.dataTrust?.sourceAgreement.level); // 'strong' | 'moderate' | 'weak' | 'none'
console.log(result.evidence?.reasons.slice(0, 3));
console.log(result.evidence?.recommendedAction);

Check a File Hash

// Supports MD5, SHA1, and SHA256
const hashResult = await client.checkHash("d41d8cd98f00b204e9800998ecf8427e");

console.log(hashResult.malicious);
console.log(hashResult.hashInfo?.fileType);
console.log(hashResult.hashInfo?.detectionStats);

Check a Password

Whether a password appears in known data breaches (Have I Been Pwned's Pwned Passwords corpus, over 2 billion hashes), and how many times.

// k-anonymity: hashed with SHA-1 here; only the first 5 hex digits are sent.
const result = await client.checkPassword(candidatePassword);
if (result.exposed) {
  console.log(`Seen ${result.count} times in breaches (${result.prevalence})`);
}

// From a hash you already hold (credential dump, AD audit). The full hash is
// sent; the API refuses a plaintext password.
await client.checkPasswordHash({ ntlm: "8846F7EAEE8FB117AD06BDD830B7586C" });

// The raw range, to match yourself.
const range = await client.getPasswordRange("5BAA6");

verdict: "not_found" means the password is not in known breach dumps; it says nothing about its strength. Hashing uses Web Crypto when the runtime has it, and a built-in SHA-1 otherwise (Node 18).

Analyze / Email / Phone / Crypto (web API)

These methods hit the Next.js host (https://ismalicious.com/api by default via webBaseUrl), not api.ismalicious.com.

const client = new IsMalicious({
  apiKey: "your-api-key",
  apiSecret: "your-api-secret",
  // optional override:
  // webBaseUrl: "https://ismalicious.com/api",
});

// Auto-detect type and route
const any = await client.analyze("user@example.com");

await client.checkEmail("user@example.com");
await client.checkPhone("+14155552671");
await client.checkCrypto("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");

Keep baseUrl (default https://api.ismalicious.com) for check, stream, monitoring, CVE, ransomware, and TAXII.

Bulk Check

const bulkResult = await client.checkBulk([
  "suspicious-domain.com",
  "8.8.8.8",
  "d41d8cd98f00b204e9800998ecf8427e",
]);

console.log(
  `${bulkResult.maliciousCount} of ${bulkResult.totalChecked} are malicious`,
);

for (const item of bulkResult.results) {
  console.log(`${item.entity}: ${item.malicious ? "MALICIOUS" : "clean"}`);
}
const searchResult = await client.search("phishing");
console.log(searchResult.hits); // Array of matching domains

Get Blocklist

Downloads pre-generated blocklists (GET /blocklist/download/blocklist-{ips|domains|urls|hashes}-{level|category}[-format].txt). Anonymous requests receive a 10% sample; a Basic plan or higher receives the full list.

// Full domain blocklist (default: entityType "domains", level "all")
const domains = await client.getBlocklist();

// Critical-level IPs
const criticalIps = await client.getBlocklist({
  entityType: "ips",
  level: "critical",
});

// Category lists, in hosts / AdGuard format (domain lists only)
const malwareHosts = await client.getBlocklist({
  category: "malware",
  format: "hosts",
});

const phishingAdguard = await client.getBlocklist({
  category: "phishing",
  format: "adguard",
});

Levels: critical, high, medium, low, all. Categories include malware, phishing, spam, botnet, ransomware, c2, … (see the BlocklistCategory type for the full list). The legacy type option is deprecated but still accepted: "full" maps to level "all", other values map to the category of the same name.

Configuration Options

const client = new IsMalicious({
  // Required: Your API key
  apiKey: "your-api-key",

  // Required: Your API secret
  apiSecret: "your-api-secret",

  // Optional: Canonical API host for automation (default: https://api.ismalicious.com)
  // Use this for check, monitoring, alerts, TAXII, CVE, webhooks CRUD, etc.
  baseUrl: "https://api.ismalicious.com",

  // Optional: Web API host for session-oriented / analyze paths
  // (default: https://ismalicious.com/api) — email/phone/crypto analyze,
  // webhook test + delivery history
  webBaseUrl: "https://ismalicious.com/api",

  // Optional: Request timeout in ms (default: 30000)
  timeout: 30000,

  // Optional: Number of retry attempts (default: 3)
  retries: 3,

  // Optional: Custom fetch implementation
  fetch: customFetch,
});

Check Options

interface CheckOptions {
  // Enrichment level: 'basic' | 'standard' | 'full'
  enrichment?: EnrichmentLevel;

  // @deprecated — the server ignores this parameter; it is no longer sent
  intelowl?: boolean;

  // @deprecated — the server ignores this parameter; checks are never saved
  // to the report history through /check. Use client.reports.save() instead.
  trackReports?: boolean;
}

Error Handling

import {
  IsMalicious,
  AuthenticationError,
  RateLimitError,
  ValidationError,
} from "@ismalicious/sdk";

try {
  const result = await client.check("example.com");
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Invalid API key");
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter} seconds`);
  } else if (error instanceof ValidationError) {
    console.error(`Invalid input: ${error.message}`);
  }
}

Rate Limiting

The SDK automatically tracks rate limit information from API responses:

// After making a request
const result = await client.check("example.com");

// Access rate limit info
const rateLimit = client.getRateLimitInfo();
console.log(
  `${rateLimit?.remaining} of ${rateLimit?.limit} requests remaining`,
);
console.log(`Resets at: ${rateLimit?.reset}`);

Response Types

CheckResponse

interface CheckResponse {
  malicious: boolean;
  // Per-source detection counts — the 0-100 score is riskScore.score
  reputation: {
    malicious: number;
    suspicious: number;
    harmless: number;
    undetected: number;
    timeout?: number;
  };
  apiVersion: string;
  enrichmentLevel?: "basic" | "standard" | "full";
  processingTime?: number;
  sources?: MaliciousSource[];
  riskScore?: RiskScore;
  classification?: ThreatClassification;
  confidence?: ConfidenceScore;
  dataTrust?: DataTrustProfile;
  evidence?: SocEvidence;
  // Threat listings only (see "Listing classes" below)
  blocklistHits?: number;
  blocklistListed?: boolean;
  // Non-threat listings split out of the verdict; absent when there are none
  infrastructure?: InfrastructureAttribution;
  geo?: GeoLocation;
  whois?: WhoisInfo;
  certificates?: CertificateInfo[];
  hashInfo?: HashInfo;
  mitre?: MITREMapping;
}

Listing classes

Every entry of sources[] carries an optional threatClass — "threat" (the default when absent), "infrastructure", "policy" or "allowlist" — and an optional fpRisk ("low" by default, "medium", "high"), the publisher's own false-positive warning. A Tor exit list, a cloud provider's published ranges or an ad-blocking list describe what the entity is or what a customer may choose to block; they are not a malicious verdict, so only "threat" listings count towards riskScore and blocklistHits (and, for hashes, malicious); a non-threat listing never raises malicious on a domain or an IP. The others are summarised under infrastructure:

interface InfrastructureAttribution {
  // Distinct, sorted: "tor-exit", "vpn", "proxy", "doh-resolver",
  // "dns-resolver", "sinkhole", "cloud", "cdn", "crawler", "scanner",
  // "monitoring", "disposable-email", "dynamic-dns", "url-shortener",
  // "bogon", "saas", "allowlist"
  attributes: InfrastructureAttribute[];
  sources: Array<{
    id?: string;
    name: string;
    category?: string;
    threatClass: ThreatClass;
  }>;
}

Both class types accept any string so that a class added to the registry before this SDK is updated still parses; compare against the known values rather than switching exhaustively.

const result = await client.check("13.107.6.152");
const threats = (result.sources ?? []).filter(
  (s) => (s.threatClass ?? "threat") === "threat",
);
if (threats.length === 0 && result.infrastructure) {
  console.log(
    `Not a threat; known as ${result.infrastructure.attributes.join(", ")}`,
  );
}

Modules

The SDK provides specialized modules for different API capabilities:

Monitoring

Watch and unwatch domains/IPs for threat notifications:

// Watch a domain
const watchResult = await client.monitoring.watch("suspicious-domain.com", {
  type: "domain",
});

// List watched entities
const watched = await client.monitoring.list();
console.log(watched.domains.length, watched.ips.length);

// Unwatch an entity (by watch-record ID, from list() or watch())
await client.monitoring.unwatch({ id: "watch-record-id", type: "domain" });

// Toggle notifications
await client.monitoring.toggleNotify({
  id: "watch-record-id",
  type: "domain",
  notifyOnChange: false,
});

AI Analysis

Get AI-powered threat analysis with MITRE ATT&CK mapping:

// Analyze check results (feed it the output of client.check)
const checkResult = await client.check("suspicious-domain.com", {
  enrichment: "full",
});
const analysis = await client.ai.analyze(checkResult);

console.log(analysis.summary);
console.log(analysis.techniques); // MITRE ATT&CK mappings
console.log(analysis.recommendations);

// Stream analysis in real-time
const stream = await client.ai.analyzeStream(checkResult, {
  onData: (event) => process.stdout.write(event.content),
  onComplete: () => console.log("\nAnalysis complete"),
  onError: (err) => console.error(err),
});

// Later, if needed:
stream.close();

Gate (isinjected) — scan before you act

Prompt-injection detection plus link reputation for AI agents, over the same 30M-indicator dataset. Scans are a separate meter from the request quota: scan() and url() never decrement the monthly /check allowance, and quota() reads the scan meter (Free 1,000, Basic 25,000, Pro 250,000, Enterprise 1,000,000 scans/month).

// Scan untrusted content (web page, email, tool result) before acting on it.
// One scan, plus one per unique link entity found in the content.
const scan = await client.gate.scan(pageText, {
  sourceUrl: "https://example.com/page",
});
console.log(scan.verdict); // "block" | "warn" | "allow"
console.log(scan.injection.families); // e.g. ["instruction_override"]
console.log(scan.links); // [{ url, entity, verdict, sources }]
if (scan.sanitized_content) useInstead(scan.sanitized_content);

// Reputation-only pre-fetch check of one URL, domain or IP (one scan)
const rep = await client.gate.url("https://evil.example/login");
console.log(rep.verdict); // "block" | "warn" | "allow"
// "allow" includes unknown URLs; it is not proof that a URL is safe.

// Remaining scan allowance — the scan meter, not the request quota
const q = await client.gate.quota();
console.log(`${q.remaining} of ${q.limit} scans left (${q.plan})`);

Over the scan allowance the API answers 429 with { error, usage, limit }, raised as RateLimitError.

Mail scan — read one message before it is delivered

One inbound email message against the dataset, for a mail system or an agent that already has its own detection (it is not a gateway). It reads the sender and Reply-To, the server that delivered the message, every link host and every attachment hash, the tells of a phishing message (link text that names another site, a domain or a display name that imitates a well-known brand, invoice.pdf.exe, a display name that shows another address) and prompt injection aimed at an AI reading the mail, and it reports what it could not check. It never runs an attachment, fetches a link or calls a third party while you wait.

One scan of the scan meter per message, however many links and attachments it carries, and never a request of the monthly quota; client.gate.quota() reads the same meter.

// A message you already parsed (Microsoft Graph, Gmail API…): headers, bodies,
// attachment digests. Attachment contents are not needed, and not read.
const result = await client.mail.scan(
  { message: { headers, text, html, attachments } },
  { authservId: "mx.corp.example" }, // what you know about your own system
);

console.log(result.verdict); // "malicious" | "suspicious" | "clean" | "inconclusive"
console.log(result.recommendedAction); // "quarantine" | "review" | "warn" | "deliver"
console.log(result.headline); // one sentence for a ticket or a chat
for (const reason of result.reasons) console.log(reason.code, reason.summary);
console.log(result.coverage.skipped); // what the scan could not check, and why

// Or the raw message (.eml), as text or base64 (up to 10 MiB): preferred when you
// have it, because attachments are then read for structure and a message
// attached to it is read as a message of its own.
await client.mail.scan({ eml: rawMessage });
await client.mail.scan({ emlBase64: base64Message });

How to read the answer:

  • malicious needs a listing in the dataset: the shape of a message alone asks for a review at most.
  • clean is a positive claim. It needs your own receiving system's DMARC pass: pass authservId (the id it writes in Authentication-Results) or trustAuthenticationResults: true, because anyone can write that header into a message. Without it, a message with nothing against it is inconclusive, which is not safe. Even with it, clean is kept for a sender domain the dataset knows as established (among the 100 000 most visited, not a free mailbox): attackers publish DMARC for the domains they register.
  • deliver means no objection from this scan. Never use it to release a message another engine quarantined.
  • trustedHops and connectingIp say which server delivered the message to yours; hosts in reasons[].evidence are defanged (evil[.]test).
  • From a raw message, attachments[].detectedType says what the bytes are and flags what was found in them (disguised_program, macro_project, remote_template, archive_risky, pdf_launch, html_smuggling…), never by running anything. Addresses found inside a file or an attached message are links of their own, tagged origin and originName. sender.posture.spoofable says nothing the sender's domain publishes would make a receiver reject a message forged with it.

Over the scan allowance the API answers 429 with { error, usage, limit }, raised as RateLimitError; a message over 10 MiB is refused with 413.

Threat Event Stream (deprecated)

Deprecated. client.stream subscribes to GET /stream, which the server backs with a simulated preview feed (threat_stream.rs, "mock threat feed") — the events are generated, not observed. The module stays for compile compatibility and may be removed in the next major. Use client.checkStream for live report enrichment or client.taxii for the real indicator feed.

Requires a Pro or Enterprise plan (the server answers 403 otherwise).

// Subscribe to the stream
const subscription = await client.stream.subscribe(
  {
    types: ["threat.new", "watchlist.alert"],
    severity: "high", // minimum severity (single value, not an array)
    categories: ["malware", "ransomware"],
  },
  {
    onEvent: (event) => {
      console.log(
        `${event.type}: ${event.data.entity} - ${event.data.category}`,
      );
    },
    onError: (error) => {
      console.error("Stream error:", error);
    },
    onClose: () => console.log("Stream closed"),
  },
);

// Later: close the subscription
subscription.close();

Ransomware Intelligence

Access ransomware victim data and statistics:

// Get ransomware feed
const feed = await client.ransomware.getFeed({
  limit: 50,
  groups: ["lockbit", "blackcat"],
});

// Get statistics
const stats = await client.ransomware.getStats();
console.log(`Total victims: ${stats.totalVictims}`);

// Check if a domain was a ransomware victim
const check = await client.ransomware.check("company.com");

CVE/Vulnerability Lookup

Search and retrieve CVE information:

// Search CVEs (severity is uppercase: CRITICAL | HIGH | MEDIUM | LOW | UNKNOWN)
const results = await client.cve.search("remote code execution", {
  severity: "CRITICAL",
  limit: 10,
});

// Get recent CVEs (days, limit)
const recent = await client.cve.getRecent(7, 20);

// Get specific CVE details
const cve = await client.cve.lookup("CVE-2024-1234");

Webhooks

Manage webhook notifications:

// Create a webhook. Valid events: threat.detected, monitor.alert,
// report.created, usage.warning, cve.finding.created,
// cve.finding.status_changed, case.updated, dataset.stale
const webhook = await client.webhooks.create({
  name: "My alerting webhook",
  url: "https://your-app.com/webhook",
  events: ["threat.detected", "monitor.alert"],
});
// The signing secret is only returned at creation time:
console.log(webhook.secret);

// List webhooks
const webhooks = await client.webhooks.list();

// Update webhook
await client.webhooks.update(webhook.id, {
  events: ["threat.detected"],
});

// Delete webhook
await client.webhooks.delete(webhook.id);

Reports

Save and export threat reports:

// Save a report
const saved = await client.reports.save({
  entity: "malicious-domain.com",
  type: "domain",
});
console.log(saved.id, saved.usage.current, saved.usage.limit);

// List saved reports
const reports = await client.reports.list();

// Export reports as JSON or CSV
const csv = await client.reports.export({ format: "csv" });

// Delete report
await client.reports.delete(saved.id);

TAXII (STIX/TAXII 2.1)

Access threat intelligence in STIX format:

// Get API root info
const apiRoot = await client.taxii.getApiRoot();

// List collections
const collections = await client.taxii.getCollections();

// Get objects from a collection. Collection IDs: malicious-ips,
// malicious-domains, malicious-urls, malicious-file-hashes,
// malicious-subdomains, c2-indicators, phishing-indicators, malware-iocs,
// ransomware-iocs, org-reported-ips, org-reported-domains,
// org-reported-file-hashes (see the TaxiiCollectionId type)
const objects = await client.taxii.getObjects("malicious-domains", {
  type: ["indicator", "malware"],
  limit: 100,
});

// Fetch a single object by STIX ID (server-side filter on the collection)
const { objects: matches } = await client.taxii.getObjects(
  "malicious-domains",
  { id: "indicator--12345" },
);

Requirements

  • Node.js 18+ (or provide a custom fetch implementation)

License

MIT

Keywords

threat-intelligence

FAQs

Package last updated on 01 Oct 2026

Related posts