revert: foto de perfil e BIMI, para restaurar o servico

O site ficou a responder 502 em todos os hostnames depois do deploy destas
duas funcionalidades. A reversao vem primeiro; o diagnostico vem a seguir, com
o servico de pe.

As migracoes 0012 e 0013 ficam. Sao aditivas, nada no codigo revertido as usa,
e apaga-las deixaria a base de dados numa versao sem ficheiro correspondente,
o que parte o proprio migrate no arranque seguinte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5 committed 2026-09-07 08:45:16 -03:00
1 parent 3e06162fa0
commit e30d000e43
26 files changed
+21 -1899

No files matched your search

@@ -1,153 +0,0 @@
// Package bimi validates the logo a domain publishes for BIMI.
//
// BIMI is the only mechanism by which an external mail client shows an image
// for a sender it has never met. It is per domain, not per mailbox, and the
// receiving side is strict: a logo that is not SVG Tiny Portable/Secure is
// ignored without explanation, so the checks that matter are made here rather
// than discovered from a mailbox that quietly shows nothing.
package bimi
import (
"encoding/xml"
"fmt"
"regexp"
"strings"
)
// MaxLogoBytes is the ceiling the BIMI specification sets. Receivers fetch
// this over the network for every sender they evaluate.
const MaxLogoBytes = 32 * 1024
// Violation is one reason a logo would be rejected.
type Violation struct {
Rule string
Detail string
}
func (v Violation) String() string { return fmt.Sprintf("%s: %s", v.Rule, v.Detail) }
// Elements that must not appear. Script and event handlers make the document
// active; external references make it fetch; raster images defeat the point of
// a vector mark and are how a tracking pixel would arrive.
var forbiddenElements = []string{"script", "a", "image", "foreignObject", "use", "animate", "animateTransform", "animateMotion", "set"}
var (
svgOpenRe = regexp.MustCompile(`(?is)<svg\b[^>]*>`)
attrRe = regexp.MustCompile(`(?is)([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*"([^"]*)"|([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*'([^']*)'`)
onEventRe = regexp.MustCompile(`(?is)\son[a-z]+\s*=`)
externalRefRe = regexp.MustCompile(`(?is)(href|xlink:href|src)\s*=\s*["']\s*(https?:|//|data:)`)
titleElementRe = regexp.MustCompile(`(?is)<title\b[^>]*>(.*?)</title>`)
)
// Validate reports every reason the logo would not be accepted.
//
// All of them, not the first: an operator fixing one rule at a time against a
// receiver that simply shows nothing is the worst way to learn these.
func Validate(payload []byte) []Violation {
var out []Violation
add := func(rule, detail string) { out = append(out, Violation{Rule: rule, Detail: detail}) }
if len(payload) == 0 {
return []Violation{{Rule: "empty", Detail: "the file is empty"}}
}
if len(payload) > MaxLogoBytes {
add("size", fmt.Sprintf("%d bytes; the limit is %d", len(payload), MaxLogoBytes))
}
// Parseable at all: a receiver will not repair a broken document.
if err := xml.Unmarshal(payload, new(struct {
XMLName xml.Name
})); err != nil {
return append(out, Violation{Rule: "xml", Detail: "the file is not well-formed XML"})
}
text := string(payload)
open := svgOpenRe.FindString(text)
if open == "" {
return append(out, Violation{Rule: "svg", Detail: "no <svg> root element"})
}
attrs := map[string]string{}
for _, m := range attrRe.FindAllStringSubmatch(open, -1) {
if m[1] != "" {
attrs[strings.ToLower(m[1])] = m[2]
} else {
attrs[strings.ToLower(m[3])] = m[4]
}
}
// The profile a receiver looks for. Without it the document is a plain
// SVG and is ignored.
if got := attrs["baseprofile"]; !strings.EqualFold(got, "tiny-ps") {
add("baseProfile", fmt.Sprintf("must be \"tiny-ps\", found %q", got))
}
if got := attrs["version"]; got != "1.2" {
add("version", fmt.Sprintf("must be \"1.2\", found %q", got))
}
// A non-square mark is cropped or letterboxed differently by every client,
// so the specification requires the viewBox itself be square.
if err := checkSquareViewBox(attrs["viewbox"]); err != nil {
add("viewBox", err.Error())
}
// x and y are meaningful only when the mark is placed in another document,
// and the specification forbids them on the root.
for _, forbidden := range []string{"x", "y"} {
if _, present := attrs[forbidden]; present {
add("rootAttributes", fmt.Sprintf("the root <svg> must not carry %q", forbidden))
}
}
// Exactly one title, and it is what a screen reader announces.
titles := titleElementRe.FindAllStringSubmatch(text, -1)
switch {
case len(titles) == 0:
add("title", "a <title> element is required")
case len(titles) > 1:
add("title", fmt.Sprintf("found %d <title> elements; there must be exactly one", len(titles)))
case strings.TrimSpace(titles[0][1]) == "":
add("title", "the <title> element is empty")
}
lower := strings.ToLower(text)
for _, name := range forbiddenElements {
if strings.Contains(lower, "<"+strings.ToLower(name)) {
add("forbiddenElement", fmt.Sprintf("<%s> is not allowed", name))
}
}
if onEventRe.MatchString(text) {
add("script", "event handler attributes are not allowed")
}
if externalRefRe.MatchString(text) {
add("externalReference", "the logo must not reference anything outside itself")
}
return out
}
// checkSquareViewBox verifies the mark occupies a square.
func checkSquareViewBox(value string) error {
if strings.TrimSpace(value) == "" {
return fmt.Errorf("a viewBox is required")
}
fields := strings.FieldsFunc(value, func(r rune) bool { return r == ' ' || r == ',' || r == '\t' || r == '\n' })
if len(fields) != 4 {
return fmt.Errorf("must be four numbers, found %q", value)
}
var width, height float64
if _, err := fmt.Sscanf(fields[2], "%g", &width); err != nil {
return fmt.Errorf("width %q is not a number", fields[2])
}
if _, err := fmt.Sscanf(fields[3], "%g", &height); err != nil {
return fmt.Errorf("height %q is not a number", fields[3])
}
if width <= 0 || height <= 0 {
return fmt.Errorf("width and height must be positive")
}
if width != height {
return fmt.Errorf("must be square; found %g by %g", width, height)
}
return nil
}
@@ -1,152 +0,0 @@
package bimi
import (
"fmt"
"strings"
"testing"
)
// A logo that satisfies every rule, used as the starting point each case
// then breaks in exactly one way.
const goodLogo = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny-ps" viewBox="0 0 512 512">
<title>LambdaMail</title>
<rect width="512" height="512" fill="#1b1b25"/>
<path d="M128 384 L256 128 L384 384 Z" fill="#9184d9"/>
</svg>`
func rulesOf(vs []Violation) []string {
out := make([]string, 0, len(vs))
for _, v := range vs {
out = append(out, v.Rule)
}
return out
}
func contains(rules []string, want string) bool {
for _, r := range rules {
if r == want {
return true
}
}
return false
}
func TestValidate_AcceptsAConformingLogo(t *testing.T) {
if vs := Validate([]byte(goodLogo)); len(vs) != 0 {
t.Errorf("a conforming logo was rejected: %v", vs)
}
}
func TestValidate_RequiresTheProfileReceiversLookFor(t *testing.T) {
cases := map[string]struct{ from, to, rule string }{
// Without the profile the document is a plain SVG, and a receiver
// ignores it without saying so.
"missing baseProfile": {`baseProfile="tiny-ps" `, "", "baseProfile"},
"wrong baseProfile": {`baseProfile="tiny-ps"`, `baseProfile="full"`, "baseProfile"},
"missing version": {`version="1.2" `, "", "version"},
"wrong version": {`version="1.2"`, `version="1.1"`, "version"},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
logo := strings.Replace(goodLogo, tc.from, tc.to, 1)
if !contains(rulesOf(Validate([]byte(logo))), tc.rule) {
t.Errorf("no %s violation reported", tc.rule)
}
})
}
}
// Every client crops or letterboxes a non-square mark differently, so the
// specification requires the viewBox itself be square.
func TestValidate_RequiresASquareViewBox(t *testing.T) {
for _, viewBox := range []string{"0 0 512 256", "0 0 100 200", "0 0 512", "", "0 0 0 0", "0 0 a b"} {
t.Run(viewBox, func(t *testing.T) {
logo := strings.Replace(goodLogo, `viewBox="0 0 512 512"`, fmt.Sprintf(`viewBox=%q`, viewBox), 1)
if !contains(rulesOf(Validate([]byte(logo))), "viewBox") {
t.Errorf("viewBox %q was accepted", viewBox)
}
})
}
// Commas are a legal separator and must not be read as a malformed box.
commas := strings.Replace(goodLogo, `viewBox="0 0 512 512"`, `viewBox="0,0,512,512"`, 1)
if contains(rulesOf(Validate([]byte(commas))), "viewBox") {
t.Error("a comma-separated square viewBox was rejected")
}
}
func TestValidate_RequiresExactlyOneTitle(t *testing.T) {
none := strings.Replace(goodLogo, "<title>LambdaMail</title>", "", 1)
if !contains(rulesOf(Validate([]byte(none))), "title") {
t.Error("a logo with no title was accepted")
}
two := strings.Replace(goodLogo, "<title>LambdaMail</title>", "<title>A</title><title>B</title>", 1)
if !contains(rulesOf(Validate([]byte(two))), "title") {
t.Error("a logo with two titles was accepted")
}
empty := strings.Replace(goodLogo, "<title>LambdaMail</title>", "<title> </title>", 1)
if !contains(rulesOf(Validate([]byte(empty))), "title") {
t.Error("an empty title was accepted")
}
}
// The logo is fetched and rendered by other people's mail clients. Anything
// active or anything that reaches out over the network has no business in it.
func TestValidate_RefusesAnythingActiveOrExternal(t *testing.T) {
cases := map[string]struct{ insert, rule string }{
"script element": {`<script>alert(1)</script>`, "forbiddenElement"},
"link": {`<a href="https://example.test">x</a>`, "forbiddenElement"},
"raster image": {`<image href="https://example.test/p.png"/>`, "forbiddenElement"},
"foreign object": {`<foreignObject><b>x</b></foreignObject>`, "forbiddenElement"},
"animation": {`<animate attributeName="x" to="9"/>`, "forbiddenElement"},
"event handler": {`<rect width="1" height="1" onload="alert(1)"/>`, "script"},
"external href": {`<rect width="1" height="1" xlink:href="https://example.test/x"/>`, "externalReference"},
"data uri": {`<rect width="1" height="1" href="data:image/png;base64,AAAA"/>`, "externalReference"},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
logo := strings.Replace(goodLogo, "</svg>", tc.insert+"</svg>", 1)
if !contains(rulesOf(Validate([]byte(logo))), tc.rule) {
t.Errorf("%s was accepted: %v", name, Validate([]byte(logo)))
}
})
}
}
func TestValidate_RefusesWhatCannotBeReadAtAll(t *testing.T) {
if !contains(rulesOf(Validate([]byte(""))), "empty") {
t.Error("an empty file was accepted")
}
if !contains(rulesOf(Validate([]byte("<svg><unclosed>"))), "xml") {
t.Error("malformed XML was accepted")
}
// A PNG renamed to .svg is the likeliest wrong upload of all.
if len(Validate([]byte{0x89, 'P', 'N', 'G'})) == 0 {
t.Error("a PNG was accepted as a logo")
}
}
func TestValidate_RefusesALogoTooBigToBeFetched(t *testing.T) {
padding := strings.Repeat(" ", MaxLogoBytes)
logo := strings.Replace(goodLogo, "</svg>", "<!--"+padding+"--></svg>", 1)
if !contains(rulesOf(Validate([]byte(logo))), "size") {
t.Error("an oversized logo was accepted")
}
}
// One rule at a time, against a receiver that shows nothing either way, is a
// miserable way to learn these - so every violation is reported at once.
func TestValidate_ReportsEveryViolationAtOnce(t *testing.T) {
broken := `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 20"><script/></svg>`
rules := rulesOf(Validate([]byte(broken)))
for _, want := range []string{"baseProfile", "version", "viewBox", "title", "forbiddenElement"} {
if !contains(rules, want) {
t.Errorf("missing %s; got %v", want, rules)
}
}
}
@@ -35,15 +35,6 @@ type DnsRecordSpec struct {
// this domain. Without it every message the relay forwards fails SPF
// (PLAN.md section 10.4).
RelaySpfInclude string
// BimiLogoURL is where the published mark is served from. Empty when the
// domain has no logo, and then no BIMI record is published at all: an
// advertised record pointing at nothing is worse than none, because a
// receiver stops looking once it finds one.
BimiLogoURL string
// BimiVmcURL is the Verified Mark Certificate. Gmail and Outlook render
// the logo only when the record carries one; without it the record is
// still valid and other clients may use it.
BimiVmcURL string
}
// BuildDnsRecordSpecs generates the DNS records of PLAN.md section 7: the 13
@@ -205,21 +196,6 @@ func BuildDnsRecordSpecs(spec DnsRecordSpec) []DnsRecord {
})
}
// BIMI, published only when there is a logo to point at.
if spec.BimiLogoURL != "" {
value := fmt.Sprintf("v=BIMI1; l=%s;", spec.BimiLogoURL)
if spec.BimiVmcURL != "" {
value = fmt.Sprintf("v=BIMI1; l=%s; a=%s;", spec.BimiLogoURL, spec.BimiVmcURL)
}
records = append(records, DnsRecord{
Type: "TXT",
Name: fmt.Sprintf("default._bimi.%s", domainName),
Value: value,
TTL: 1,
Comment: "LambdaMail BIMI Record",
})
}
// 2. Conditional AAAA record if IPv6 is supplied
if ownsMailHost && serverIPv6 != "" {
records = append(records, DnsRecord{
@@ -195,72 +195,3 @@ func TestBuildDnsRecordSpecs_KeepsMailHostAddressesInsideTheDomain(t *testing.T)
t.Errorf("lost the mail host addresses (A=%v AAAA=%v)", a, aaaa)
}
}
// BIMI is published only when there is a mark to point at.
//
// A record that advertises a logo which is not there is worse than no record:
// a receiver stops looking once it finds one, so an empty pointer buys a
// failed fetch instead of the fallback it would otherwise have used.
func TestBuildDnsRecordSpecs_BimiOnlyWhenThereIsALogo(t *testing.T) {
without := BuildDnsRecordSpecs(DnsRecordSpec{
DomainName: "example.test",
MailHost: "mail.example.test",
ServerIPv4: "192.0.2.1",
})
for _, r := range without {
if strings.Contains(r.Name, "_bimi") {
t.Errorf("published %s with no logo configured", r.Name)
}
}
with := BuildDnsRecordSpecs(DnsRecordSpec{
DomainName: "example.test",
MailHost: "mail.example.test",
ServerIPv4: "192.0.2.1",
BimiLogoURL: "https://mail.example.test/.well-known/bimi/default.svg",
})
var found *DnsRecord
for i, r := range with {
if r.Name == "default._bimi.example.test" {
found = &with[i]
}
}
if found == nil {
t.Fatal("no BIMI record was published for a domain with a logo")
}
if found.Type != "TXT" {
t.Errorf("BIMI record type is %q, want TXT", found.Type)
}
if !strings.HasPrefix(found.Value, "v=BIMI1;") {
t.Errorf("value %q does not start with the version tag", found.Value)
}
// No certificate configured, so no a= tag: an empty one would advertise a
// certificate that cannot be fetched.
if strings.Contains(found.Value, "a=") {
t.Errorf("value %q carries an empty certificate tag", found.Value)
}
}
// Gmail and Outlook render the mark only when the record points at a Verified
// Mark Certificate, so the tag has to be carried when there is one.
func TestBuildDnsRecordSpecs_BimiCarriesTheCertificateWhenConfigured(t *testing.T) {
specs := BuildDnsRecordSpecs(DnsRecordSpec{
DomainName: "example.test",
MailHost: "mail.example.test",
ServerIPv4: "192.0.2.1",
BimiLogoURL: "https://mail.example.test/.well-known/bimi/default.svg",
BimiVmcURL: "https://mail.example.test/.well-known/bimi/default.pem",
})
for _, r := range specs {
if r.Name != "default._bimi.example.test" {
continue
}
if !strings.Contains(r.Value, "a=https://mail.example.test/.well-known/bimi/default.pem") {
t.Errorf("value %q does not carry the certificate", r.Value)
}
return
}
t.Fatal("no BIMI record was published")
}