MAS-WP-0004: Postfix integration profile, corpus probe in pytest, Exim notes
Profile in integrations/postfix (install.sh + README), now used by the live deployment. Probe runs every conformance-corpus local-part and the relay negatives against it; tests/test_postfix_profile.py runs it where Docker is available. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 5787@bnt-lap001 Assistant-Session: 5db26b82-138b-40cc-9856-4d5eb0f752fd
This commit is contained in:
parent
082d4c39eb
commit
d766d61e7f
12 changed files with 1029 additions and 98 deletions
|
|
@ -1,7 +1,7 @@
|
|||
# Real Postfix -> pipe transport -> mas-gateway -> reference ticket service.
|
||||
FROM debian:bookworm-slim
|
||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
postfix python3 python3-jsonschema ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
postfix opendkim opendmarc publicsuffix python3 python3-jsonschema ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
RUN useradd -r -s /usr/sbin/nologin mas
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
EXPOSE 25 8024 8025
|
||||
|
|
|
|||
|
|
@ -7,14 +7,20 @@ probe can tell whether Postfix rewrote anything.
|
|||
|
||||
```bash
|
||||
docker build -t mas-postfix-probe experiments/postfix-probe
|
||||
python3 experiments/postfix-probe/probe.py --profile mas-profile --corpus # the shipped profile
|
||||
python3 experiments/postfix-probe/probe.py --profile defaults # relay domain
|
||||
python3 experiments/postfix-probe/probe.py --profile local-domain # domain in mydestination
|
||||
python3 experiments/postfix-probe/probe.py --profile local-domain-safe # + swap_bangpath/percent_hack off
|
||||
```
|
||||
|
||||
The other profiles are `defaults-local` and `mas-safe-local`. The repo is
|
||||
mounted read-only. In `results/` a `=` mark means the address arrived
|
||||
unchanged and `!` means it was rewritten. Findings are in
|
||||
`mas-profile` runs the shipped integration profile
|
||||
(`integrations/postfix/`) with `--corpus` (every conformance-corpus
|
||||
local-part), plus relay attempts that must be refused. `pytest` runs it as
|
||||
`tests/test_postfix_profile.py` when Docker is available. The other
|
||||
experiment profiles are `defaults-local` and `mas-safe-local`. The repo is
|
||||
mounted read-only. In the output, `=` means the address arrived unchanged,
|
||||
`~` means only its RFC 5321 quoting changed, `!` means it was rewritten, and
|
||||
`R` means a relay attempt was refused. Findings are in
|
||||
`docs/experiment-review.md` under *Email infrastructure*.
|
||||
|
||||
The Postfix side is the `master.cf` entry in `entrypoint.sh`:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,24 @@ exec env PYTHONPATH=/mas python3 -m mas_gateway.main "$@"
|
|||
SH
|
||||
chmod +x /usr/local/bin/mas-gateway
|
||||
|
||||
# mas-profile: the shipped integration profile (integrations/postfix), as
|
||||
# deployed live. Relaying is loopback-only, so the probe client is an ordinary
|
||||
# outside sender. The other profiles below are the MAS-WP-0002 experiments.
|
||||
if [ "$PROFILE" = "mas-profile" ]; then
|
||||
postconf -e "maillog_file=/dev/stdout" "compatibility_level=3.6"
|
||||
sh /mas/integrations/postfix/install.sh --domain great-service.example \
|
||||
--statute /mas/examples/statute/great-service.example/.well-known/mas \
|
||||
--app-url http://127.0.0.1:8024/mas-events --auth
|
||||
opendkim -f -x /etc/opendkim.conf &
|
||||
opendmarc -f -c /etc/opendmarc.conf &
|
||||
mkdir -p /var/lib/tickets && chown mas /var/lib/tickets
|
||||
su -s /bin/sh mas -c "PYTHONPATH=/mas python3 /mas/examples/ticket_service/ticket_service.py \
|
||||
--db /var/lib/tickets/t.sqlite3 --listen 0.0.0.0:8025" &
|
||||
python3 /mas/experiments/postfix-probe/capture.py http://127.0.0.1:8025/mas-events &
|
||||
echo "PROFILE=mas-profile"
|
||||
exec postfix start-fg
|
||||
fi
|
||||
|
||||
postconf -e "maillog_file=/dev/stdout" "myhostname=mx.probe.test" "mydestination=" \
|
||||
"relay_domains=great-service.example" "transport_maps=inline:{great-service.example=mas:}" \
|
||||
"mynetworks=0.0.0.0/0" "smtpd_relay_restrictions=permit_mynetworks,reject" \
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
python experiments/postfix-probe/probe.py --profile defaults
|
||||
python experiments/postfix-probe/probe.py --profile defaults-local
|
||||
python experiments/postfix-probe/probe.py --profile mas-safe-local
|
||||
python experiments/postfix-probe/probe.py --profile mas-profile --corpus
|
||||
|
||||
Starts the mas-postfix-probe container, sends one message per address via
|
||||
SMTP, then compares the envelope recipient Postfix handed to mas-gateway
|
||||
|
|
@ -38,9 +39,33 @@ CASES = [
|
|||
("quoted local-part", '"ticket=PR42"'),
|
||||
("unknown resource", "ticket=DOES-NOT-EXIST"),
|
||||
]
|
||||
# Relay attempts: the MAS profile must refuse all of them (not an open relay).
|
||||
RELAY_NEGATIVES = ["x@other.example", "x!y@other.example", "a@other.example@" + D,
|
||||
"a@" + D + "@other.example", "x%y@other.example"]
|
||||
|
||||
|
||||
def run(profile, port):
|
||||
|
||||
|
||||
def unquoted(addr):
|
||||
"""Strip RFC 5321 quoting. Postfix hands ${recipient} over in canonical
|
||||
form: needless quotes dropped, quotes added around non-dot-atoms."""
|
||||
local, _, dom = (addr or "").rpartition("@")
|
||||
if len(local) > 1 and local[0] == local[-1] == '"':
|
||||
local = local[1:-1]
|
||||
return f"{local}@{dom}"
|
||||
|
||||
|
||||
def corpus_cases():
|
||||
"""Every distinct local-part of the shared conformance corpus."""
|
||||
seen, out = set(), []
|
||||
for c in json.loads((ROOT / "mas_conformance/addresses.json").read_text()):
|
||||
if c["local_part"] not in seen:
|
||||
seen.add(c["local_part"])
|
||||
out.append((f"corpus {c['id']}", c["local_part"]))
|
||||
return out
|
||||
|
||||
|
||||
def run(profile, port, corpus=False):
|
||||
name = f"mas-probe-{profile}"
|
||||
subprocess.run(["docker", "rm", "-f", name], capture_output=True)
|
||||
subprocess.run(["docker", "run", "-d", "--name", name, "-e", f"PROFILE={profile}",
|
||||
|
|
@ -55,7 +80,7 @@ def run(profile, port):
|
|||
except OSError:
|
||||
time.sleep(0.5)
|
||||
results, tid = [], None
|
||||
for label, lp in CASES:
|
||||
for label, lp in CASES + (corpus_cases() if corpus else []):
|
||||
rcpt_lp = lp.replace("{ID}", tid or "MISSING")
|
||||
rcpt = f"{rcpt_lp}@{D}"
|
||||
msg = (f"From: probe@example.net\r\nTo: {rcpt}\r\nSubject: {label}\r\n"
|
||||
|
|
@ -65,7 +90,7 @@ def run(profile, port):
|
|||
with smtplib.SMTP("127.0.0.1", port, timeout=10) as s:
|
||||
refused = s.sendmail("probe@example.net", [rcpt], msg)
|
||||
smtp = "accepted" if not refused else f"refused {refused}"
|
||||
except smtplib.SMTPException as e:
|
||||
except (smtplib.SMTPException, UnicodeEncodeError) as e:
|
||||
smtp = f"rejected: {e}"
|
||||
got = wait_event(port, before) if smtp == "accepted" else None
|
||||
arrived = got["event"]["recipient"] if got else None
|
||||
|
|
@ -75,9 +100,19 @@ def run(profile, port):
|
|||
tid = app.get("ticket_id")
|
||||
results.append({"label": label, "sent": rcpt, "smtp": smtp, "arrived_as": arrived,
|
||||
"unchanged": arrived == rcpt,
|
||||
"equivalent": arrived is not None and unquoted(arrived) == unquoted(rcpt),
|
||||
"mas_valid": mas.get("valid"), "kind": mas.get("kind"),
|
||||
"app_status": got["app_status"] if got else None,
|
||||
"app": (app or {}).get("status") if isinstance(app, dict) else app})
|
||||
for rcpt in RELAY_NEGATIVES:
|
||||
try:
|
||||
with smtplib.SMTP("127.0.0.1", port, timeout=10) as s:
|
||||
refused = s.sendmail("probe@example.net", [rcpt], f"Subject: relay\r\n\r\nx\r\n")
|
||||
smtp = "accepted" if not refused else f"refused {refused}"
|
||||
except smtplib.SMTPException as e:
|
||||
smtp = f"rejected: {e}"
|
||||
results.append({"label": "relay attempt", "sent": rcpt, "smtp": smtp, "arrived_as": None,
|
||||
"unchanged": False, "relay_refused": smtp != "accepted"})
|
||||
logs = subprocess.run(["docker", "logs", name], capture_output=True, text=True)
|
||||
return results, logs.stdout + logs.stderr
|
||||
finally:
|
||||
|
|
@ -101,16 +136,18 @@ def wait_event(port, before, timeout=15):
|
|||
|
||||
if __name__ == "__main__":
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--profile", choices=["defaults", "defaults-local", "mas-safe-local", "local-domain", "local-domain-safe"], default="defaults")
|
||||
ap.add_argument("--profile", choices=["mas-profile", "defaults", "defaults-local", "mas-safe-local",
|
||||
"local-domain", "local-domain-safe"], default="mas-profile")
|
||||
ap.add_argument("--port", type=int, default=2525)
|
||||
ap.add_argument("--corpus", action="store_true", help="also send every conformance-corpus local-part")
|
||||
ap.add_argument("--json", metavar="FILE")
|
||||
ap.add_argument("--log", metavar="FILE")
|
||||
a = ap.parse_args()
|
||||
results, log = run(a.profile, a.port)
|
||||
results, log = run(a.profile, a.port, a.corpus)
|
||||
for r in results:
|
||||
mark = "=" if r["unchanged"] else "!"
|
||||
mark = ("R" if r["relay_refused"] else "X") if "relay_refused" in r else ("=" if r["unchanged"] else "~" if r["equivalent"] else "!")
|
||||
print(f"{mark} {r['label']:<22} {r['sent']:<52} -> {r['arrived_as'] or r['smtp']}"
|
||||
f" [{r['kind']}/{r['app']}]")
|
||||
f" [{r.get('kind')}/{r.get('app')}]")
|
||||
if a.json:
|
||||
Path(a.json).write_text(json.dumps({"profile": a.profile, "results": results}, indent=2) + "\n")
|
||||
if a.log:
|
||||
|
|
|
|||
573
experiments/postfix-probe/results/mas-profile.json
Normal file
573
experiments/postfix-probe/results/mas-profile.json
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
{
|
||||
"profile": "mas-profile",
|
||||
"results": [
|
||||
{
|
||||
"label": "P2 intake",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "resource",
|
||||
"sent": "ticket=gs-aktbdkka1q@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=gs-aktbdkka1q@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "resource + facet",
|
||||
"sent": "ticket=gs-aktbdkka1q+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=gs-aktbdkka1q+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "extra '=' in id",
|
||||
"sent": "ticket==X1@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket==X1@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "registry function",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "person-style",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "upper-case local-part",
|
||||
"sent": "Ticket=gs-aktbdkka1q@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=gs-aktbdkka1q@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "percent data",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "percent hack form",
|
||||
"sent": "ticket%other.example@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket%other.example@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "bang in resource",
|
||||
"sent": "ticket=gs-aktbdkka1q!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=gs-aktbdkka1q!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "facet + interaction",
|
||||
"sent": "ticket=gs-aktbdkka1q+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=gs-aktbdkka1q+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 200,
|
||||
"app": "correlated"
|
||||
},
|
||||
{
|
||||
"label": "reserved $",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "quoted local-part",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "unknown resource",
|
||||
"sent": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=DOES-NOT-EXIST@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-001",
|
||||
"sent": "bernd.worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-002",
|
||||
"sent": "anne-marie.schmidt@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "anne-marie.schmidt@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-003",
|
||||
"sent": "support@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-004",
|
||||
"sent": "invoice-reviewer_backup@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "invoice-reviewer_backup@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-005",
|
||||
"sent": "happy-helper.v2@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "happy-helper.v2@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-010",
|
||||
"sent": "ticket=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-011",
|
||||
"sent": "invoice=RE=2026-4711@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "invoice=RE=2026-4711@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-012",
|
||||
"sent": "ticket=PR42+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-013",
|
||||
"sent": "bernd.worsch+github@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "bernd.worsch+github@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-020",
|
||||
"sent": "=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-021",
|
||||
"sent": "ticket=@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-022",
|
||||
"sent": "ticket=PR42+@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-023",
|
||||
"sent": "ticket=PR42+comments+attachments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+comments+attachments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-024",
|
||||
"sent": "ticket=PR42$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-025",
|
||||
"sent": "ticket=PR42^1h@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42^1h@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-026",
|
||||
"sent": ".bernd@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "\".bernd\"@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-027",
|
||||
"sent": "bernd.@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "\"bernd.\"@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-028",
|
||||
"sent": "bernd..worsch@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "\"bernd..worsch\"@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-029",
|
||||
"sent": "ticket=PR.42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR.42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-030",
|
||||
"sent": "ticket=PR%2B42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR%2B42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-001",
|
||||
"sent": "ticket!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "function",
|
||||
"app_status": 201,
|
||||
"app": "created"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-002",
|
||||
"sent": "ticket=PR42!web@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42!web@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-003",
|
||||
"sent": "ticket=PR42+comments!callback@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+comments!callback@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-010",
|
||||
"sent": "ticket!@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-011",
|
||||
"sent": "ticket!callback!chat@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback!chat@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-012",
|
||||
"sent": "ticket!callback+comments@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket!callback+comments@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-013",
|
||||
"sent": "ticket=PR42+comments!callback$4.99@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+comments!callback$4.99@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p2-014",
|
||||
"sent": "ticket=PR42+comments!call.back@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42+comments!call.back@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-040",
|
||||
"sent": "\"ticket=PR42\"@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "ticket=PR42@great-service.example",
|
||||
"unchanged": false,
|
||||
"mas_valid": true,
|
||||
"kind": "resource",
|
||||
"app_status": 202,
|
||||
"app": "unknown-resource"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-041",
|
||||
"sent": "t\u00efcket=PR42@great-service.example",
|
||||
"smtp": "rejected: 'ascii' codec can't encode character '\\xef' in position 30: ordinal not in range(128)",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"mas_valid": null,
|
||||
"kind": null,
|
||||
"app_status": null,
|
||||
"app": null
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-042",
|
||||
"sent": "Ticket=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "Ticket=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-043",
|
||||
"sent": "my--type=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "my--type=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-044",
|
||||
"sent": "type-=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "type-=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": false,
|
||||
"kind": null,
|
||||
"app_status": 422,
|
||||
"app": "not-mas"
|
||||
},
|
||||
{
|
||||
"label": "corpus p1-045",
|
||||
"sent": "support-case_v2=PR42@great-service.example",
|
||||
"smtp": "accepted",
|
||||
"arrived_as": "support-case_v2=PR42@great-service.example",
|
||||
"unchanged": true,
|
||||
"mas_valid": true,
|
||||
"kind": "opaque",
|
||||
"app_status": 404,
|
||||
"app": "not-handled"
|
||||
},
|
||||
{
|
||||
"label": "relay attempt",
|
||||
"sent": "x@other.example",
|
||||
"smtp": "rejected: {'x@other.example': (554, b'5.7.1 <x@other.example>: Recipient address rejected: Access denied')}",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"relay_refused": true
|
||||
},
|
||||
{
|
||||
"label": "relay attempt",
|
||||
"sent": "x!y@other.example",
|
||||
"smtp": "rejected: {'x!y@other.example': (554, b'5.7.1 <x!y@other.example>: Recipient address rejected: Access denied')}",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"relay_refused": true
|
||||
},
|
||||
{
|
||||
"label": "relay attempt",
|
||||
"sent": "a@other.example@great-service.example",
|
||||
"smtp": "rejected: {'a@other.example@great-service.example': (554, b'5.7.1 <a@other.example@great-service.example>: Recipient address rejected: Access denied')}",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"relay_refused": true
|
||||
},
|
||||
{
|
||||
"label": "relay attempt",
|
||||
"sent": "a@great-service.example@other.example",
|
||||
"smtp": "rejected: {'a@great-service.example@other.example': (554, b'5.7.1 <a@great-service.example@other.example>: Recipient address rejected: Access denied')}",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"relay_refused": true
|
||||
},
|
||||
{
|
||||
"label": "relay attempt",
|
||||
"sent": "x%y@other.example",
|
||||
"smtp": "rejected: {'x%y@other.example': (554, b'5.7.1 <x%y@other.example>: Recipient address rejected: Access denied')}",
|
||||
"arrived_as": null,
|
||||
"unchanged": false,
|
||||
"relay_refused": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue