bastion 0.60.0
ci / personal-data (push) Successful in 4s
ci / test (push) Successful in 3m36s
ci / fuzz (push) Successful in 3m35s

This commit is contained in:
Privatae
2026-11-02 00:10:58 +00:00
parent 4a5c310482
commit 4f8285ef18
100 changed files with 11537 additions and 840 deletions
+138
View File
@@ -0,0 +1,138 @@
package main
import (
"encoding/json"
"errors"
"net"
"os"
"os/user"
"path/filepath"
"strings"
"testing"
"time"
)
// The socket answers "status" with the kernel's name for the caller, so a
// real connection reads the caller's own ask and nobody else's.
func TestTheAskSocketAnswersStatusOnlyForTheAsker(t *testing.T) {
askFlowWorld(t)
me, err := user.Current()
if err != nil {
t.Skip("no account name for this process:", err)
}
mine := fileAsk(agentAskRequest{Op: "stop", PID: 101}, me.Username, 0)
theirs := fileAsk(agentAskRequest{Op: "stop", PID: 102}, me.Username+"-other", 0)
if !mine.OK || !theirs.OK {
t.Fatalf("asks: %+v %+v", mine, theirs)
}
sock := filepath.Join(t.TempDir(), "ask.sock")
ln, err := net.Listen("unix", sock)
if err != nil {
t.Fatal(err)
}
defer ln.Close()
go func() {
for {
c, err := ln.Accept()
if err != nil {
return
}
go askSockServe(c)
}
}()
ask := func(id string) agentAskReply {
t.Helper()
c, err := net.DialTimeout("unix", sock, 3*time.Second)
if err != nil {
t.Fatal(err)
}
defer c.Close()
_ = json.NewEncoder(c).Encode(agentAskRequest{Op: "status", Request: id})
var r agentAskReply
if err := json.NewDecoder(c).Decode(&r); err != nil {
t.Fatal(err)
}
return r
}
if r := ask(mine.Request); !r.OK || r.State != askWaiting || r.User != me.Username {
t.Errorf("my own ask over the socket: %+v", r)
}
if r := ask(theirs.Request); r.OK || r.State != askUnknown {
t.Errorf("another account's ask over the socket: %+v", r)
}
}
// `stop` run by an agent (no terminal) files the ask and prints it as JSON;
// `request` follows it to the outcome, with the exit status to branch on.
func TestStopAndRequestFromAnAgent(t *testing.T) {
d := askFlowWorld(t)
prevAuth := machineAuthorityHeld
t.Cleanup(func() { machineAuthorityHeld = prevAuth })
machineAuthorityHeld = func() bool { return true } // holds the state: the direct path
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
prevIn := os.Stdin
os.Stdin = r // a pipe, not a terminal: an agent
t.Cleanup(func() { os.Stdin = prevIn; _ = w.Close(); _ = r.Close() })
out := captureStdout(t)
if err := cmdStop([]string{"101", "--json"}); err != nil {
t.Fatalf("stop: %v", err)
}
var v askView
if err := json.Unmarshal([]byte(printedSince(out, 0, "}\n")), &v); err != nil || v.State != askWaiting ||
v.Request == "" {
t.Fatalf("stop --json printed %q (%v)", out(), err)
}
rows := auditRowsOf(t, d, localStopAskOp)
if len(rows) != 1 {
t.Fatalf("filing rows: %v", rows)
}
id := fullRequestID(t, d, v.Request)
if _, err := stepupApproveRequest(d, id, codeFor(t, machineTestSecret)); err != nil {
t.Fatal(err)
}
localStopOnce(d, time.Now())
before := len(out())
if err := cmdRequest([]string{v.Request}); err != nil {
t.Fatalf("request after the stop: %v", err)
}
if got := printedSince(out, before, "done."); !strings.Contains(got, "approved by the machine's owner and done") {
t.Errorf("request printed %q", got)
}
// A second ask, denied: `request` exits 1 without an error line.
second := mustAsk(t, 102)
if err := stepupDenyRequest(d, fullRequestID(t, d, second)); err != nil {
t.Fatal(err)
}
var q quietExit
if err := cmdRequest([]string{second, "--json"}); !errors.As(err, &q) || q.code != 1 {
t.Errorf("a denied ask: %v", err)
}
}
// printedSince waits (briefly) for captured output past from to hold want:
// captureStdout copies the pipe on its own goroutine.
func printedSince(out func() string, from int, want string) string {
deadline := time.Now().Add(3 * time.Second)
for {
got := out()[from:]
if strings.Contains(got, want) || time.Now().After(deadline) {
return got
}
time.Sleep(10 * time.Millisecond)
}
}
func mustAsk(t *testing.T, pid int) string {
t.Helper()
r := fileAsk(agentAskRequest{Op: "stop", PID: pid}, procUser(os.Getpid()), 0)
if !r.OK {
t.Fatal(r.Why)
}
return r.Request
}