Refuse a credential in a log entry and take a full 256 KiB value with its expect header (ADR 0297 review)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
A log keeps what it is given for good, so the state guard against secrets applies to it too; the stream holds 260 KiB a message so the headers an append carries never push a 256 KiB value over.
This commit is contained in:
@@ -57,7 +57,8 @@ type LogInfo struct {
|
||||
}
|
||||
|
||||
const (
|
||||
// LogValueMax is the most one entry's value may hold (ADR 0297 §3), the stream's own MaxMsgSize.
|
||||
// LogValueMax is the most one entry's value may hold (ADR 0297 §3); the stream's MaxMsgSize is 4 KiB
|
||||
// more, for the headers an append carries.
|
||||
LogValueMax = 256 * 1024
|
||||
// LogKeyMax is the longest key.
|
||||
LogKeyMax = 255
|
||||
@@ -156,6 +157,11 @@ func (c *Conn) LogAppend(module, name, key string, value json.RawMessage, expect
|
||||
return 0, fmt.Errorf("a log entry's value is at most %d bytes (256 KiB), and this one is %d (novox/hq ADR 0297)",
|
||||
LogValueMax, len(value))
|
||||
}
|
||||
if field := credentialField(value); field != "" {
|
||||
return 0, fmt.Errorf("%s's %s.%s carries a field %q, which names a credential: no secret is kept in a log, "+
|
||||
"which keeps it for good. Name the secret and fetch it on request/reply (novox/hq ADR 0201 §13, ADR 0297)",
|
||||
module, name, key, field)
|
||||
}
|
||||
msg := nats.NewMsg(l.Subject + "." + key)
|
||||
msg.Data = value
|
||||
if expect != nil {
|
||||
|
||||
@@ -234,7 +234,7 @@ func (m *Mesh) Bucket(t *testing.T, name string) nats.KeyValue {
|
||||
|
||||
// Log makes a module's log afresh as the controller does from the catalogue (novox/hq ADR 0297): a file
|
||||
// stream named LOG_<module>_<name> on mesh.log.<module>.<name>.>, limits retention, no age, no cap per
|
||||
// key, the declared cap, 256 KiB an entry, refusing new entries when full, direct get, no delete or
|
||||
// key, the declared cap, 260 KiB a message (a 256 KiB value and its headers), refusing new entries when full, direct get, no delete or
|
||||
// purge. It answers the membership entry the controller issues for it.
|
||||
func (m *Mesh) Log(t *testing.T, module, name string, maxBytes int64) bus.LogIssued {
|
||||
t.Helper()
|
||||
@@ -243,7 +243,7 @@ func (m *Mesh) Log(t *testing.T, module, name string, maxBytes int64) bus.LogIss
|
||||
_ = m.js.DeleteStream(stream)
|
||||
if _, err := m.js.AddStream(&nats.StreamConfig{Name: stream, Subjects: []string{subject + ".>"},
|
||||
Storage: nats.FileStorage, Retention: nats.LimitsPolicy, MaxAge: 0, MaxMsgsPerSubject: -1,
|
||||
MaxBytes: maxBytes, MaxMsgSize: 256 * 1024, Discard: nats.DiscardNew, AllowDirect: true,
|
||||
MaxBytes: maxBytes, MaxMsgSize: 260 * 1024, Discard: nats.DiscardNew, AllowDirect: true,
|
||||
DenyDelete: true, DenyPurge: true, Replicas: 1,
|
||||
Description: module + "'s log " + name + ", kept as its record (novox/hq ADR 0297)"}); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -313,6 +313,24 @@ func TestWhatALogCannotHoldIsRefusedWithTheReason(t *testing.T) {
|
||||
if info := infoOf(t, b); info["entries"] != 0 {
|
||||
t.Fatalf("a refused append wrote: %v", info)
|
||||
}
|
||||
// The largest value allowed is taken.
|
||||
appendOf(t, b, `{"log":"changes","key":"1","value":"`+strings.Repeat("x", 256*1024-2)+`"}`)
|
||||
// The largest value allowed is taken, also with the header an expect carries.
|
||||
largest := `"` + strings.Repeat("x", 256*1024-2) + `"`
|
||||
appendOf(t, b, `{"log":"changes","key":"1","value":`+largest+`,"expect":0}`)
|
||||
appendOf(t, b, `{"log":"changes","key":"2","value":`+largest+`}`)
|
||||
}
|
||||
|
||||
// A value naming a credential anywhere in it is refused, as mesh/state.put refuses one (novox/hq ADR
|
||||
// 0201 §13): a log keeps what it is given for good. A value that only counts tokens is taken.
|
||||
func TestALogRefusesAValueNamingACredential(t *testing.T) {
|
||||
b, _, _ := logBuses(t, 1<<20)
|
||||
for _, value := range []string{`{"op":"set","api_key":"s"}`, `{"op":"note","with":{"headers":{"Authorization":"Bearer s"}}}`} {
|
||||
_, err := logAsk(b, "append", `{"log":"changes","key":"1","value":`+value+`}`)
|
||||
if err == nil || !strings.Contains(err.Error(), "names a credential") {
|
||||
t.Errorf("append of %s: %v", value, err)
|
||||
}
|
||||
}
|
||||
if info := infoOf(t, b); info["entries"] != 0 {
|
||||
t.Fatalf("a refused append wrote: %v", info)
|
||||
}
|
||||
appendOf(t, b, `{"log":"changes","key":"1","value":{"op":"note","maxTokens":4096}}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user