|
1 | 1 | package writ_test |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bufio" |
| 5 | + "encoding/json" |
| 6 | + "os" |
| 7 | + "path/filepath" |
4 | 8 | "testing" |
5 | 9 | "time" |
6 | 10 |
|
7 | 11 | "github.com/opskernel-io/writ" |
8 | 12 | ) |
9 | 13 |
|
| 14 | +// testConfig creates a minimal writ.Config backed by a temp directory. |
| 15 | +func testConfig(t *testing.T) writ.Config { |
| 16 | + t.Helper() |
| 17 | + dir := t.TempDir() |
| 18 | + policyPath := filepath.Join(dir, "policy") |
| 19 | + if err := os.MkdirAll(policyPath, 0o700); err != nil { |
| 20 | + t.Fatalf("mkdir policy: %v", err) |
| 21 | + } |
| 22 | + const policy = "package writ.gate\nimport rego.v1\ndefault allow := true\ndefault tier := 2\ndefault denial_reason := \"\"" |
| 23 | + if err := os.WriteFile(filepath.Join(policyPath, "writ.rego"), []byte(policy), 0o600); err != nil { |
| 24 | + t.Fatalf("write test policy: %v", err) |
| 25 | + } |
| 26 | + return writ.Config{ |
| 27 | + PolicyPath: policyPath, |
| 28 | + AuditPath: filepath.Join(dir, "audit.chain"), |
| 29 | + CallerID: "test-agent", |
| 30 | + } |
| 31 | +} |
| 32 | + |
10 | 33 | func TestMemoryStoreAppendAndVerify(t *testing.T) { |
11 | 34 | store := writ.NewMemoryStore() |
12 | 35 |
|
@@ -58,3 +81,85 @@ func TestDenialErrorMessage(t *testing.T) { |
58 | 81 | t.Fatal("DenialError.Error() returned empty string") |
59 | 82 | } |
60 | 83 | } |
| 84 | + |
| 85 | +// PR 3: StoreFullInputs tests. |
| 86 | + |
| 87 | +func TestStoreFullInputsCreatesPayloadsFile(t *testing.T) { |
| 88 | + cfg := testConfig(t) |
| 89 | + cfg.StoreFullInputs = true |
| 90 | + c, err := writ.New(cfg) |
| 91 | + if err != nil { |
| 92 | + t.Fatalf("New: %v", err) |
| 93 | + } |
| 94 | + wantPath := cfg.AuditPath + ".payloads" |
| 95 | + if c.PayloadsPath() != wantPath { |
| 96 | + t.Fatalf("want PayloadsPath=%q, got %q", wantPath, c.PayloadsPath()) |
| 97 | + } |
| 98 | + if _, err := os.Stat(wantPath); err != nil { |
| 99 | + t.Fatalf("payloads file not created: %v", err) |
| 100 | + } |
| 101 | + fi, err := os.Stat(wantPath) |
| 102 | + if err != nil { |
| 103 | + t.Fatalf("stat payloads file: %v", err) |
| 104 | + } |
| 105 | + if fi.Mode().Perm() != 0o600 { |
| 106 | + t.Errorf("want payloads file mode 0600, got %#o", fi.Mode().Perm()) |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +func TestStoreFullInputsDisabledNoFile(t *testing.T) { |
| 111 | + cfg := testConfig(t) |
| 112 | + // StoreFullInputs defaults to false. |
| 113 | + c, err := writ.New(cfg) |
| 114 | + if err != nil { |
| 115 | + t.Fatalf("New: %v", err) |
| 116 | + } |
| 117 | + if c.PayloadsPath() != "" { |
| 118 | + t.Errorf("want empty PayloadsPath when StoreFullInputs=false, got %q", c.PayloadsPath()) |
| 119 | + } |
| 120 | + if _, err := os.Stat(cfg.AuditPath + ".payloads"); !os.IsNotExist(err) { |
| 121 | + t.Error("want no payloads file when StoreFullInputs=false") |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +func TestStoreFullInputsAuditWritesPayload(t *testing.T) { |
| 126 | + cfg := testConfig(t) |
| 127 | + cfg.StoreFullInputs = true |
| 128 | + c, err := writ.New(cfg) |
| 129 | + if err != nil { |
| 130 | + t.Fatalf("New: %v", err) |
| 131 | + } |
| 132 | + |
| 133 | + if err := c.Audit(writ.AuditEvent{ |
| 134 | + EventType: "tool_use", |
| 135 | + ActionType: "read_file", |
| 136 | + Metadata: map[string]string{"path": "/etc/passwd", "reason": "audit test"}, |
| 137 | + }); err != nil { |
| 138 | + t.Fatalf("Audit: %v", err) |
| 139 | + } |
| 140 | + |
| 141 | + f, err := os.Open(c.PayloadsPath()) |
| 142 | + if err != nil { |
| 143 | + t.Fatalf("open payloads file: %v", err) |
| 144 | + } |
| 145 | + defer f.Close() |
| 146 | + |
| 147 | + var entries []map[string]json.RawMessage |
| 148 | + scanner := bufio.NewScanner(f) |
| 149 | + for scanner.Scan() { |
| 150 | + var e map[string]json.RawMessage |
| 151 | + if err := json.Unmarshal(scanner.Bytes(), &e); err != nil { |
| 152 | + t.Fatalf("unmarshal payload entry: %v", err) |
| 153 | + } |
| 154 | + entries = append(entries, e) |
| 155 | + } |
| 156 | + if len(entries) != 1 { |
| 157 | + t.Fatalf("want 1 payload entry, got %d", len(entries)) |
| 158 | + } |
| 159 | + if _, ok := entries[0]["audit_id"]; !ok { |
| 160 | + t.Error("payload entry missing audit_id") |
| 161 | + } |
| 162 | + if _, ok := entries[0]["input"]; !ok { |
| 163 | + t.Error("payload entry missing input") |
| 164 | + } |
| 165 | +} |
0 commit comments