
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
Official Metigan SDK - Email, Forms, Contacts, and Audiences management with built-in security features
The official Metigan library for Node.js and browsers. Send emails, manage forms, contacts, and audiences with ease.
npm install metigan
# or
yarn add metigan
import Metigan from 'metigan';
// Initialize the client with all features
const metigan = new Metigan({
apiKey: 'your-api-key'
});
// Send email
await metigan.email.sendEmail({
from: 'Your Company <noreply@yourcompany.com>',
recipients: ['customer@email.com'],
subject: 'Welcome!',
content: '<h1>Hello!</h1><p>Thank you for signing up.</p>'
});
// Submit form
await metigan.forms.submit({
formId: 'form-123',
data: {
email: 'user@email.com',
name: 'John Doe'
}
});
// Create contact
await metigan.contacts.create({
email: 'new@email.com',
firstName: 'Jane',
audienceId: 'audience-456'
});
const response = await metigan.email.sendEmail({
from: 'Your Company <noreply@yourcompany.com>',
recipients: ['recipient@email.com'],
subject: 'Email Subject',
content: '<h1>HTML Content</h1>'
});
import fs from 'fs';
const response = await metigan.email.sendEmail({
from: 'company@email.com',
recipients: ['customer@email.com'],
subject: 'Important Document',
content: 'Please find the document attached.',
attachments: [
{
buffer: fs.readFileSync('./document.pdf'),
originalname: 'document.pdf',
mimetype: 'application/pdf'
}
]
});
await metigan.email.sendEmail({
from: 'company@email.com',
recipients: ['main@email.com'],
subject: 'Meeting',
content: 'Email content',
cc: ['copy@email.com'],
bcc: ['hidden-copy@email.com'],
replyTo: 'reply-here@email.com'
});
const response = await metigan.forms.submit({
formId: 'form-123', // or form slug
data: {
'field-email': 'user@email.com',
'field-name': 'John Doe',
'field-message': 'Hello, I would like more information.'
}
});
console.log(response.message); // "Thank you for your submission!"
// By slug (for public display)
const form = await metigan.forms.getPublicForm('my-form');
console.log(form.title);
console.log(form.fields);
const { forms, pagination } = await metigan.forms.listForms({
page: 1,
limit: 10
});
forms.forEach(form => {
console.log(`${form.title} - ${form.analytics?.submissions || 0} responses`);
});
const newForm = await metigan.forms.createForm({
title: 'Contact Form',
description: 'Get in touch with us',
fields: [
{
id: 'field-email',
type: 'email',
label: 'Your Email',
required: true
},
{
id: 'field-name',
type: 'text',
label: 'Your Name',
required: true
},
{
id: 'field-subject',
type: 'select',
label: 'Subject',
options: ['Support', 'Sales', 'Partnerships']
},
{
id: 'field-message',
type: 'textarea',
label: 'Message',
required: true
}
],
settings: {
successMessage: 'Thank you! We will get back to you soon.',
notifyEmail: 'contact@company.com'
}
});
const { publishedUrl, slug } = await metigan.forms.publishForm('form-123', 'contact');
console.log(`Form published at: ${publishedUrl}`);
const analytics = await metigan.forms.getAnalytics('form-123');
console.log(`Views: ${analytics.views}`);
console.log(`Submissions: ${analytics.submissions}`);
console.log(`Conversion rate: ${analytics.conversionRate}%`);
const contact = await metigan.contacts.create({
email: 'new@email.com',
firstName: 'Jane',
lastName: 'Doe',
audienceId: 'audience-123',
tags: ['customer', 'newsletter']
});
// By ID
const contact = await metigan.contacts.get('contact-456');
// By email
const contact = await metigan.contacts.getByEmail('jane@email.com', 'audience-123');
const updated = await metigan.contacts.update('contact-456', {
firstName: 'Jane Marie',
tags: ['customer', 'vip']
});
// Unsubscribe
await metigan.contacts.unsubscribe('contact-456');
// Resubscribe
await metigan.contacts.subscribe('contact-456');
// Add tags
await metigan.contacts.addTags('contact-456', ['vip', 'black-friday']);
// Remove tags
await metigan.contacts.removeTags('contact-456', ['test']);
const { contacts, pagination } = await metigan.contacts.list({
audienceId: 'audience-123',
status: 'subscribed',
tag: 'customer',
page: 1,
limit: 50
});
const result = await metigan.contacts.bulkImport(
[
{ email: 'john@email.com', firstName: 'John' },
{ email: 'jane@email.com', firstName: 'Jane' },
{ email: 'peter@email.com', firstName: 'Peter', tags: ['vip'] }
],
'audience-123'
);
console.log(`Imported: ${result.imported}`);
console.log(`Failed: ${result.failed}`);
const results = await metigan.contacts.search('doe', 'audience-123');
const audience = await metigan.audiences.create({
name: 'Main Newsletter',
description: 'Main subscriber list'
});
const { audiences, pagination } = await metigan.audiences.list({
page: 1,
limit: 10
});
audiences.forEach(audience => {
console.log(`${audience.name}: ${audience.count} contacts`);
});
const stats = await metigan.audiences.getStats('audience-123');
console.log(`Total: ${stats.total}`);
console.log(`Subscribed: ${stats.subscribed}`);
console.log(`Unsubscribed: ${stats.unsubscribed}`);
console.log(`Bounced: ${stats.bounced}`);
// Remove bounced and unsubscribed contacts
const { removed } = await metigan.audiences.clean('audience-123');
console.log(`${removed} contacts removed`);
// Merge source into target (source is deleted)
const merged = await metigan.audiences.merge(
'source-audience-id',
'target-audience-id'
);
const metigan = new Metigan({
apiKey: 'your-api-key',
// User ID for logging
userId: 'user-123',
// Disable logging
disableLogs: true,
// Timeout in milliseconds
timeout: 60000,
// Number of retries on failure
retryCount: 5,
// Delay between retries (ms)
retryDelay: 2000,
// Security options (see Security section)
debug: false,
sanitizeHtml: true,
enableRateLimit: true,
maxRequestsPerSecond: 10
});
The SDK includes built-in security features:
Automatically removes dangerous HTML tags and attributes from email content to prevent XSS attacks:
// HTML sanitization is enabled by default
const metigan = new Metigan({
apiKey: 'your-api-key',
sanitizeHtml: true // default
});
// Dangerous content is automatically sanitized
await metigan.email.sendEmail({
from: 'company@email.com',
recipients: ['user@email.com'],
subject: 'Newsletter',
content: '<h1>Hello</h1><script>alert("xss")</script>' // Script tag will be removed
});
Prevents accidental API abuse with built-in rate limiting:
const metigan = new Metigan({
apiKey: 'your-api-key',
enableRateLimit: true, // default
maxRequestsPerSecond: 10 // default
});
// Check rate limit before making requests
if (metigan.email.canMakeRequest()) {
await metigan.email.sendEmail({...});
} else {
const waitTime = metigan.email.getTimeUntilNextRequest();
console.log(`Please wait ${waitTime}ms before next request`);
}
// Reset rate limiter if needed
metigan.email.resetRateLimit();
Validates file attachments for security:
import { isAllowedMimeType, isSafeFileExtension, ALLOWED_MIME_TYPES } from 'metigan';
// Check if file type is safe
const filename = 'document.pdf';
const mimetype = 'application/pdf';
if (isSafeFileExtension(filename) && isAllowedMimeType(mimetype)) {
// Safe to attach
}
// View allowed MIME types
console.log(ALLOWED_MIME_TYPES);
Email addresses and subjects are automatically sanitized to prevent header injection attacks:
import { sanitizeEmail, sanitizeSubject } from 'metigan';
// Remove newlines and dangerous characters
const safeEmail = sanitizeEmail('user@email.com\r\nBcc: hacker@evil.com');
const safeSubject = sanitizeSubject('Subject\r\nFrom: hacker@evil.com');
Enable debug mode for troubleshooting (logs are hidden by default in production):
const metigan = new Metigan({
apiKey: 'your-api-key',
debug: true // Shows internal logs
});
// Or enable/disable at runtime
metigan.email.enableDebug();
metigan.email.disableDebug();
For advanced use cases, you can create your own rate limiter:
import { RateLimiter } from 'metigan';
const limiter = new RateLimiter({
maxRequests: 5,
windowMs: 1000 // 5 requests per second
});
if (limiter.tryRequest()) {
// Request allowed and recorded
await makeApiCall();
} else {
// Rate limited
console.log(`Wait ${limiter.getTimeUntilNextRequest()}ms`);
}
If you only need a specific module:
import { MetiganForms, MetiganContacts, MetiganAudiences } from 'metigan';
// Forms only
const forms = new MetiganForms({
apiKey: 'your-api-key'
});
// Contacts only
const contacts = new MetiganContacts({
apiKey: 'your-api-key'
});
// Audiences only
const audiences = new MetiganAudiences({
apiKey: 'your-api-key'
});
<script src="https://unpkg.com/metigan/dist/index.js"></script>
<script>
const metigan = new Metigan({ apiKey: 'your-api-key' });
// Submit form
document.getElementById('my-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
try {
const response = await metigan.forms.submit({
formId: 'form-123',
data
});
alert(response.message);
} catch (error) {
alert('Error submitting: ' + error.message);
}
});
</script>
import { MetiganError, ValidationError, ApiError } from 'metigan';
try {
await metigan.email.sendEmail({
from: 'invalid',
recipients: [],
subject: '',
content: ''
});
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid data:', error.message);
} else if (error instanceof ApiError) {
console.error(`API error (${error.status}):`, error.message);
} else if (error instanceof MetiganError) {
console.error('Metigan error:', error.message);
} else {
console.error('Unknown error:', error);
}
}
The library includes full TypeScript definitions:
import Metigan, {
EmailOptions,
FormConfig,
Contact,
Audience,
FormFieldType
} from 'metigan';
const emailOptions: EmailOptions = {
from: 'company@email.com',
recipients: ['customer@email.com'],
subject: 'Test',
content: '<p>Content</p>'
};
const fieldType: FormFieldType = 'email';
MIT © Metigan
FAQs
Official Metigan SDK - Email, Forms, Contacts, and Audiences management with built-in security features
The npm package metigan receives a total of 3 weekly downloads. As such, metigan popularity was classified as not popular.
We found that metigan demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.