Skip to content

Commit 55a4850

Browse files
authored
fix: critical issue go/command-injection (#56)
* fix: critical issue go/command-injection * fix: retrieve home dir with os.UserHomeDir()
1 parent 283099d commit 55a4850

3 files changed

Lines changed: 111 additions & 7 deletions

File tree

pkg/console/console.go

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package console
22

33
import (
44
"fmt"
5+
"os"
56
"os/exec"
7+
"path/filepath"
68
"strings"
79

810
log "github.com/sirupsen/logrus"
@@ -14,7 +16,7 @@ func RunCmd(sudo bool, command string, args ...string) (string, error) {
1416

1517
var stdout strings.Builder
1618
var stderr strings.Builder
17-
19+
1820
if sudo {
1921
// Check if sudo is available
2022
_, err := exec.LookPath("sudo")
@@ -26,7 +28,7 @@ func RunCmd(sudo bool, command string, args ...string) (string, error) {
2628
}
2729
} else {
2830
cmd = exec.Command(command, args...)
29-
}
31+
}
3032

3133
cmd.Stdout = &stdout
3234
cmd.Stderr = &stderr
@@ -37,4 +39,107 @@ func RunCmd(sudo bool, command string, args ...string) (string, error) {
3739
}
3840

3941
return stdout.String(), nil
40-
}
42+
}
43+
44+
func WgShow() (string, error) {
45+
var stdout strings.Builder
46+
var stderr strings.Builder
47+
48+
cmd := exec.Command("sudo", "wg", "show")
49+
50+
cmd.Stdout = &stdout
51+
cmd.Stderr = &stderr
52+
53+
err := cmd.Run()
54+
if err != nil {
55+
return stdout.String(), fmt.Errorf("wg show command execution failed: %w, stderr: %v, stdout: %v", err, stderr.String(), stdout.String())
56+
}
57+
58+
return stdout.String(), nil
59+
}
60+
61+
func WgUp(cfgPath string) error {
62+
cleanedPath, err := sanitizeWgConfigPath(cfgPath)
63+
if err != nil {
64+
return fmt.Errorf("invalid WireGuard config path: %w", err)
65+
}
66+
67+
var stdout strings.Builder
68+
var stderr strings.Builder
69+
70+
cmd := exec.Command("sudo", "wg-quick", "up", cleanedPath)
71+
72+
cmd.Stdout = &stdout
73+
cmd.Stderr = &stderr
74+
75+
err = cmd.Run()
76+
if err != nil {
77+
return fmt.Errorf("wg-quick up command execution failed: %w, stderr: %v, stdout: %v", err, stderr.String(), stdout.String())
78+
}
79+
80+
return nil
81+
}
82+
83+
func WgDown(cfgPath string) error {
84+
cleanedPath, err := sanitizeWgConfigPath(cfgPath)
85+
if err != nil {
86+
return fmt.Errorf("invalid WireGuard config path: %w", err)
87+
}
88+
89+
var stdout strings.Builder
90+
var stderr strings.Builder
91+
92+
cmd := exec.Command("sudo", "wg-quick", "down", cleanedPath)
93+
94+
cmd.Stdout = &stdout
95+
cmd.Stderr = &stderr
96+
97+
err = cmd.Run()
98+
if err != nil {
99+
return fmt.Errorf("wg-quick down command execution failed: %w, stderr: %v, stdout: %v", err, stderr.String(), stdout.String())
100+
}
101+
102+
return nil
103+
}
104+
105+
func sanitizeWgConfigPath(cfgPath string) (string, error) {
106+
// Clean the path (removes .., redundant separators, etc.)
107+
cleanPath := filepath.Clean(cfgPath)
108+
109+
// Expand home directory if present
110+
home, err := os.UserHomeDir()
111+
if err != nil {
112+
return "", fmt.Errorf("failed to get home directory: %w", err)
113+
} else if strings.HasPrefix(cleanPath, "~/") {
114+
cleanPath = filepath.Join(home, cleanPath[2:])
115+
}
116+
117+
// Convert to absolute path
118+
absPath, err := filepath.Abs(cleanPath)
119+
if err != nil {
120+
return "", fmt.Errorf("failed to resolve absolute path: %w", err)
121+
}
122+
123+
// Verify the file exists and is a regular file
124+
fileInfo, err := os.Stat(absPath)
125+
if err != nil {
126+
return "", fmt.Errorf("config file not accessible: %w", err)
127+
}
128+
129+
if !fileInfo.Mode().IsRegular() {
130+
return "", fmt.Errorf("config path is not a regular file")
131+
}
132+
133+
// Ensure it's within the expected config directory
134+
expectedBase := filepath.Join(home, ".config", "mbvpn", "servers")
135+
if !strings.HasPrefix(absPath, expectedBase) {
136+
return "", fmt.Errorf("config file must be within %s", expectedBase)
137+
}
138+
139+
// Verify file extension (WireGuard configs must be .conf)
140+
if filepath.Ext(absPath) != ".conf" {
141+
return "", fmt.Errorf("config file must have .conf extension")
142+
}
143+
144+
return absPath, nil
145+
}

pkg/remote/holocron.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
)
1616

1717
const (
18-
// baseUrl = "https://idms-holocron-stage.mwbsys.com/graphql"
1918
productCode = "MBMA-C"
2019
productVersion = "5.14.0"
2120
)

pkg/vpn/vpn.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func (vpn *DefaultVpn) Connect(cfg string) error {
268268
}
269269

270270
output.PrintMsg(fmt.Sprintf("Calling 'wg-quick up %s'", cfgPath), output.MsgOutput)
271-
_, err = console.RunCmd(true, "wg-quick", "up", cfgPath)
271+
err = console.WgUp(cfgPath)
272272
if err != nil {
273273
return errors.NewVPNError("connect", err)
274274
}
@@ -301,7 +301,7 @@ func (vpn *DefaultVpn) Disconnect(cfg string) error {
301301
return errors.NewConfigError("ensure config directory", err)
302302
}
303303

304-
_, err = console.RunCmd(true, "wg-quick", "down", filepath.Join(cfgDir, cfg+".conf"))
304+
err = console.WgDown(filepath.Join(cfgDir, cfg+".conf"))
305305
if err != nil {
306306
return errors.NewVPNError("disconnect", err)
307307
}
@@ -380,7 +380,7 @@ AllowedIPs = 0.0.0.0/0, ::/0`,
380380
}
381381

382382
func getConnectedServers() ([]string, error) {
383-
output, err := console.RunCmd(true, "wg", "show")
383+
output, err := console.WgShow()
384384
if err != nil {
385385
return nil, fmt.Errorf("failed to execute 'wg show': %w", err)
386386
}

0 commit comments

Comments
 (0)