issue 1840/S9: prove pacman's output puts no command line or environment on the bus

A fixture of pacman and checkupdates output holding command lines, environment variables and messages
leaves only names, versions and repositories in installed, updates and package.changed, and no command
the keeper runs names a package, a foreign one least of all.
This commit is contained in:
2026-10-22 14:37:00 +02:00
parent 32c66a98c1
commit 9651a8b2da
5 changed files with 116 additions and 0 deletions
@@ -0,0 +1,97 @@
package main
// What the keeper puts on the bus is only its named fields (novox/hq ADR 0507 §1; acceptance 1840/AC7): pacman's
// output holding command lines, environment variables and messages leaves none of them in a value or an event,
// and no command the keeper runs names a package, a foreign one least of all. (busshapes_test.go holds every
// shape to its named fields.)
import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestPacmanOutputHoldingCommandLinesAndEnvironmentPutsNeitherOnTheBus(t *testing.T) {
dir := filepath.Join("testdata", "pacman-output-with-command-lines-and-environment")
read := func(name string) string {
b, err := os.ReadFile(filepath.Join(dir, name))
if err != nil {
t.Fatal(err)
}
return string(b)
}
db := &pacmanDB{sl: read("pacman-Sl"), checkup: Ran{Stdout: read("checkupdates")}}
db.set(read("pacman-Q"), read("pacman-Qeq"))
k, states, ev, clock, w := newTestKeeper(db)
ctx, cancel := context.WithCancel(context.Background())
done := make(chan error, 1)
go func() { done <- k.Run(ctx) }()
for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); time.Sleep(time.Millisecond) {
if e, _ := states.store(StateUpdates).Get("node-a"); e != nil {
break
}
}
clock.Advance(time.Minute)
w.c <- LockReleased
w.c <- MirrorsRewritten
time.Sleep(20 * time.Millisecond)
cancel()
<-done
var onBus []string
for _, name := range []string{StateInstalled, StateUpdates} {
s := states.store(name)
keys, _ := s.Keys()
for _, key := range keys {
e, _ := s.Get(key)
onBus = append(onBus, name+"."+key+" "+string(e.Value))
}
}
events := ev.Take()
if len(events) == 0 {
t.Fatal("the first write said nothing")
}
for _, e := range events {
body, _ := json.Marshal(e.body)
onBus = append(onBus, e.name+" "+string(body))
}
if len(onBus) < 3 {
t.Fatalf("on the bus: %v", onBus)
}
whole := strings.Join(onBus, "\n")
for _, never := range []string{"=", "sudo", "--noconfirm", "/", "rm", "warning", "hunter2", "Synchronizing", "HOME", "TOKEN", "SECRET"} {
if strings.Contains(whole, never) {
t.Errorf("%q reached the bus:\n%s", never, whole)
}
}
var inst InstalledValue
e, _ := states.store(StateInstalled).Get("node-a")
json.Unmarshal(e.Value, &inst)
if !samePackages(inst.Packages, []InstalledPackage{{"glibc", "2.42-1", false, "core"}, {"yay", "12.0-1", true, RepoForeign}, {"zsh", "5.9-1", true, "extra"}}) {
t.Errorf("the packages are read, the rest left out: %+v", inst.Packages)
}
var up UpdatesValue
e, _ = states.store(StateUpdates).Get("node-a")
json.Unmarshal(e.Value, &up)
if len(up.Packages) != 1 || up.Packages[0] != (VersionChange{"glibc", "2.42-1", "2.42-2"}) {
t.Errorf("updates: %+v", up)
}
// no command the keeper ran names a package; the foreign one least of all.
db.mu.Lock()
defer db.mu.Unlock()
for _, c := range db.commands {
switch c {
case "pacman -Q", "pacman -Qeq", "pacman -Sl", "checkupdates":
default:
t.Errorf("the keeper ran %q", c)
}
if strings.Contains(c, "yay") {
t.Errorf("a command named the foreign package: %q", c)
}
}
}
@@ -0,0 +1,4 @@
glibc 2.42-1 -> 2.42-2
zsh 5.9-1 -> 5.9-2 PASSWORD=hunter2
env FOO=bar -> /bin/sh
:: Synchronizing package databases...
@@ -0,0 +1,9 @@
glibc 2.42-1
sudo pacman -Syu --noconfirm
yay 12.0-1
LD_PRELOAD=/tmp/hook.so 1.0-1
zsh 5.9-1
bash 5.2-1 HOME=/root
evil 1.0;rm-rf
PATH=/usr/bin:/bin
warning: could not get file information for etc/secret
@@ -0,0 +1,3 @@
yay
zsh
TOKEN=abc
@@ -0,0 +1,3 @@
core glibc 2.42-1 [installed]
extra zsh 5.9-1 [installed]
extra SECRET=x 1-1