Справочник API

JavaScript
import SpamScanner from 'spamscanner';             // ESM
const SpamScanner = require('spamscanner');        // CommonJS

const scanner = new SpamScanner(options);
const result = await scanner.scan(rawMessage, scanOptions);

Создайте один сканер и используйте его повторно: он загружает модель один раз и кеширует ответы DNS и языковой модели.

Параметры#

Любой параметр можно передать в конструктор. Большинство можно также передать в scan() для одного письма; они накладываются поверх параметров конструктора.

ПараметрПо умолчаниюЗначение
threshold5Оценка, при которой письмо считается спамом
rejectThreshold15Оценка, при которой action равно reject
scores{}Баллы для тестов: ключи настроек или имена тестов (тесты и баллы)
classifierвстроенная модельClassifier, объект модели, путь к файлу модели или false
classifierOptions{}Параметры встроенной модели (см. Classifier)
allowedLanguages[]Коды ISO 639-1; остальные языки получают LANGUAGE_NOT_ALLOWED
phishing.cloudflaretrueСпрашивать фильтрующие резолверы Cloudflare о хостах из ссылок
phishing.adulttrueТакже спрашивать семейный резолвер, который блокирует сайты для взрослых
phishing.maxHosts25Сколько хостов из ссылок проверяется в одном письме
phishing.homograph{}brands (замена встроенного списка), extraBrands, allowlist (домены, которые никогда не помечаются), strictMode
dnsbl{ip: [], domain: []}Зоны чёрных списков в DNS для IP клиента и для доменов из ссылок
dns{servers: null, timeout: 3000}Серверы имён для DNS-проверок (по умолчанию системные)
attachmentstrueПроверять вложения
macrostrueПомечать макросы, активные PDF и объекты RTF
arbitrarytrueПрименять правила
authenticationfalsetrue или {dnsServers, timeout, mta, weights}: SPF, DKIM, DMARC, ARC
reputationfalse{allowlist, denylist, apiUrl, headers, timeout}
allowlist, denylistIP-адреса, домены или адреса; краткая форма для reputation
clamavfalsetrue (сокет по умолчанию), {socket} или {host, port}
llmnullНастройки языковой модели с mode, minScore, maxScore
toxicityfalse{model, threshold = 0.7}: модель, чей classify([text]) отвечает как @tensorflow-models/toxicity
nsfwfalse{model, threshold = 0.6}: модель, чей classify(imageBuffer) отвечает как nsfwjs
maxLength100000Сколько символов текста письма читается
timeout10000Миллисекунды, отведённые на каждую сетевую проверку
session{}Сведения об SMTP-сессии по умолчанию

scan() также принимает session:

JavaScript
await scanner.scan(raw, {
  session: {
    remoteAddress: '203.0.113.5',             // the client's IP address
    resolvedClientHostname: 'mx.example.com', // its verified reverse DNS name
    helo: 'mx.example.com',
    envelope: {
      mailFrom: {address: 'alice@example.com'},
      rcptTo: [{address: 'bob@example.org'}],
    },
  },
});

Сервер, который уже проверил SPFSender Policy Framework: запись в DNS со списком серверов, которым разрешено отправлять почту от имени домена., DKIMDomainKeys Identified Mail: подпись письма, которая проверяется открытым ключом, опубликованным в DNS отправителя., DMARCПолитика в DNS, которая сообщает получателям, что делать с письмом от имени домена, не прошедшим выравнивание SPF и DKIM. и ARCAuthenticated Received Chain: подписи, которые переносят результаты аутентификации через пересылающие серверы. (с помощью mailauth), может передать результаты как authenticationResults, и тогда они оцениваются, а не проверяются повторно:

JavaScript
import {authenticate} from 'mailauth';

const auth = await authenticate(raw, {ip, helo, sender, mta});
await scanner.scan(raw, {authenticationResults: auth});

Методы#

МетодВозвращает
scan(source, options)Результат. source — Buffer, строка, Uint8Array или поток для чтения
scanFile(path, options)Результат для файла с письмом
learn(source, 'spam' | 'ham')Обучает классификатор на одном письме
unlearn(source, 'spam' | 'ham')Отменяет learn
saveModel(path, options)Записывает классификатор в файл (options: minCount, maxFeatures, metadata)
getClassifier()Используемый Classifier или null
getClassification(features)Вердикт классификатора для признаков из getFeatures
getFeatures(mail){features, words, language, script, links, hosts, text, hidden} письма
getTokens(text, locale)Слова текста
getTokensAndMailFromSource(source){tokens, features, mail}
parse(source){raw, mail}, где mail получен от mailparser

В getFeatures text — это текст, который показывает почтовый клиент и из которого читаются слова, а hidden — сколько символов текста скрывает HTML.

scanner.metrics содержит totalScans, averageTime и lastScanTime.

Результат#

JavaScript
{
  isSpam: true,
  score: 16.25,
  threshold: 5,
  rejectThreshold: 15,
  action: 'reject',                       // 'accept', 'tag' or 'reject'
  message: 'Spam (BAYES_999, PHISHING_LOOKALIKE_DOMAIN, DECEPTIVE_LINK, FROM_NAME_BRAND)',
  tests: [
    {name: 'BAYES_999', score: 6.25, description: 'Classifier spam probability 99.9%'},
    {name: 'PHISHING_LOOKALIKE_DOMAIN', score: 5, description: '"paypa1-secure.top" imitates paypal by swapping characters'},
    // ...
  ],
  results: {
    classification: {probability, category, spam, ham, clues, coverage},
    phishing: [],        // lookalike domains, deceptive links, Cloudflare and URIBL findings
    uncheckedHosts: 0,   // link hosts beyond phishing.maxHosts, not looked up
    attachments: [],     // every attachment finding
    executables: [],     // the executable findings among them
    macros: [],          // macros, active PDFs, RTF objects
    viruses: [],         // ClamAV findings: {filename, virus: [names], message}
    arbitrary: [],       // rules strong enough to mark spam alone
    obfuscation: {invisible, mixed, styled},
    authentication: null, // mailauth's results with a score, when checked
    reputation: null,
    dnsbl: [],           // {zone, value} for each listing
    language: {language, script, notAllowed},
    llm: null,           // the language model's verdict, when asked
    toxicity: [],
    nsfw: [],
    idnHomographAttack: {detected, domains, riskScore},
  },
  links: ['http://paypa1-secure.top/login'],
  language: 'en',
  tokens: ['dear', 'customer', /* ... */],
  mail: {/* the parsed message, from mailparser */},
  version: '7.0.0',
  metrics: {totalTime: 42},
}

Находки в phishing, attachments, executables, macros, viruses и arbitrary — объекты с полями type и message. String(finding) возвращает сообщение.

Экспорт#

JavaScript
import SpamScanner, {
  Classifier, getFeatures, segmentWords, detectLanguage,
  LLMClassifier, PROVIDERS, RECOMMENDED_MODELS, CLASSIFIER_MODELS,
  DEFAULT_SCORES, scoreResults, spamHeaders, rewriteMessage,
  loadModel, saveModel, defaultModelPath, loadDefaultModel,
  train, evaluate, readExamples,
  MilterServer, createHttpServer, createTcpServer, createSpamdServer,
  DEFAULTS, VERSION,
} from 'spamscanner';

import ArfParser from 'spamscanner/arf';

Classifier#

JavaScript
const classifier = new Classifier({spamCutoff: 0.99, hamCutoff: 0.2});
classifier.learn(getFeatures(mail).features, 'spam');
classifier.classify(features); // {probability, category, spam, ham, clues, coverage}
ПараметрПо умолчаниюЗначение
strength, unknown0.45, 0.5Параметры s и x Робинсона: насколько сильно редкие признаки притягиваются к 0,5
minDistance0.1Признаки, которые ближе к 0,5, чем это значение, игнорируются
maxClues150Сколько самых сильных признаков объединяется в одном письме
hamCutoff, spamCutoff0.2, 0.99Границы диапазона unsure
minLanguageExamples, languageShare1000, 0.02Сколько писем каждого класса нужно на языке для полной уверенности
languagePriortrueВзвешивать слова относительно счётчиков их собственного языка

Также доступны merge(other), toJSON(options), Classifier.fromJSON(json), size и entries().

Обучение#

JavaScript
import {train, evaluate, saveModel} from 'spamscanner';

const {classifier, spam, ham} = await train({
  spam: ['/mail/Junk'],
  ham: ['/mail/Archive', '/mail/archive.mbox'],
  datasets: [{file: 'extra.csv', textColumn: 'body', labelColumn: 'kind'}],
});
saveModel(classifier, 'model.json');

evaluate(classifier, examples) принимает элементы {label, features} или результат readExamples(sources), который читает те же источники, что и train, и возвращает счётчики, точность, полноту, F1, долю верных ответов, а также доли ложноположительных, ложноотрицательных и неуверенных ответов.

Серверы#

JavaScript
import SpamScanner, {MilterServer, createHttpServer, createSpamdServer} from 'spamscanner';

const scanner = new SpamScanner({authentication: true});
const milter = new MilterServer(scanner, {reject: true, rejectCode: 451, subjectTag: '[SPAM]'});
milter.on('scan', ({session, result}) => console.log(session.remoteAddress, result.score));
await milter.listen(7831);

createHttpServer(scanner, {token: process.env.SPAMSCANNER_TOKEN}).listen(7832, '127.0.0.1');
createSpamdServer(scanner).listen(783, '127.0.0.1');

Отчёты ARF#

spamscanner/arf читает и создаёт отчёты о злоупотреблениях (RFC 5965) — формат, в котором почтовые провайдеры сообщают о жалобах на спам:

JavaScript
import ArfParser from 'spamscanner/arf';

const report = await ArfParser.parse(rawReport);
console.log(report.feedbackType, report.sourceIp, report.originalHeaders.subject);

const raw = ArfParser.create({
  feedbackType: 'abuse', userAgent: 'MyService/1.0',
  from: 'abuse@example.com', to: 'fbl@example.net',
  originalMessage, sourceIp: '192.0.2.1',
});

ArfParser.tryParse() возвращает null вместо исключения для писем, которые не являются отчётами, а isArfMessage(mail) проверяет разобранное письмо.

Редактировать эту страницу на GitHub · Markdown ·