package outward // The operator channel's cases, carried here with the rule (novox/hq ADR 0310 §4): the rule moved, its meaning did not. import ( "encoding/base64" "fmt" "math/rand" "testing" ) func TestRolesAndWordsMayLeave(t *testing.T) { for _, s := range []string{ "URGENT: the home server has not been heard for 15 min", "key: provider.keycloak.ace.gitea.failing", "more: conditions show machine.ace.silent", "since: 2026-10-06 12:30 UTC", "call call-1759752000123456789-12 has run past its bound of 10 min", "ace/postgres.query answers no more", "3/4 machines answered; plan 41 waits on build 7f3a9c1e", "the bus advisory slow-consumer for ace_records", "core.controller.novox.rolled-back", "seat node-notifier has no live holder", "HELD BACK: 5 message(s) over the cap of 20 an hour", "STILL OPEN after 1.5 h: the controller's event loop takes no message", "node-engine and node tools builds differ from the plan's", } { if r, ok := Check(s); !ok { t.Errorf("refused %q: %s", s, r) } } } func TestAnAddressAPathOrASecretMayNot(t *testing.T) { for s, class := range map[string]string{ "cannot reach 192.168.1.20": "address", "listening on 10.0.0.7:5432": "address", "route fd00:1234:5678::1 is gone": "address", "fe80::1 answered": "address", "2001:db8:0:0:0:0:2:1 answered": "address", "see https://git.example.org/x": "address", "git.novox.internal does not answer": "address", "the mail for admin@example.org bounced": "address", "zurag.be is down": "address", "localhost refused": "address", "the card aa:bb:cc:dd:ee:ff went away": "address", "/var/lib/mesh-controller is full": "path", "~/.config/hal/env changed": "path", "read ./grants.json": "path", "services/postgres/data/pg_hba.conf": "path", "C:\\Users\\x": "path", "token 123456789:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw": "secret", "ghp_abcdefghijklmnopqrstuvwxyz0123456789": "secret", "password=hunter2": "secret", "api_key: x": "secret", "-----BEGIN OPENSSH PRIVATE KEY-----": "secret", "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0": "secret", "commit 3f2a9c1e5b7d4f6a8c0e2b4d6f8a0c2e4b6d8f0a": "secret", "value Zx8Qp2Lm9Rt4Vb7Nc1Kd5Hs3Jf6": "secret", } { r, ok := Check(s) if ok { t.Errorf("let %q leave", s) continue } if r.Class != class { t.Errorf("%q refused as %s, want %s", s, r, class) } } } // The secret class alone (novox/hq ADR 0310 §4, issue 383): a path and an address pass, a path's pieces are // judged between its slashes, and every named secret shape and a random run outside a path are refused. func TestCheckSecretRefusesASecretsShapeAndNothingElse(t *testing.T) { for text, may := range map[string]bool{ "+ shares: photos=/mnt/Photos_2024/Jochen": true, "+ sources: games=smb://nas.lan/Recalbox_Games2024": true, "+ library: /srv/media/Series_Archive/Season01": true, "root:x:0:0::/root:/usr/bin/bash": true, "nameserver 192.168.1.1": true, "drwxr-x--- jochen users /home/jochen": true, "+ key: xK9mQ2vL8pR4tW7yB3nF6hJ1dG5sA0zC": false, "-----BEGIN OPENSSH PRIVATE KEY-----": false, "password=hunter2": false, "Authorization: token ghp_abcdefghijklmnopqrstuvwxyz0123456789": false, "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0": false, "sha 3f2a9c1e5b7d4f6a8c0e2b4d6f8a0c2e4b6d8f0a": false, } { r, ok := CheckSecret(text) if ok != may { t.Errorf("CheckSecret(%q) = %v (%s), want %v", text, ok, r, may) } if !ok && r.Class != "secret" { t.Errorf("CheckSecret(%q) refused as %s: only the secret class is refused there", text, r) } } } // The review of mesh-sdk #23: the shapes node-reads meets in env files, unit files, configurations and // history files, each refused by its shape whatever the value looks like. func TestCheckSecretRefusesTheShapesOfConfigurationFiles(t *testing.T) { for _, text := range []string{ "url = nats://user:pa55word@host:4222", "DATABASE_URL=postgres://app:hunter2@db/app", "MESH_TOKEN=s3cr3t-v4lue", "DB_PASS=hunter2", "export GITEA_SECRET=abc", "Environment=APP_API_KEY=xyz", "SUDO_PASSWORD hunter2", "aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "password = hunter2", "wifi_psk: correcthorse", "value: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", } { if r, ok := CheckSecret(text); ok || r.Class != "secret" { t.Errorf("CheckSecret(%q) let it through (%v)", text, r) } } // What a configuration file says that is not a secret. for _, text := range []string{ "ExecStart=/usr/lib/systemd/systemd-resolved", "UUID=3f2a9c1e-5b7d-4f6a-8c0e-2b4d6f8a0c2e /home btrfs defaults 0 0", "password required pam_unix.so", "bypass = true", "PasswordAuthentication no", "url = https://git.example.org/novox/mesh-sdk.git", "HOME=/var/lib/mesh-reads", "TOKEN_FILE=/var/lib/app/token", } { if r, ok := CheckSecret(text); !ok { t.Errorf("CheckSecret(%q) withheld it: %s", text, r) } } } // Random values as base64 and base64url, wherever their slashes fall: every one is refused. func TestCheckSecretRefusesRandomValuesAsBase64(t *testing.T) { rng := rand.New(rand.NewSource(418)) passed := map[string]int{} for i := 0; i < 1000; i++ { for _, n := range []int{16, 32} { b := make([]byte, n) rng.Read(b) for name, enc := range map[string]*base64.Encoding{"std": base64.StdEncoding, "url": base64.RawURLEncoding} { v := enc.EncodeToString(b) if _, ok := CheckSecret("key " + v); ok { passed[fmt.Sprintf("%s/%d", name, n)]++ } } } } if len(passed) > 0 { t.Errorf("random values passed as clean, of 1000 each: %v", passed) } } // A CamelCase name is a word, not a value: the case changes once a word. func TestCheckSecretLetsACamelCaseNameThrough(t *testing.T) { for _, text := range []string{"PasswordAuthenticationIsOff", "ChallengeResponseAuthentication no", "KbdInteractiveAuthentication"} { if r, ok := CheckSecret(text); !ok { t.Errorf("CheckSecret(%q) withheld it: %s", text, r) } } }