MailScript Language Reference
MailScript is a powerful email filtering and automation scripting language based on Starlark (Python-like syntax). Create sophisticated email processing rules with 51 built-in functions for filtering, routing, security, and network validation.
Table of Contents
Overview
Scripts are executed for each incoming email message. You can define an evaluate() function as the main entry point,
or write code at the root level. The script has access to email headers, body content, and metadata through built-in functions.
Language Features
MailScript is built on Starlark, a Python-like language with support for:
- Data Types: Strings, integers, floats, booleans, lists, dictionaries
- Lists (Arrays): Dynamic arrays with indexing, iteration, and list comprehensions
- Dictionaries: Associative arrays (key-value pairs)
- Loops: For loops (while loops not supported for safety)
- Conditionals: if-elif-else statements
- Functions: Define reusable functions
- String Operations: Concatenation, case conversion, searching
Example: Lists and Loops
spam_keywords = ["viagra", "cialis", "lottery"]
for keyword in spam_keywords:
if search_body(keyword):
quarantine()
return
Example: Dictionaries
spam_scores = {
"viagra": 10.0,
"lottery": 8.5,
"winner": 7.0
}
for keyword, score in spam_scores.items():
if search_body(keyword):
add_header("X-Spam-Match", keyword)
add_header("X-Spam-Score", str(score))
Message Actions15 functions
Accept and deliver the message to the inbox.
accept()
Silently drop the message without delivering it.
discard()
Forcefully drop the message without processing.
drop()
Bounce the message back to the sender.
if getspamscore() > 9.0:
bounce()
Move the message to quarantine for manual review.
if getvirusstatus() == "infected":
quarantine()
Move the message to a specific folder.
fileinto("Spam")
fileinto("Archive/2024")
Add the message to the next digest email.
True if successfulif add_to_next_digest():
log_entry("Added to digest")
Send an automated reply to the sender.
auto_reply("Thank you for your email. We will respond within 24 hours.")
Reply with a specific SMTP error code.
reply_with_smtp_error(550) # Mailbox unavailable
Reply with an SMTP Delivery Status Notification.
reply_with_smtp_dsn("5.7.1") # Delivery not authorized
Header Operations
Get the value of an email header.
sender = get_header("From")
subject = get_header("Subject")
Add a custom header to the message.
add_header("X-MailScript", "processed")
add_header("X-Spam-Score", "7.5")
Content Search & Pattern Matching
Search for text in the message body (literal match).
True if found, False otherwiseif search_body("viagra"):
fileinto("Spam")
Test if text matches a regular expression pattern.
True if matches, False otherwisesender = get_header("From")
if regex_match(".*@trusted\\.com$", sender):
accept()
Message Metadata7 functions
Get the spam score of the message (0.0 to 10.0).
score = getspamscore()
if score > 7.0:
fileinto("Spam")
Get the virus scan status of the message.
if getvirusstatus() == "infected":
quarantine()
Get the MIME type of the message.
mime = getmimetype()
if mime == "application/pdf":
add_header("X-Has-PDF", "true")
Get the size of the email body in bytes.
bs = body_size()
if bs > 10485760: # 10MB
reply_with_smtp_error(552)
drop()
Get the size of the email headers in bytes.
hs = header_size()
if hs > 102400: # 100KB
log_entry("Large headers detected")
Get the number of envelope senders.
n = num_envelope()
if n > 10:
log_entry("Suspicious: multiple envelope senders")
quarantine()
Routing Actions
Redirect the message to a different email address.
if regex_match(".*(invoice|payment).*", subject):
divert_to("accounting@example.com")
Send a copy of the message to another address for screening/review.
if getspamscore() > 5.0:
screen_to("admin@example.com")
Route the message to another mail server for additional processing.
force_second_pass("priority-server.example.com")
Security Controls
Bypass malware scanning for messages from a specific sender.
if regex_match(".*@trusted\\.com$", sender):
skip_malware_check(sender)
Bypass spam filtering for messages from a specific sender.
skip_spam_check("newsletter@company.com")
Bypass whitelist checking for a specific IP address.
skip_whitelist_check("192.168.1.100")
Data Loss Prevention
Set Data Loss Prevention policy for the message.
if search_body("confidential"):
set_dlp("always", "domain")
Skip DLP checks for the message.
skip_dlp("sometimes", "user")
DNS & Network Functions13 functions
Get the IP address of the message sender.
sender_ip = get_sender_ip()
log_entry("Message from IP: " + sender_ip)
Get the domain of the message sender.
sender_domain = get_sender_domain()
add_header("X-Sender-Domain", sender_domain)
Check if a domain has valid DNS records.
True if DNS resolvesif not dns_check(sender_domain):
reply_with_smtp_error(550)
drop()
Check if an IP address is listed in a Real-time Blackhole List (RBL).
True if listedsender_ip = get_sender_ip()
if rbl_check(sender_ip, "zen.spamhaus.org"):
quarantine()
Check if a domain has valid MX (Mail Exchange) records.
True if valid MX records existif not valid_mx(sender_domain):
reply_with_smtp_error(550)
bounce()
Get all MX records for a domain.
mx_records = get_mx_records(sender_domain)
for mx in mx_records:
log_entry("MX: " + mx)
Received Headers Analysis
Get a specific Received header at the given level (0 = most recent hop).
# Check third hop for spam relay patterns
third_hop = check_received_header(2)
if "suspicious-relay.com" in third_hop:
quarantine()
Get all Received headers as a list (most recent to oldest).
received = get_received_headers()
num_hops = len(received)
if num_hops > 10:
log_entry("Suspicious: " + str(num_hops) + " mail hops")
quarantine()
Complete Examples
Example 1: Comprehensive Spam Filter
def evaluate():
sender = get_header("From")
sender_ip = get_sender_ip()
sender_domain = get_sender_domain()
# DNS validation
if not dns_check(sender_domain):
reply_with_smtp_error(550)
drop()
return
# RBL check
if rbl_check(sender_ip, "zen.spamhaus.org"):
quarantine()
return
# MX validation
if not valid_mx(sender_domain):
bounce()
return
# Spam score check
if getspamscore() > 8.0:
quarantine()
return
# Virus check
if getvirusstatus() == "infected":
quarantine()
return
# Accept clean mail
accept()
Example 2: Smart Routing
def evaluate():
subject = get_header("Subject")
sender = get_header("From")
# Route invoices to accounting
if regex_match(".*(invoice|payment|billing).*", subject.lower()):
divert_to("accounting@example.com")
return
# Trusted senders bypass checks
if regex_match(".*@trusted\\.com$", sender):
skip_spam_check(sender)
skip_malware_check(sender)
accept()
return
# Default processing
accept()
Example 3: Advanced Network Validation
def evaluate():
sender_domain = get_sender_domain()
sender_ip = get_sender_ip()
# Get all received headers
received = get_received_headers()
# Too many hops = suspicious
if len(received) > 10:
log_entry("Suspicious relay chain: " + str(len(received)) + " hops")
quarantine()
return
# Check for known spam relays
for hop in received:
if "known-spammer.com" in hop:
drop()
return
# Validate MX records and check for RBL
if valid_mx(sender_domain):
mx_records = get_mx_records(sender_domain)
for mx in mx_records:
log_entry("MX record: " + mx)
else:
bounce()
return
# Check if MX servers are in RBL
if mx_in_rbl(sender_domain, "zen.spamhaus.org"):
add_header("X-MX-Blacklisted", "true")
quarantine()
return
accept()
Security Note: Scripts run in a sandboxed Starlark environment with no file system or network access. Regular expressions are evaluated with timeouts to prevent ReDoS attacks.