package main import ( "path/filepath" "strings" "git.novox.be/novox/mesh-sdk/go/outward" ) // What is refused by name, before any read (novox/hq ADR 0394 §5), and what an answer withholds (§4). // // **Two lists of places.** The places the mesh knows a secret is kept on this node — every module's own-secret // directory, every credential file and grant directory, the vault's directories — come from the controller, // which composes them from the manifests (MESH_SECRET_PLACES). The known credential files of the programs // on a machine are this module's list, below: the mesh does not write them, so nothing else names them. Both // hold whatever the account's file modes would allow: the file modes are the first bound, the names the second. // knownDirs are directories that hold nothing but credentials, wherever they are: an account's keys. var knownDirs = map[string]bool{ ".ssh": true, ".gnupg": true, ".password-store": true, ".aws": true, ".kube": true, ".azure": true, ".mozilla": true, ".thunderbird": true, // browser and mail profiles: saved logins and cookies } // knownNames are file names that are a credential whatever directory they are in. var knownNames = map[string]bool{ ".credentials.json": true, // the agent's credentials (Claude Code) ".netrc": true, ".pgpass": true, ".git-credentials": true, ".npmrc": true, ".pypirc": true, ".my.cnf": true, ".vault-token": true, "credentials": true, "credentials.json": true, "id_rsa": true, "id_dsa": true, "id_ecdsa": true, "id_ed25519": true, "id_ecdsa_sk": true, "id_ed25519_sk": true, "shadow": true, "gshadow": true, "shadow-": true, "gshadow-": true, "opasswd": true, "broker": true, // the mesh's name for a module's bus credential // Shell and program histories: where a token is typed. ".bash_history": true, ".zsh_history": true, ".histfile": true, ".python_history": true, ".psql_history": true, ".mysql_history": true, ".lesshst": true, ".viminfo": true, ".node_repl_history": true, ".sqlite_history": true, ".rediscli_history": true, } // knownSuffixes are file name endings a credential has. var knownSuffixes = []string{".key", ".p12", ".pfx", ".jks", ".keystore", ".creds", ".nk", ".kdbx", "_key"} // knownPaths are the places on a machine that hold credentials and are not a module's. var knownPaths = []string{ "/etc/ssl/private", "/etc/wireguard", "/etc/NetworkManager/system-connections", "/etc/ipsec.d/private", "/var/lib/iwd", "/etc/docker/key.json", "/root/.docker/config.json", "/var/lib/sss/secrets", "/etc/rancher", "/etc/kubernetes/admin.conf", } // knownProfiles are directories under an account's .config that hold a browser's saved logins and cookies. var knownProfiles = map[string]bool{"google-chrome": true, "chromium": true, "BraveSoftware": true, "vivaldi": true, "microsoft-edge": true} // procPrivate are the entries of a process under /proc that reach its memory, its environment or its files. var procPrivate = map[string]bool{"environ": true, "mem": true, "fd": true, "map_files": true, "root": true, "cwd": true} // Refuser says whether a path is a place a secret is kept. type Refuser struct { // Places are the mesh's, from the controller: refused with everything beneath them. Places []string } // Refused says why a path may not be read, or "" when it may. The path is clean and absolute. func (r Refuser) Refused(path string) string { path = filepath.Clean(path) for _, p := range r.Places { if within(path, p) { return "it is a place the mesh keeps a secret (" + p + ")" } } for _, p := range knownPaths { if within(path, p) { return "it is a place a machine keeps credentials (" + p + ")" } } parts := strings.Split(strings.TrimPrefix(path, "/"), "/") if len(parts) >= 3 && parts[0] == "proc" && procPrivate[parts[2]] { return "it reaches a process's " + parts[2] } for i, part := range parts { if i > 0 && parts[i-1] == ".config" && knownProfiles[part] { return "it is " + part + "'s profile, which keeps saved logins" } if knownDirs[part] { return "it is in " + part + ", which holds an account's credentials" } if i == len(parts)-1 { if knownNames[part] { return part + " is a known credential file" } for _, s := range knownSuffixes { if strings.HasSuffix(part, s) && len(part) > len(s) { return part + " is named as a key or a credential (" + s + ")" } } if strings.HasSuffix(part, ".pem") && (strings.Contains(part, "key") || strings.Contains(part, "private")) { return part + " is named as a private key" } if part == "config.json" && i > 0 && parts[i-1] == ".docker" { return "a container registry login is kept there" } } } return "" } func within(path, place string) bool { return path == place || strings.HasPrefix(path, strings.TrimSuffix(place, "/")+"/") } // Withheld is one piece of an answer the content rule kept back: where it was, and the class it carried. type Withheld struct { At string `json:"at"` Class string `json:"why"` } // clean is the content rule's secret class (mesh-sdk outward, novox/hq ADR 0394 §4) over one piece of an // answer: the piece, or a mark in its place when it is shaped like a secret. // // **What a file holds, not what the file system names.** It judges a file's lines, a matching line, a link's // target, a mount's source and an account's words. A path and an entry's name are not judged: they are what // the caller named or the file system calls a thing, and the rule's random-run test takes a temporary // directory's or a container layer's name for a token, which would withhold the very survey asked for. A // place where a secret sits by name is refused before any read instead (Refuser). func clean(s string) (string, string) { if refusal, ok := outward.CheckSecret(s); !ok { return "‹withheld: " + refusal.What + "›", refusal.What } return s, "" }