The fourth review (hq issue 339): the safe reading of a file that asks for a setting and does not say is that root or a consumer trusts it, so its settings are the terminal's; `"trusted": false` is the opt-out. With that, nothing unsafe is left to refuse: `module check` lists and counts the unmarked files and never refuses them.
113 lines
4.5 KiB
Go
113 lines
4.5 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// Which settings are the controller's terminal's alone (novox/hq issue 339).
|
|
//
|
|
// A setting is the operator's word on how a module is configured, and the `settings` verb writes it for any
|
|
// caller allowed to call verbs — agents among them. Most settings change only the module itself. Some change
|
|
// what root or another module trusts, and those are said at the terminal alone, never through a verb:
|
|
//
|
|
// 1. `places` and `accesses`: where the node-engine creates and, as root, owns a module's directories, and
|
|
// which of the machine's paths are mounted into its container.
|
|
// 2. **Every key a provider serves**, and every setting a served value asks for. A setting overrides a served
|
|
// key (Settle), and what is served is what every consumer of the provision connects to and believes: a
|
|
// database's port, an object store's scheme, a registry's port, an identity provider's issuer and token
|
|
// path. Through a verb, any caller could point every consumer at a listener of its own and collect the
|
|
// credentials they present.
|
|
// 3. **Every setting a file asks for, unless the file says `"trusted": false`.** A file root or a consumer trusts —
|
|
// a logind drop-in, an env file that says which uid a container runs as, a script run as root — must not
|
|
// change through a verb, and the safe reading of a file that says nothing is that it is one of them (fail
|
|
// closed). `"trusted": false` is the opt-out, for a file nothing trusts: a person's own notifier settings.
|
|
// `module check` lists the files that say nothing, so an author can opt one out where that is true.
|
|
//
|
|
// Derived from the manifest, never listed by hand, so a provider or a trusted file added tomorrow is covered.
|
|
|
|
// TrustedField is the key a file resource carries to say whether the settings it asks for are trusted: true, or
|
|
// absent, makes each a terminal key; false says, out loud, that none changes what root or a consumer trusts. Said in the
|
|
// catalogue, never on the machine: the composer takes it out before the node-engine, which parses strictly.
|
|
const TrustedField = "trusted"
|
|
|
|
// TerminalKeys are the settings keys of a module that are set at the controller's terminal alone: places and
|
|
// accesses, every key its provisions serve and every setting a served value asks for, and every setting a file
|
|
// asks for unless it says `"trusted": false`. Places and accesses first, then the rest sorted.
|
|
func TerminalKeys(m Manifest) []string {
|
|
keys := map[string]bool{}
|
|
for _, served := range m.Serves {
|
|
for key, value := range served {
|
|
keys[key] = true
|
|
if s, ok := value.(string); ok {
|
|
for _, asked := range settingsUsed(s) {
|
|
keys[asked] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
continue
|
|
}
|
|
if trusted, said := r[TrustedField].(bool); said && !trusted {
|
|
continue
|
|
}
|
|
if content, ok := r["content"].(string); ok {
|
|
for _, asked := range settingsUsed(content) {
|
|
keys[asked] = true
|
|
}
|
|
}
|
|
}
|
|
delete(keys, PlacesSetting)
|
|
delete(keys, AccessesSetting)
|
|
rest := make([]string, 0, len(keys))
|
|
for k := range keys {
|
|
rest = append(rest, k)
|
|
}
|
|
sort.Strings(rest)
|
|
return append([]string{PlacesSetting, AccessesSetting}, rest...)
|
|
}
|
|
|
|
// UnsaidTrust is every file of a module that asks for a setting and does not say whether it is trusted, by id:
|
|
// each counts as trusted, and is listed so an author can opt out a file nothing trusts.
|
|
func UnsaidTrust(m Manifest) []string {
|
|
var out []string
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
continue
|
|
}
|
|
content, _ := r["content"].(string)
|
|
if len(settingsUsed(content)) == 0 {
|
|
continue
|
|
}
|
|
if _, said := r[TrustedField]; !said {
|
|
out = append(out, fmt.Sprint(r["id"]))
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// TrustProblems are the ways a manifest states `trusted` wrongly: anything but true or false, or on anything but
|
|
// a file. Refused at registration and by `module check`.
|
|
func TrustProblems(m Manifest) []string {
|
|
var out []string
|
|
for _, r := range m.Resources {
|
|
v, said := r[TrustedField]
|
|
if !said {
|
|
continue
|
|
}
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
out = append(out, fmt.Sprintf("%s: %v is a %v and says %q; only a file says whether the settings it "+
|
|
"asks for are trusted (novox/hq issue 339)", m.Module, r["id"], r["type"], TrustedField))
|
|
continue
|
|
}
|
|
if _, ok := v.(bool); !ok {
|
|
out = append(out, fmt.Sprintf("%s: %v says %q as %v; it is true or false (novox/hq issue 339)",
|
|
m.Module, r["id"], TrustedField, v))
|
|
}
|
|
}
|
|
return out
|
|
}
|