Through the settings verb, or a settings line run by the generic command verb, any caller of the mesh's console could place a module's directory at /etc with an owner of its own and have the node-engine, as root, hand it over at the next push, or mount any of the machine's paths into a container (hq issue 339). A change to either key is now refused in every process a verb runs, the generic verb refuses settings writes outright, and neither key may name the machine's own trees from anywhere, the terminal included. A line break, carriage return or NUL in any setting, which a file it is written into reads as a line of the caller's own, is refused where a layer is kept and where it is composed; PEM blocks alone may hold lines.
84 lines
4.2 KiB
Go
84 lines
4.2 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// No placement and no access at the machine's own system or the mesh's state, however it is spelled (novox/hq
|
|
// issue 339); a module's own place elsewhere is taken.
|
|
func TestAPlacementOrAnAccessAtTheMachinesOwnIsRefused(t *testing.T) {
|
|
m := Manifest{Module: "notes", Resources: []map[string]any{{"id": "data", "type": "directory"}},
|
|
Accesses: []Access{{ID: "media"}}}
|
|
for _, path := range []string{"/", "/etc", "/etc/", "/etc/sudoers.d", "/usr/bin", "/root", "/var", "/var/lib",
|
|
"/home", "/home/", "/run", "/run/user/1000", "/var/lib/mesh/x", "/var/lib/mesh-host", "/var/lib/mesh-host/identity",
|
|
"/srv/../etc", "//etc", "/proc/1", "/sys", "/dev", "/boot/efi", "/bin", "/sbin", "/lib", "/lib64"} {
|
|
layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}}
|
|
if _, err := Places(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") {
|
|
t.Errorf("a place at %s: %v", path, err)
|
|
}
|
|
layers = []Layer{{From: "laptop", Values: map[string]any{AccessesSetting: map[string]any{"media": path}}}}
|
|
if _, err := AccessPlaces(m, layers); err == nil || !strings.Contains(err.Error(), "issue 339") {
|
|
t.Errorf("an access at %s: %v", path, err)
|
|
}
|
|
}
|
|
for _, path := range []string{"/srv/notes", "/mnt/plex/data", "/storage/media", "/services/media/movies",
|
|
"/var/lib/notes/data", "/home/restic/repo", "/var/lib/mesh-store"} {
|
|
layers := []Layer{{From: "laptop", Values: map[string]any{PlacesSetting: map[string]any{"data": path}}}}
|
|
if got, err := Places(m, layers); err != nil || got["data"].Path != path {
|
|
t.Errorf("a place at %s: %v %v", path, got, err)
|
|
}
|
|
layers = []Layer{{From: "laptop", Values: map[string]any{AccessesSetting: map[string]any{"media": path}}}}
|
|
if got, err := AccessPlaces(m, layers); err != nil || got["media"] != path {
|
|
t.Errorf("an access at %s: %v %v", path, got, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A line break, a carriage return or a NUL in any string of any setting is refused, at any depth, keys too —
|
|
// where a layer is judged, which is both where it is kept and where it is composed (novox/hq issue 339).
|
|
func TestASettingHoldsOneLine(t *testing.T) {
|
|
m := Manifest{Module: "mailu"}
|
|
for _, v := range []any{"a\nDEBUG=1", "a\rb", "a\x00b", []any{"ok", "x\ny"},
|
|
map[string]any{"k": []any{"ok", map[string]any{"deep": "x\ny"}}}, map[string]any{"k\nx": "v"}} {
|
|
if err := JudgeSettings(m, []Layer{{From: "home", Values: map[string]any{"v": v}}}, false); err == nil ||
|
|
!strings.Contains(err.Error(), "line break") {
|
|
t.Errorf("%q: %v", v, err)
|
|
}
|
|
}
|
|
if err := JudgeSettings(m, []Layer{{From: "home", Values: map[string]any{"v\nPATH": "x"}}}, false); err == nil {
|
|
t.Error("a line break in a key was taken")
|
|
}
|
|
if err := JudgeSettings(m, []Layer{{From: "home", Values: map[string]any{"v": "one line", "n": 3.0,
|
|
"l": []any{"a", "b"}}}}, false); err != nil {
|
|
t.Errorf("one line each: %v", err)
|
|
}
|
|
}
|
|
|
|
// PEM blocks alone may hold lines: a provider serves its authority's root as a setting. Anything around them, or
|
|
// a line in them that is not base64, is refused.
|
|
func TestAPEMBlockIsTheOneSettingWithLines(t *testing.T) {
|
|
m := Manifest{Module: "route-proxy"}
|
|
full := strings.Repeat("MIIB", 16)
|
|
pem := "-----BEGIN CERTIFICATE-----\n" + full + "\n" + full + "\nAbCd+/==\n-----END CERTIFICATE-----\n"
|
|
for _, ok := range []string{pem, pem + pem, strings.TrimSuffix(pem, "\n"),
|
|
"-----BEGIN CERTIFICATE-----\nMIIBeDCCAR2gAwIBAgIQfake000000000000000000000000\n-----END CERTIFICATE-----\n"} {
|
|
if err := JudgeSettings(m, []Layer{{From: "anchor", Values: map[string]any{"root": ok}}}, false); err != nil {
|
|
t.Errorf("a PEM block: %v", err)
|
|
}
|
|
}
|
|
for _, bad := range []string{
|
|
pem + "PATH=/tmp\n",
|
|
"PATH=\n" + pem,
|
|
"-----BEGIN CERTIFICATE-----\n" + full + "\nPATH=\n-----END CERTIFICATE-----\n",
|
|
"-----BEGIN CERTIFICATE-----\nshort\n" + full + "\n-----END CERTIFICATE-----\n",
|
|
"-----BEGIN CERTIFICATE-----\n" + full + "\n-----END KEY-----\n",
|
|
"-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n",
|
|
"-----BEGIN CERTIFICATE-----\r\n" + full + "\r\n-----END CERTIFICATE-----\r\n",
|
|
} {
|
|
if err := JudgeSettings(m, []Layer{{From: "anchor", Values: map[string]any{"root": bad}}}, false); err == nil {
|
|
t.Errorf("taken: %q", bad)
|
|
}
|
|
}
|
|
}
|