mesh/merge-gate head 35232c853483 merged into trunk 6f1a05366c1d — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 35232c853483 merged into trunk 6f1a05366c1d — pass: its merge-check.sh passed
mesh/delivery-group group fix/383-an-ask-reads-at-a-glance checking: composed check out of date: novox/mesh-catalog@ea1dfdce5bc7 was judged against trunk 5f491…
mesh/delivery superseded: a newer head of the same pull request
The operator could not read the settings proposals on the phone: the change, the fingerprint and a how-to
paragraph in one wall of plain text, and the values masked as ‹path› and ‹address›, so the approval was
blind.
The ask is now a summary of what changes where ("Changes 3 setting(s) of mounts on shanks; approved, the
layer is set at once and shanks takes it at its next push."), then at most three points, one key each (more
keys: the first two and the names of the rest). Who proposed it, the fingerprint and how to read it whole
are its Details, which the router notes after the answers. The change is carried twice: under the content
rule, and whole (asks.Ask.Whole), shown only on a channel that proves who answers, a value shaped like a
secret withheld in both (from the earlier work on issue 383, #202).
Vendors mesh-sdk at 9588871 (fix/639-an-ask-reads-at-a-glance: Message.Note, asks.Glance), and captures its
conformance events at that commit.
126 lines
5.8 KiB
Go
126 lines
5.8 KiB
Go
package outward
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// machines are the names a mesh in these tests has.
|
|
var machines = []string{"anchor", "laptop", "g14", "home-server"}
|
|
|
|
// **The content rule's table** (novox/hq ADR 0234, "machine names pass the content rule; domains,
|
|
// addresses, paths and secrets do not"): the shapes the messenger refuses are refused here, and the
|
|
// words a condition is made of pass — the mesh's machine names among them.
|
|
func TestMachineNamesPassAndAddressesDomainsPathsAndSecretsDoNot(t *testing.T) {
|
|
pass := []string{
|
|
"the mesh's resolver on anchor has not answered for two runs of the self-check",
|
|
"laptop has not been heard from since 2026-10-06 15:02 UTC (bound 3m0s)",
|
|
"g14's node-engine would refuse its declaration whole",
|
|
"home-server runs the node-engine 3f2a9c1b7d0e, the mesh holds 5e6f7a8b9c0d",
|
|
"seat.mesh-dns-resolver.anchor.wrong",
|
|
"anchor/postgres.query answers",
|
|
"3/4 of the consumers are behind; 1200 message(s) behind its stream's head",
|
|
"the plan for novox/app c0ffee00 has been at tier 1 of 2",
|
|
"mesh-controller.conditions key=machine.anchor.silent",
|
|
"backed-up 2 days ago: tank/data shrank to 1.2 GiB",
|
|
}
|
|
for _, text := range pass {
|
|
if r, ok := Check(text, machines...); !ok {
|
|
t.Errorf("refused as %s: %q", r, text)
|
|
}
|
|
}
|
|
refuse := map[string]string{
|
|
"AAAA for anchor: no answer from 10.77.0.1: read udp 10.77.0.3:41234->10.77.0.1:53: i/o timeout": "address",
|
|
"the resolver at [fd00::1]:53 did not answer": "address",
|
|
"fe80::1ff:fe23:4567:890a is banned": "address",
|
|
"anchor.internal answered NXDOMAIN": "address",
|
|
"the store at https://artifacts.example.org/v2 is away": "address",
|
|
"write to jochen@example.org": "address",
|
|
"aa:bb:cc:dd:ee:ff": "address",
|
|
"data at /srv/postgres/data is gone": "path",
|
|
"kept at ~/backups": "path",
|
|
`kept at C:\backups`: "path",
|
|
"password=hunter2": "secret",
|
|
"the token glpat-abcdefghijklmnop12 leaked": "secret",
|
|
"0123456789abcdef0123456789abcdef01": "secret",
|
|
}
|
|
for text, class := range refuse {
|
|
r, ok := Check(text, machines...)
|
|
if ok || r.Class != class {
|
|
t.Errorf("%q: want refused as %s, got %v %v", text, class, r, ok)
|
|
}
|
|
}
|
|
// A machine name joined to a domain is a domain, the machine's name notwithstanding.
|
|
if _, ok := Check("anchor.lan answered", machines...); ok {
|
|
t.Error("a machine's name with a domain passed")
|
|
}
|
|
// And a machine whose name has a shape the rule would otherwise refuse still passes as a name.
|
|
if _, ok := Check("node.internal is silent", "node.internal"); !ok {
|
|
t.Error("a machine name was refused")
|
|
}
|
|
}
|
|
|
|
// **Scrub says what Check refuses in words, and what it gives back always passes** — or is the
|
|
// fallback, never the text as it was.
|
|
func TestScrubAlwaysGivesWhatMayLeave(t *testing.T) {
|
|
for _, text := range []string{
|
|
"AAAA for anchor.internal: no answer from 10.77.0.1: read udp 10.77.0.3:41234->10.77.0.1:53: i/o timeout",
|
|
"dial tcp [fd00::1]:4222: connect: connection refused",
|
|
"open /var/lib/mesh/data/x.db: no such file or directory",
|
|
"GET https://artifacts.example.org/v2/blobs/sha256:0123456789abcdef0123456789abcdef0123456789abcdef: 404",
|
|
"password=hunter2 and a key -----BEGIN PRIVATE KEY-----\nAAAA\n-----END PRIVATE KEY-----",
|
|
"laptop's ban list holds 192.0.2.7 (anchor's endpoint)",
|
|
} {
|
|
got := Scrub(text, "FALLBACK", machines...)
|
|
if r, ok := Check(got, machines...); !ok {
|
|
t.Errorf("Scrub(%q) = %q still refused as %s", text, got, r)
|
|
}
|
|
if got == "FALLBACK" {
|
|
t.Errorf("Scrub(%q) fell back where words could be kept", text)
|
|
}
|
|
}
|
|
if got := Scrub("laptop's ban list holds 192.0.2.7", "", machines...); !strings.HasPrefix(got, "laptop's ban list holds an address") {
|
|
t.Errorf("the machine's name was not kept: %q", got)
|
|
}
|
|
if got := Scrub("nothing wrong with anchor", "", machines...); got != "nothing wrong with anchor" {
|
|
t.Errorf("a text that passes was changed: %q", got)
|
|
}
|
|
}
|
|
|
|
// Secret is the one class that leaves the mesh nowhere (novox/hq issue 383): it refuses every secret's shape
|
|
// Check refuses, and nothing Check refuses as an address or a path — those an ask's whole words may carry.
|
|
func TestSecretRefusesOnlyASecretsShape(t *testing.T) {
|
|
for _, text := range []string{
|
|
"-----BEGIN RSA PRIVATE KEY-----",
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.abc",
|
|
"123456789:ABCdefGHIjklMNOpqrSTUvwxYZ0123456789ab",
|
|
"ghp_abcdefghijklmnopqrstuvwxyz0123456789",
|
|
"password=hunter2",
|
|
"0123456789abcdef0123456789abcdef",
|
|
"a key xK9mQ2vL8pR4tW7yB3nF6hJ1dG5sA0zC",
|
|
} {
|
|
r, ok := Secret(text)
|
|
if ok || r.Class != "secret" {
|
|
t.Errorf("not refused as a secret: %q (%s)", text, r)
|
|
}
|
|
}
|
|
for _, text := range []string{
|
|
"recalbox=smb://nas.lan/recalbox@/mnt/recalbox:ro",
|
|
"library=/mnt/library",
|
|
"photos=/mnt/Photos_2024/Jochen",
|
|
"games=smb://nas.lan/Recalbox_Games2024",
|
|
"/srv/media/Series_Archive/Season01",
|
|
"10.77.0.9:53",
|
|
"jochen@example.com",
|
|
"C:\\Users\\jo",
|
|
"anchor and the laptop",
|
|
} {
|
|
if r, ok := Secret(text, "anchor"); !ok {
|
|
t.Errorf("refused as %s: %q", r, text)
|
|
}
|
|
if _, ok := Check(text, "anchor"); ok && text != "anchor and the laptop" {
|
|
t.Errorf("Check lets %q leave, so Secret is not the narrower rule", text)
|
|
}
|
|
}
|
|
}
|