nats_stream_messages refuses a module's kept state (KV_*, OBJ_*), which can hold secrets (issue 1966, review 1)
Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -266,7 +266,7 @@ func readTools(jsz func() ([]byte, error), open MsgOpen) []stdio.Tool {
|
||||
return Consumer(doc, arg(a, "stream"), arg(a, "consumer"))
|
||||
}},
|
||||
{Name: "nats_stream_messages",
|
||||
Description: "The messages a stream keeps for one subject (a filter with * or > is allowed), newest first: sequence, subject, time and size of each, and the body only when asked for and no larger than 4 KiB. Bounded by count (default 20, at most 200) and bytes (default 1 MiB); says when it stopped before the oldest the stream keeps. Read on the bus module's own credential.",
|
||||
Description: "The messages a stream keeps for one subject (a filter with * or > is allowed), newest first: sequence, subject, time and size of each, and the body only when asked for and no larger than 4 KiB. Bounded by count (default 20, at most 200) and bytes (default 1 MiB); says when it stopped before the oldest the stream keeps. Read on the bus module's own credential. Refuses the streams of a module's kept state (KV_*, OBJ_*): their values are read through their owners' tools; this tool is for lost events.",
|
||||
Input: map[string]any{"stream": str("the stream, e.g. EVENTS"), "subject": str("a subject or filter, e.g. mesh.mod.mesh-delivery-indexer.event.view-changed"),
|
||||
"count": num("at most this many (default 20, at most 200)"), "bytes": num("at most this many bytes of messages (default 1048576)"),
|
||||
"body": map[string]any{"type": "boolean", "description": "carry the body of each message of at most 4 KiB"}},
|
||||
@@ -275,6 +275,9 @@ func readTools(jsz func() ([]byte, error), open MsgOpen) []stdio.Tool {
|
||||
if !name.MatchString(stream) || subject == "" || strings.ContainsAny(subject, " \t") {
|
||||
return nil, errors.New("stream (one name) and subject (a subject or filter, no space) are required")
|
||||
}
|
||||
if strings.HasPrefix(stream, "KV_") || strings.HasPrefix(stream, "OBJ_") {
|
||||
return nil, fmt.Errorf("%s is a module's kept state (a bucket or an object store), which can hold secrets: it is read through its owner's tools, never here; this tool reads the events of a stream", stream)
|
||||
}
|
||||
doc, err := jsz()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -123,6 +123,11 @@ func TestTheReadToolsAreServedAndRefuseBadArguments(t *testing.T) {
|
||||
if err != nil || out.(map[string]any)["returned"].(int) != 0 && out.(map[string]any)["returned"].(int) != 1 {
|
||||
t.Fatalf("%v %v", out, err)
|
||||
}
|
||||
for _, st := range []string{"KV_mesh-vault_signers", "OBJ_mesh-knowledge_sections"} {
|
||||
if _, err := by["nats_stream_messages"](map[string]any{"stream": st, "subject": ">", "body": true}); err == nil || !strings.Contains(err.Error(), "kept state") {
|
||||
t.Fatalf("%s was read: %v", st, err)
|
||||
}
|
||||
}
|
||||
if _, err := by["nats_stream_messages"](map[string]any{"stream": "EVENTS"}); err == nil {
|
||||
t.Fatal("no subject answered")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user