Skip to content

Commit b519e2c

Browse files
authored
fix(policy): surface all rule fields in CLI and Telegram listings (#31)
Telegram `/policy show` only printed id, target, and ports. Protocols, replacement, name, and source were hidden, so scoped rules and redact substitutions were invisible to operators. CLI `policy list` also omitted replacement for redact rules. - `internal/telegram/commands.go`: add protocols to both store and snapshot paths. Store path also prints `-> "replacement"`, `(name)`, `[source]`. - `cmd/sluice/policy.go`: append `-> "replacement"` after protocols. - Tests: `TestPolicyShowIncludesAllFields`, `TestHandlePolicyListShowsReplacement`. API is unchanged, `storeRuleToAPI` already exposes every field.
1 parent 1ae8b29 commit b519e2c

4 files changed

Lines changed: 88 additions & 1 deletion

File tree

cmd/sluice/policy.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,15 @@ func handlePolicyList(args []string) error {
7878
if len(r.Protocols) > 0 {
7979
proto = " protocols=" + strings.Join(r.Protocols, ",")
8080
}
81+
replacement := ""
82+
if r.Replacement != "" {
83+
replacement = fmt.Sprintf(" -> %q", r.Replacement)
84+
}
8185
name := ""
8286
if r.Name != "" {
8387
name = " (" + r.Name + ")"
8488
}
85-
fmt.Printf("[%d] %s %s%s%s%s [%s]\n", r.ID, r.Verdict, target, ports, proto, name, r.Source)
89+
fmt.Printf("[%d] %s %s%s%s%s%s [%s]\n", r.ID, r.Verdict, target, ports, proto, replacement, name, r.Source)
8690
}
8791
return nil
8892
}

cmd/sluice/policy_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,32 @@ func TestHandlePolicyListShowsPorts(t *testing.T) {
159159
}
160160
}
161161

162+
func TestHandlePolicyListShowsReplacement(t *testing.T) {
163+
dir := t.TempDir()
164+
dbPath := filepath.Join(dir, "test.db")
165+
db, err := store.New(dbPath)
166+
if err != nil {
167+
t.Fatalf("create test DB: %v", err)
168+
}
169+
if _, err := db.AddRule("redact", store.RuleOpts{
170+
Pattern: `sk-[A-Za-z0-9]+`,
171+
Replacement: "sk-REDACTED",
172+
}); err != nil {
173+
t.Fatalf("add redact rule: %v", err)
174+
}
175+
_ = db.Close()
176+
177+
output := capturePolicyOutput(t, func() {
178+
if err := handlePolicyList([]string{"--db", dbPath}); err != nil {
179+
t.Fatalf("handlePolicyList: %v", err)
180+
}
181+
})
182+
183+
if !strings.Contains(output, `-> "sk-REDACTED"`) {
184+
t.Errorf("expected replacement in output: %s", output)
185+
}
186+
}
187+
162188
// --- handlePolicyAdd tests ---
163189

164190
func TestHandlePolicyAddAllow(t *testing.T) {

internal/telegram/commands.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ func (h *CommandHandler) policyShow() string {
240240
b.WriteString(" ports=")
241241
b.WriteString(formatPorts(r.Ports))
242242
}
243+
if len(r.Protocols) > 0 {
244+
b.WriteString(" protocols=")
245+
b.WriteString(strings.Join(r.Protocols, ","))
246+
}
243247
b.WriteString("\n")
244248
}
245249
}
@@ -303,6 +307,19 @@ func (h *CommandHandler) policyShowFromStore() string {
303307
b.WriteString(" ports=")
304308
b.WriteString(formatPorts(r.Ports))
305309
}
310+
if len(r.Protocols) > 0 {
311+
b.WriteString(" protocols=")
312+
b.WriteString(strings.Join(r.Protocols, ","))
313+
}
314+
if r.Replacement != "" {
315+
fmt.Fprintf(&b, " -> %q", r.Replacement)
316+
}
317+
if r.Name != "" {
318+
fmt.Fprintf(&b, " (%s)", r.Name)
319+
}
320+
if r.Source != "" {
321+
fmt.Fprintf(&b, " [%s]", r.Source)
322+
}
306323
b.WriteString("\n")
307324
}
308325
}

internal/telegram/commands_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,46 @@ func TestPolicyPersistence(t *testing.T) {
445445
}
446446
}
447447

448+
func TestPolicyShowIncludesAllFields(t *testing.T) {
449+
s := newTestStore(t)
450+
451+
if _, err := s.AddRule("allow", store.RuleOpts{
452+
Destination: "example.com",
453+
Ports: []int{443},
454+
Protocols: []string{"quic"},
455+
Name: "test rule",
456+
Source: "manual",
457+
}); err != nil {
458+
t.Fatal(err)
459+
}
460+
if _, err := s.AddRule("redact", store.RuleOpts{
461+
Pattern: `sk-[A-Za-z0-9]+`,
462+
Replacement: "sk-REDACTED",
463+
Source: "seed",
464+
}); err != nil {
465+
t.Fatal(err)
466+
}
467+
468+
handler := newTestHandlerWithStore(t, s, nil, "")
469+
out := handler.Handle(&Command{Name: "policy", Args: []string{"show"}})
470+
471+
mustContain := []string{
472+
"example.com",
473+
"ports=443",
474+
"protocols=quic",
475+
"(test rule)",
476+
"[manual]",
477+
"pattern:sk-[A-Za-z0-9]+",
478+
`-> "sk-REDACTED"`,
479+
"[seed]",
480+
}
481+
for _, want := range mustContain {
482+
if !strings.Contains(out, want) {
483+
t.Errorf("policy show output missing %q\nfull output:\n%s", want, out)
484+
}
485+
}
486+
}
487+
448488
func TestPolicyRemoveThenRecompile(t *testing.T) {
449489
s := newTestStore(t)
450490
id, err := s.AddRule("allow", store.RuleOpts{Destination: "to-remove.com"})

0 commit comments

Comments
 (0)