fix: harden target recovery metadata safety
This commit is contained in:
+51
-3
@@ -22,8 +22,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
integrationStart = "# >>> proxyctl shell integration >>>"
|
||||
integrationEnd = "# <<< proxyctl shell integration <<<"
|
||||
integrationStart = "# >>> proxyctl shell integration >>>"
|
||||
integrationEnd = "# <<< proxyctl shell integration <<<"
|
||||
shellRecoveryVersion = 1
|
||||
)
|
||||
|
||||
var proxyVariables = []string{
|
||||
@@ -149,7 +150,8 @@ func (f OSFileSystem) AtomicWrite(path string, data []byte, mode os.FileMode, ow
|
||||
}
|
||||
|
||||
type recovery struct {
|
||||
Original map[string]string `json:"original,omitempty"`
|
||||
Version int `json:"version"`
|
||||
Original map[string]string `json:"original"`
|
||||
OriginalScript *string `json:"originalScript,omitempty"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
}
|
||||
@@ -288,6 +290,7 @@ func (a *Adapter) enable(active profile.Profile) error {
|
||||
return fmt.Errorf("read Shell recovery metadata: %w", err)
|
||||
}
|
||||
if !managed {
|
||||
metadata.Version = shellRecoveryVersion
|
||||
if existing, readErr := a.files.ReadFile(a.configuration.EnvironmentFile); readErr == nil && recognizesProxy(existing) {
|
||||
original := string(existing)
|
||||
metadata.OriginalScript = &original
|
||||
@@ -344,12 +347,57 @@ func (a *Adapter) readRecovery() (recovery, bool, error) {
|
||||
if err != nil {
|
||||
return recovery{}, false, err
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return recovery{}, true, err
|
||||
}
|
||||
if fields == nil {
|
||||
return recovery{}, true, errors.New("Shell recovery metadata must be a JSON object")
|
||||
}
|
||||
var value recovery
|
||||
if err := json.Unmarshal(data, &value); err != nil {
|
||||
return recovery{}, true, err
|
||||
}
|
||||
if err := validateRecovery(value, fields); err != nil {
|
||||
return recovery{}, true, err
|
||||
}
|
||||
return value, true, nil
|
||||
}
|
||||
|
||||
func validateRecovery(value recovery, fields map[string]json.RawMessage) error {
|
||||
for _, name := range []string{"version", "original", "fingerprint"} {
|
||||
if _, present := fields[name]; !present {
|
||||
return fmt.Errorf("Shell recovery metadata is missing required field %q", name)
|
||||
}
|
||||
}
|
||||
if value.Version != shellRecoveryVersion {
|
||||
return fmt.Errorf("unsupported Shell recovery metadata version %d", value.Version)
|
||||
}
|
||||
decoded, err := hex.DecodeString(value.Fingerprint)
|
||||
if err != nil || len(decoded) != sha256.Size {
|
||||
return errors.New("Shell recovery metadata has an invalid fingerprint")
|
||||
}
|
||||
_, hasOriginalScript := fields["originalScript"]
|
||||
if hasOriginalScript {
|
||||
if value.OriginalScript == nil || value.Original != nil || len(*value.OriginalScript) == 0 || !recognizesProxy([]byte(*value.OriginalScript)) {
|
||||
return errors.New("Shell recovery metadata has inconsistent original script state")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if value.Original == nil {
|
||||
return errors.New("Shell recovery metadata is missing the original environment snapshot")
|
||||
}
|
||||
for name := range value.Original {
|
||||
known := false
|
||||
for _, candidate := range proxyVariables {
|
||||
known = known || name == candidate
|
||||
}
|
||||
if !known {
|
||||
return fmt.Errorf("Shell recovery metadata contains unsupported variable %q", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (a *Adapter) ownership() *Ownership {
|
||||
if !a.configuration.OwnFiles {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user