session_environment.go RawHistoryFolder
1package client
2
3import (
4 "errors"
5 "path/filepath"
6 "strings"
7 "unicode/utf8"
8
9 "github.com/bnema/vev/internal/ports"
10)
11
12// SessionEnvironmentProvenance names where a session environment came from.
13type SessionEnvironmentProvenance uint8
14
15const (
16 // SessionEnvironmentUnspecified is the zero value and never validates.
17 SessionEnvironmentUnspecified SessionEnvironmentProvenance = iota
18 // SessionEnvironmentLocalCLI carries environment and working directory
19 // captured from a local CLI invocation.
20 SessionEnvironmentLocalCLI
21 // SessionEnvironmentLocalPicker carries environment and working directory
22 // captured from a local picker selection.
23 SessionEnvironmentLocalPicker
24 // SessionEnvironmentRemote carries no local environment: remote sessions
25 // must not forward local variables or a local working directory.
26 SessionEnvironmentRemote
27)
28
29// SessionEnvironment carries the environment variables and working directory
30// proposed for a session creation.
31type SessionEnvironment struct {
32 Provenance SessionEnvironmentProvenance
33 Env []string
34 Cwd string
35}
36
37// Clone returns a deep copy with an independent environment slice.
38func (e SessionEnvironment) Clone() SessionEnvironment {
39 out := e
40 if e.Env != nil {
41 out.Env = make([]string, len(e.Env))
42 copy(out.Env, e.Env)
43 }
44 return out
45}
46
47// Validate reports whether the environment is well-formed for its provenance.
48func (e SessionEnvironment) Validate() error {
49 switch e.Provenance {
50 case SessionEnvironmentLocalCLI, SessionEnvironmentLocalPicker:
51 if len(e.Env) > ports.BrokerMaxEnvEntries {
52 return errors.New("client: session environment has too many entries")
53 }
54 for _, entry := range e.Env {
55 if !utf8.ValidString(entry) {
56 return errors.New("client: session environment entry is not valid UTF-8")
57 }
58 if strings.ContainsRune(entry, 0) {
59 return errors.New("client: session environment entry contains NUL")
60 }
61 if len(entry) > ports.BrokerMaxEnvEntryBytes {
62 return errors.New("client: session environment entry too long")
63 }
64 name, _, found := strings.Cut(entry, "=")
65 if !found || name == "" {
66 return errors.New("client: session environment entry must be NAME=value")
67 }
68 }
69 if e.Cwd != "" {
70 if !utf8.ValidString(e.Cwd) {
71 return errors.New("client: session environment cwd is not valid UTF-8")
72 }
73 if strings.ContainsRune(e.Cwd, 0) {
74 return errors.New("client: session environment cwd contains NUL")
75 }
76 if len(e.Cwd) > 65535 {
77 return errors.New("client: session environment cwd too long")
78 }
79 if !filepath.IsAbs(e.Cwd) {
80 return errors.New("client: session environment cwd must be absolute")
81 }
82 }
83 return nil
84 case SessionEnvironmentRemote:
85 if len(e.Env) != 0 {
86 return errors.New("client: remote session environment must not carry environment entries")
87 }
88 if e.Cwd != "" {
89 return errors.New("client: remote session environment must not carry a working directory")
90 }
91 return nil
92 default:
93 return errors.New("client: session environment provenance is unspecified or unknown")
94 }
95}