fix: harden local proxy and import boundaries

This commit is contained in:
Andy Dixon
2026-06-16 17:09:20 +01:00
parent 739651d548
commit cb2fbf70eb
12 changed files with 1189 additions and 49 deletions
+35 -6
View File
@@ -49,6 +49,7 @@ const (
maxSFTPUploadChunkBytes = 1024 * 1024
maxStreamingSFTPUploadSize = int64(2 * 1024 * 1024 * 1024)
maxSSHPublicKeyBytes = 64 * 1024
sshKeygenTimeout = 30 * time.Second
)
// Service is the main API surface exposed to the frontend via Wails bindings.
@@ -1809,6 +1810,10 @@ func (s *Service) GenerateSSHKey(name string, keyType string, bits int, passphra
if err != nil {
return err
}
keyType, bits, err = validateSSHKeyGenerationOptions(keyType, bits)
if err != nil {
return err
}
sshDir := filepath.Dir(keyPath)
// Ensure .ssh directory exists
@@ -1821,10 +1826,6 @@ func (s *Service) GenerateSSHKey(name string, keyType string, bits int, passphra
return fmt.Errorf("key %q already exists", name)
}
if keyType == "" {
keyType = "ed25519"
}
// Build ssh-keygen arguments
args := []string{"-t", keyType, "-f", keyPath, "-C", name + "@nexus-terminal"}
if passphrase != "" {
@@ -1837,10 +1838,15 @@ func (s *Service) GenerateSSHKey(name string, keyType string, bits int, passphra
}
// #nosec G204 — arguments are sanitised above
cmd := exec.Command("ssh-keygen", args...)
ctx, cancel := context.WithTimeout(context.Background(), sshKeygenTimeout)
defer cancel()
cmd := exec.CommandContext(ctx, "ssh-keygen", args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if ctx.Err() == context.DeadlineExceeded {
return fmt.Errorf("ssh-keygen timed out after %s", sshKeygenTimeout)
}
return fmt.Errorf("ssh-keygen failed: %s", stderr.String())
}
@@ -1929,12 +1935,35 @@ func safeSFTPUploadName(fileName string) (string, error) {
}
name = strings.ReplaceAll(name, "\\", "/")
safeName := path.Base(name)
if safeName == "" || safeName == "." || safeName == "/" || strings.ContainsRune(safeName, 0) {
if safeName == "" || safeName == "." || safeName == ".." || safeName == "/" || strings.ContainsRune(safeName, 0) {
return "", fmt.Errorf("invalid file name")
}
return safeName, nil
}
func validateSSHKeyGenerationOptions(keyType string, bits int) (string, int, error) {
keyType = strings.ToLower(strings.TrimSpace(keyType))
if keyType == "" {
keyType = "ed25519"
}
switch keyType {
case "ed25519", "ecdsa":
return keyType, bits, nil
case "rsa":
if bits == 0 {
bits = 4096
}
switch bits {
case 2048, 3072, 4096:
return keyType, bits, nil
default:
return "", 0, fmt.Errorf("unsupported RSA key size %d (use 2048, 3072, or 4096)", bits)
}
default:
return "", 0, fmt.Errorf("unsupported SSH key type %q", keyType)
}
}
func (s *Service) lookupSFTPUpload(uploadID string) (*sftpUpload, error) {
s.uploadMu.Lock()
defer s.uploadMu.Unlock()