mesh/delivery-group group feat/journal-window-on-the-seat delivering: 0 of 2 delivered
mesh/merge-gate pass: builds systemd → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery held for a person: its group feat/journal-window-on-the-seat's composed check did not pass for the heads that merged; a person decides that…
An incident is read for the minutes it happened in (the operator's direction 2026-10-07): journal takes since, until, priority and a fixed-string match. Every value is one word of journalctl's argv, held to the forms journalctl reads, so nothing reaches a shell or is read as an option under sudo. What the unit printed of a secret is redacted, as docker_logs does, before the match is applied, so a match cannot find one. systemd_failed becomes the seat's failed, with an optional scope. Needs the controller's seat with these verbs (mesh-controller, same branch): an older controller refuses a claim serving a verb its seat does not promise.
244 lines
8.5 KiB
Go
244 lines
8.5 KiB
Go
package main
|
|
|
|
// The journal verb's window, priority and match (the operator's direction 2026-10-07): every value one
|
|
// word of journalctl's argv, nothing read as an option, the cap kept, a secret never answered — and
|
|
// what has failed on the seat.
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// journalOf is a manager whose journalctl answers the given lines and whose unit has the given
|
|
// Environment=, keeping each call.
|
|
func journalOf(lines []string, env string, calls *[]call) *Manager {
|
|
return operator(fake(func(c call) Ran {
|
|
if contains(c.args, "journalctl") || c.cmd == "journalctl" {
|
|
return Ran{Stdout: strings.Join(lines, "\n") + "\n"}
|
|
}
|
|
if contains(c.args, "--property=Environment") {
|
|
return Ran{Stdout: env + "\n"}
|
|
}
|
|
return Ran{}
|
|
}, calls))
|
|
}
|
|
|
|
func journalArgs(t *testing.T, calls []call) []string {
|
|
t.Helper()
|
|
for _, c := range calls {
|
|
if c.cmd == "sudo" && len(c.args) > 1 && c.args[1] == "journalctl" {
|
|
return c.args[2:]
|
|
}
|
|
if c.cmd == "journalctl" {
|
|
return c.args
|
|
}
|
|
}
|
|
t.Fatalf("journalctl was not run: %+v", calls)
|
|
return nil
|
|
}
|
|
|
|
func TestAWindowAndAPriorityAreEachOneWordOfJournalctl(t *testing.T) {
|
|
var calls []call
|
|
m := journalOf([]string{"a"}, "", &calls)
|
|
_, err := m.Journal(System, "mail.service", JournalQuery{Lines: 50, Since: "-30min", Until: "2026-10-07T10:00:00.5Z", Priority: "warning"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := strings.Join(journalArgs(t, calls), " ")
|
|
want := "--no-pager --output=short-iso --unit=mail.service --since=-30min --until=@1791367201 --priority=4 --lines=50"
|
|
if got != want {
|
|
t.Fatalf("journalctl was given\n %s\nnot\n %s", got, want)
|
|
}
|
|
if calls[0].cmd != "sudo" || calls[0].args[0] != "-n" {
|
|
t.Fatalf("the system journal was not read escalated: %+v", calls[0])
|
|
}
|
|
}
|
|
|
|
func TestTheFormsAWindowIsReadIn(t *testing.T) {
|
|
for _, ok := range []string{"-30min", "-2h", "+1h", "-1h30min", "2h ago", "30min ago", "yesterday", "now", "today",
|
|
"2026-10-07T09:30:00Z", "2026-10-07T09:30:00+02:00", "2026-10-07", "2026-10-07 09:30", "2026-10-07 09:30:15"} {
|
|
if _, err := (JournalQuery{Since: ok}).valid(); err != nil {
|
|
t.Errorf("%q refused: %v", ok, err)
|
|
}
|
|
}
|
|
for _, bad := range []string{"--user", "-u", "-D/etc", "--directory=/", "-30min --merge", "-30min;id", "$(id)",
|
|
"-x", "--", "1h; rm", "2026-10-07T09:30:00", "last week", "-30min\n--merge"} {
|
|
if _, err := (JournalQuery{Since: bad}).valid(); err == nil {
|
|
t.Errorf("since %q accepted", bad)
|
|
}
|
|
if _, err := (JournalQuery{Until: bad}).valid(); err == nil {
|
|
t.Errorf("until %q accepted", bad)
|
|
}
|
|
}
|
|
if _, err := (JournalQuery{Since: "2026-10-07T10:00:00Z", Until: "2026-10-07T09:00:00Z"}).valid(); err == nil {
|
|
t.Error("a window ending before it starts was accepted")
|
|
}
|
|
}
|
|
|
|
func TestPriorityIsANumberOrAName(t *testing.T) {
|
|
for in, want := range map[string]string{"0": "0", "7": "7", "err": "3", "ERROR": "3", "warning": "4", "debug": "7", "emerg": "0"} {
|
|
q, err := (JournalQuery{Priority: in}).valid()
|
|
if err != nil || q.Priority != want {
|
|
t.Errorf("%q: %q %v", in, q.Priority, err)
|
|
}
|
|
}
|
|
for _, bad := range []string{"8", "-1", "--user", "3..5", "loud", "33"} {
|
|
if _, err := (JournalQuery{Priority: bad}).valid(); err == nil {
|
|
t.Errorf("priority %q accepted", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNothingRunsWhenAValueIsRefused(t *testing.T) {
|
|
var calls []call
|
|
m := journalOf(nil, "", &calls)
|
|
for _, q := range []JournalQuery{{Since: "--merge"}, {Until: "-D/"}, {Priority: "--user"}, {Lines: -1},
|
|
{Match: "a\nb"}, {Match: strings.Repeat("x", LongestMatch+1)}} {
|
|
if _, err := m.Journal(System, "x.service", q); err == nil {
|
|
t.Errorf("%+v accepted", q)
|
|
}
|
|
}
|
|
if _, err := m.Journal(System, "--merge", JournalQuery{}); err == nil {
|
|
t.Error("an option was accepted as a unit")
|
|
}
|
|
if len(calls) != 0 {
|
|
t.Fatalf("something ran: %+v", calls)
|
|
}
|
|
}
|
|
|
|
func TestTheCapIsKeptAndTheDefaultIsAHundred(t *testing.T) {
|
|
for in, want := range map[int]int{0: DefaultLines, 1: 1, 2000: 2000, 2001: 2000, 1 << 30: 2000} {
|
|
q, err := (JournalQuery{Lines: in}).valid()
|
|
if err != nil || q.Lines != want {
|
|
t.Errorf("%d: %d %v", in, q.Lines, err)
|
|
}
|
|
}
|
|
// What the seat's schema carries is text, and an agent may send a number: both read the same.
|
|
for _, a := range []map[string]any{{"lines": "250"}, {"lines": float64(250)}} {
|
|
q, err := journalQuery(a)
|
|
if err != nil || q.Lines != 250 {
|
|
t.Errorf("%v: %+v %v", a, q, err)
|
|
}
|
|
}
|
|
for _, a := range []map[string]any{{"lines": "many"}, {"lines": 2.5}, {"since": float64(3)}, {"match": []any{"x"}}} {
|
|
if _, err := journalQuery(a); err == nil {
|
|
t.Errorf("%v accepted", a)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAMatchIsAFixedStringOverTheWindowsLastLines(t *testing.T) {
|
|
var calls []call
|
|
lines := []string{"one (a.b)", "two a.b", "three axb", "four (a.b)"}
|
|
r, err := journalOf(lines, "", &calls).Journal(System, "x.service", JournalQuery{Lines: 1, Match: "(a.b)"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := r["lines"].([]string); len(got) != 1 || got[0] != "four (a.b)" {
|
|
t.Fatalf("%v", got)
|
|
}
|
|
if r["scanned"] != 4 || r["count"] != 1 || r["match"] != "(a.b)" {
|
|
t.Fatalf("%v", r)
|
|
}
|
|
if a := journalArgs(t, calls); a[len(a)-1] != fmt.Sprintf("--lines=%d", MatchScan) {
|
|
t.Fatalf("a match was not looked for over a scan: %v", a)
|
|
}
|
|
for _, a := range journalArgs(t, calls) {
|
|
if strings.Contains(a, "a.b") || strings.HasPrefix(a, "--grep") {
|
|
t.Fatalf("the match reached journalctl: %v", a)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestASecretTheUnitPrintedIsNeverAnsweredNorFoundByAMatch(t *testing.T) {
|
|
env := `HOME=/var/lib/x "DB_PASSWORD=hunter2hunter2" DATABASE_URL=postgres://app:s3cr3tpw@db/app PORT=5432`
|
|
lines := []string{
|
|
"starting with password hunter2hunter2",
|
|
"connecting to postgres://app:s3cr3tpw@db/app",
|
|
"proxy at https://u:otherpassword@example.test/",
|
|
"ran: tool --password=flagsecret1 --token tokensecret2",
|
|
"API_TOKEN=abcdefghij set",
|
|
"nothing secret here",
|
|
}
|
|
r, err := journalOf(lines, env, nil).Journal(System, "x.service", JournalQuery{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
all := strings.Join(r["lines"].([]string), "\n")
|
|
for _, secret := range []string{"hunter2hunter2", "s3cr3tpw", "otherpassword", "flagsecret1", "tokensecret2", "abcdefghij"} {
|
|
if strings.Contains(all, secret) {
|
|
t.Errorf("%s was answered:\n%s", secret, all)
|
|
}
|
|
}
|
|
if !strings.Contains(all, "[redacted: DB_PASSWORD]") || !strings.Contains(all, "nothing secret here") {
|
|
t.Fatalf("%s", all)
|
|
}
|
|
if r["redacted"] == nil || r["leak"] == nil {
|
|
t.Fatalf("the redaction was not said: %v", r)
|
|
}
|
|
found, _ := journalOf(lines, env, nil).Journal(System, "x.service", JournalQuery{Match: "hunter2"})
|
|
if found["count"] != 0 {
|
|
t.Fatalf("a match found a secret: %v", found)
|
|
}
|
|
}
|
|
|
|
func TestAnUnreadableEnvironmentStillRedactsByShapeAndSaysSo(t *testing.T) {
|
|
m := operator(fake(func(c call) Ran {
|
|
if contains(c.args, "--property=Environment") {
|
|
return Ran{Status: 1, Stderr: "Failed to get properties: Access denied\n"}
|
|
}
|
|
return Ran{Stdout: "postgres://app:s3cr3tpw@db/app\n"}
|
|
}, nil))
|
|
r, err := m.Journal(System, "x.service", JournalQuery{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(strings.Join(r["lines"].([]string), ""), "s3cr3tpw") || r["redaction"] == nil {
|
|
t.Fatalf("%v", r)
|
|
}
|
|
}
|
|
|
|
func TestALongLineIsCut(t *testing.T) {
|
|
r, _ := journalOf([]string{strings.Repeat("y", LongestLine+10)}, "", nil).Journal(System, "x.service", JournalQuery{})
|
|
if l := r["lines"].([]string)[0]; len(l) > LongestLine+len("…") {
|
|
t.Fatalf("a line of %d bytes", len(l))
|
|
}
|
|
}
|
|
|
|
func TestTheEnvironmentPropertyIsReadAsWords(t *testing.T) {
|
|
got := environment(`A=1 "B=two words" 'C=x\'y' D=`)
|
|
if strings.Join(got, "|") != "A=1|B=two words|C=x'y|D=" {
|
|
t.Fatalf("%q", got)
|
|
}
|
|
}
|
|
|
|
func TestFailedIsOnTheSeatAndNarrowsToAScope(t *testing.T) {
|
|
var calls []call
|
|
m := operator(fake(func(call) Ran { return Ran{Stdout: list} }, &calls))
|
|
var failed func(map[string]any) (any, error)
|
|
for _, tool := range tools(m) {
|
|
if tool.Name == seat+".failed" {
|
|
failed = tool.Run
|
|
}
|
|
if tool.Name == "systemd_failed" {
|
|
t.Fatal("the module still serves its own systemd_failed beside the seat's verb")
|
|
}
|
|
}
|
|
if failed == nil {
|
|
t.Fatal("the seat's failed is not served")
|
|
}
|
|
both, err := failed(map[string]any{})
|
|
if err != nil || len(both.(map[string]any)) != 2 {
|
|
t.Fatalf("%v %v", both, err)
|
|
}
|
|
calls = nil
|
|
one, err := failed(map[string]any{"scope": "system"})
|
|
if err != nil || len(one.(map[string]any)) != 1 || len(calls) != 1 || contains(calls[0].args, "--user") {
|
|
t.Fatalf("%v %v %+v", one, err, calls)
|
|
}
|
|
if _, err := failed(map[string]any{"scope": "everything"}); err == nil {
|
|
t.Fatal("a scope that is none was accepted")
|
|
}
|
|
}
|