package host import ( "bufio" "bytes" "errors" "path/filepath" "regexp" "strings" ) var errActivation = errors.New("claim activation: input or protected state refused") func activationEnvironment(raw []byte) (map[string]string, error) { values := map[string]string{} scan := bufio.NewScanner(bytes.NewReader(raw)) for scan.Scan() { line := strings.TrimSpace(scan.Text()) if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, ";") { continue } key, value, ok := strings.Cut(line, "=") key = strings.TrimSpace(key) value = strings.TrimSpace(value) if !ok || !regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`).MatchString(key) { return nil, errActivation } if _, exists := values[key]; exists { return nil, errActivation } if len(value) >= 2 && (value[0] == '"' && value[len(value)-1] == '"' || value[0] == '\'' && value[len(value)-1] == '\'') { value = value[1 : len(value)-1] } if strings.ContainsAny(value, "\\\"'\x00\r\n") { return nil, errActivation } values[key] = value } if scan.Err() != nil { return nil, scan.Err() } return values, nil } func activationUnitSettings(raw []byte) (claimUnitSettings, error) { v, err := activationEnvironment(raw) if err != nil { return claimUnitSettings{}, err } cfg := claimUnitSettings{enabled: strings.TrimSpace(v["CARLOS_HOST_CLAIM"]) != "", data: v["CARLOS_DATA_DIR"], opt: v["CARLOS_OPT_DIR"]} if !cfg.enabled || v["CARLOS_REGION"] != "" { return claimUnitSettings{}, errActivation } if cfg.data == "" { cfg.data = "/data" } if cfg.opt == "" { cfg.opt = "/opt/carlos" } for _, p := range []string{cfg.data, cfg.opt} { if !filepath.IsAbs(p) || filepath.Clean(p) != p || p == "/" || strings.ContainsAny(p, "\x00\r\n") { return claimUnitSettings{}, errActivation } } return cfg, nil } type activationFenceScope struct{ Bucket, Region, Role string }