74 lines
2.9 KiB
Go
74 lines
2.9 KiB
Go
package delivery
|
|
|
|
import (
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// secretNamed is facts.secretKey's pattern: a field so named would read as a secret, and the runtime's credential-field
|
|
// check (ADR 0201 §13) refuses such a field in a state every node reads.
|
|
var secretNamed = regexp.MustCompile(`(?i)(secret|passw|token|api[-_]?key|private[-_]?key|credential|bearer|cookie|salt|signing)`)
|
|
|
|
// Issue 1126 (ADR 0555): a node's record reads back, rides in its have key, refuses a node that is not a name and an empty capability, and
|
|
// neither it nor the placement file names a field like a secret.
|
|
func TestNodeRecord(t *testing.T) {
|
|
hub := NodeRecord{Node: "novox", TunnelKey: "k", Capabilities: []string{"docker"}, SealingKey: "age1x", ServingKey: "pub",
|
|
Certificate: "cert", OutwardLinks: []string{"eth0"}, Networks: []catalogue.AttachedNetwork{{Link: "eth0", Network: "192.0.2.0/24"}},
|
|
NetworksSaid: true, Contract: 3, Trusts: []string{"controller key sha256:aa", "vault key sha256:bb"}}
|
|
raw, err := EncodeNodeRecord(hub)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
back, err := DecodeNodeRecord(raw)
|
|
if err != nil || !SameNodeRecord(back, hub) || back.V != Version {
|
|
t.Fatalf("round trip: %+v, %v", back, err)
|
|
}
|
|
// It rides in the node's own have key: a have that differs only in its record is another value.
|
|
with, without := HaveNode{V: Version, Record: &hub}, HaveNode{V: Version}
|
|
if raw, err := EncodeHaveNode(with); err != nil {
|
|
t.Fatal(err)
|
|
} else if got, err := DecodeHaveNode(raw); err != nil || got.Record == nil || !SameHaveNode(got, with) || SameHaveNode(got, without) {
|
|
t.Fatalf("the have's record: %+v, %v", got, err)
|
|
}
|
|
changed := hub
|
|
changed.Contract = 4
|
|
if SameHaveNode(with, HaveNode{V: Version, Record: &changed}) {
|
|
t.Error("a changed record reads as the same have")
|
|
}
|
|
|
|
for name, r := range map[string]NodeRecord{
|
|
"empty capability": {Node: "ace", Capabilities: []string{""}},
|
|
"node that is not a name": {Node: "a.b"},
|
|
} {
|
|
if _, err := EncodeNodeRecord(r); err == nil {
|
|
t.Errorf("%s: taken", name)
|
|
}
|
|
}
|
|
|
|
// Every JSON field name of the record and the types it carries, walked: none reads as a secret.
|
|
var walk func(reflect.Type, string)
|
|
walk = func(ty reflect.Type, at string) {
|
|
for ty.Kind() == reflect.Pointer || ty.Kind() == reflect.Slice {
|
|
ty = ty.Elem()
|
|
}
|
|
if ty.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
for i := 0; i < ty.NumField(); i++ {
|
|
f := ty.Field(i)
|
|
name, _, _ := strings.Cut(f.Tag.Get("json"), ",")
|
|
if secretNamed.MatchString(name) || secretNamed.MatchString(f.Name) {
|
|
t.Errorf("%s.%s (%q) is named like a secret", at, f.Name, name)
|
|
}
|
|
walk(f.Type, at+"."+f.Name)
|
|
}
|
|
}
|
|
walk(reflect.TypeOf(NodeRecord{}), "NodeRecord")
|
|
// And the placement file, which holds what a person decides of a node (K2): accounts by name, never a credential.
|
|
walk(reflect.TypeOf(Placement{}), "Placement")
|
|
}
|