Fix the five resilience review findings
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -28,6 +29,13 @@ func TestProcessSubprocessHelper(t *testing.T) {
|
||||
}
|
||||
for i, arg := range os.Args {
|
||||
if arg == "--" {
|
||||
if value := os.Getenv("AUDITA_TEST_UMASK"); value != "" {
|
||||
mask, err := strconv.ParseUint(value, 8, 32)
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
syscall.Umask(int(mask))
|
||||
}
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
code := cli.Run(ctx, os.Args[i+1:], os.Stdout, os.Stderr)
|
||||
stop()
|
||||
@@ -45,6 +53,7 @@ type fixtureProvider struct {
|
||||
mode string
|
||||
secret string
|
||||
entered chan struct{}
|
||||
beforeResponse func()
|
||||
}
|
||||
|
||||
func (p *fixtureProvider) serve(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -71,7 +80,11 @@ func (p *fixtureProvider) serve(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
validationCall := p.validationCalls
|
||||
proposalCall := p.proposalCalls
|
||||
beforeResponse := p.beforeResponse
|
||||
p.mu.Unlock()
|
||||
if beforeResponse != nil {
|
||||
beforeResponse()
|
||||
}
|
||||
if p.mode == "wait-cancel" {
|
||||
select {
|
||||
case p.entered <- struct{}{}:
|
||||
@@ -166,6 +179,7 @@ func (f processFixture) run(t *testing.T, includeOutput bool, extra ...string) (
|
||||
}
|
||||
args = append(args, extra...)
|
||||
command := exec.Command(os.Args[0], args...)
|
||||
command.Dir = f.dir
|
||||
command.Env = append(os.Environ(), "AUDITA_HELPER_PROCESS=1", "AUDITA_TEST_PROVIDER_KEY="+f.provider.secret)
|
||||
var stdout, stderr bytes.Buffer
|
||||
command.Stdout, command.Stderr = &stdout, &stderr
|
||||
@@ -486,3 +500,141 @@ func TestSubprocessTranscriptPreflightWithoutReporting(t *testing.T) {
|
||||
t.Fatalf("transcript preflight: code=%d calls=%d stderr=%q", code, f.provider.callCount(), stderr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessOutputPermissionsHonorUmask(t *testing.T) {
|
||||
for _, mask := range []string{"0077", "0022"} {
|
||||
t.Run(mask, func(t *testing.T) {
|
||||
t.Setenv("AUDITA_TEST_UMASK", mask)
|
||||
f := newProcessFixture(t, "normal", "[]")
|
||||
paths := []string{f.output, filepath.Join(f.dir, "errors.yml"), filepath.Join(f.dir, "recoveries.yml"), filepath.Join(f.dir, "report.md")}
|
||||
parsed, _ := strconv.ParseUint(mask, 8, 32)
|
||||
want := os.FileMode(0644 &^ parsed)
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
code, _, stderr := f.run(t, true)
|
||||
if code != 0 {
|
||||
t.Fatalf("run: %d %s", code, stderr)
|
||||
}
|
||||
for _, path := range paths {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.Mode().Perm() != want {
|
||||
t.Errorf("%s mode=%o want=%o", filepath.Base(path), info.Mode().Perm(), want)
|
||||
}
|
||||
if err := os.Chmod(path, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
want = 0600 // Existing restrictive modes survive replacement under either mask.
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessWhitespaceOutputIsALiteralFile(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[]")
|
||||
f.output = " "
|
||||
code, stdout, stderr := f.run(t, true)
|
||||
if code != 0 || stdout != "" {
|
||||
t.Fatalf("code=%d stdout=%q stderr=%q", code, stdout, stderr)
|
||||
}
|
||||
output, err := os.ReadFile(filepath.Join(f.dir, " "))
|
||||
if err != nil || !bytes.Contains(output, []byte("hello world")) {
|
||||
t.Fatalf("missing transcript: %s %v", output, err)
|
||||
}
|
||||
if f.errors(t).Status != reporting.StatusCompleted {
|
||||
t.Fatal("reports did not follow file output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessPathAnnouncementsAreRedacted(t *testing.T) {
|
||||
for _, fail := range []bool{false, true} {
|
||||
t.Run(fmt.Sprint(fail), func(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[]")
|
||||
selected, unused := "selected-path-secret", "unused-path-secret"
|
||||
f.provider.secret = selected
|
||||
t.Setenv("AUDITA_UNUSED_PATH_KEY", unused)
|
||||
cfg, err := os.ReadFile(f.config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg = bytes.Replace(cfg, []byte("backends:\n"), []byte("backends:\n unused:\n endpoint: http://unused.invalid/v1\n credential: AUDITA_UNUSED_PATH_KEY\n"), 1)
|
||||
if err := os.WriteFile(f.config, cfg, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reportDir := filepath.Join(f.dir, unused)
|
||||
if err := os.Mkdir(reportDir, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.output = filepath.Join(reportDir, "out.json")
|
||||
debugParent := filepath.Join(f.dir, selected)
|
||||
if fail {
|
||||
if err := os.WriteFile(f.transcript, []byte("invalid"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
code, _, stderr := f.run(t, true, "--debug", "--work-dir", debugParent)
|
||||
if (!fail && code != 0) || (fail && code != 1) {
|
||||
t.Fatalf("code=%d stderr=%q", code, stderr)
|
||||
}
|
||||
if strings.Contains(stderr, selected) || strings.Contains(stderr, unused) {
|
||||
t.Fatalf("credential in stderr: %q", stderr)
|
||||
}
|
||||
if !strings.Contains(stderr, "[REDACTED]") {
|
||||
t.Fatalf("missing redacted announcement: %q", stderr)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(reportDir, "report.md")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dirs, err := os.ReadDir(debugParent)
|
||||
if err != nil || len(dirs) != 1 {
|
||||
t.Fatalf("debug location: %v %v", dirs, err)
|
||||
}
|
||||
if !fail {
|
||||
if _, err := os.Stat(f.output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessInteractionWriteFailureIsVisibleAndNonfatal(t *testing.T) {
|
||||
f := newProcessFixture(t, "normal", "[grammar]")
|
||||
code, _, baselineSummary := f.run(t, true)
|
||||
if code != 0 {
|
||||
t.Fatal(baselineSummary)
|
||||
}
|
||||
baseline, err := os.ReadFile(f.output)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.provider.mu.Lock()
|
||||
f.provider.beforeResponse = func() {
|
||||
dirs, err := filepath.Glob(filepath.Join(f.dir, "runs", "run-*"))
|
||||
if err != nil || len(dirs) != 1 {
|
||||
t.Errorf("debug directory: %v %v", dirs, err)
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dirs[0], "grammar"), []byte("block interaction writes"), 0600); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
f.provider.mu.Unlock()
|
||||
code, _, stderr := f.run(t, true, "--debug")
|
||||
if code != 0 || !strings.HasPrefix(stderr, strings.SplitN(baselineSummary, "\n", 2)[0]) {
|
||||
t.Fatalf("changed outcome: %d %q", code, stderr)
|
||||
}
|
||||
if !strings.Contains(stderr, "debug proposal interaction") || !strings.Contains(stderr, "debug validation interaction") {
|
||||
t.Fatalf("missing debug failures: %q", stderr)
|
||||
}
|
||||
output, err := os.ReadFile(f.output)
|
||||
if err != nil || !bytes.Equal(output, baseline) {
|
||||
t.Fatalf("changed transcript: %s %v", output, err)
|
||||
}
|
||||
document := f.errors(t)
|
||||
if len(document.Errors) != 0 || *document.Summary.Recoveries != 0 {
|
||||
t.Fatalf("debug failure classified as model incident: %+v", document)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user