Skip to content

Commit e85553a

Browse files
authored
Merge pull request #99 from marklogic/MLE20489/BUG-CWE-fix
MLE-20489/BUG-CWE-fix
2 parents ada4caa + 1b143be commit e85553a

File tree

5 files changed

+142
-22
lines changed

5 files changed

+142
-22
lines changed

pkg/k8sutil/common.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package k8sutil
22

33
import (
4-
"math/rand"
5-
"time"
4+
"crypto/rand"
5+
"math/big"
66

77
marklogicv1 "github.com/marklogic/marklogic-operator-kubernetes/api/v1"
88
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -108,10 +108,15 @@ func generateRandomAlphaNumeric(length int) string {
108108
const charset = "abcdefghijklmnopqrstuvwxyz" +
109109
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
110110

111-
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
112111
result := make([]byte, length)
113112
for i := range result {
114-
result[i] = charset[seededRand.Intn(len(charset))]
113+
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
114+
if err != nil {
115+
// Fallback to a deterministic character if crypto/rand fails
116+
result[i] = charset[i%len(charset)]
117+
} else {
118+
result[i] = charset[num.Int64()]
119+
}
115120
}
116121
return string(result)
117122
}

pkg/k8sutil/handler.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ func (oc *OperatorContext) ReconsileMarklogicGroupHandler() (reconcile.Result, e
1010
if result := oc.ReconcileServices(); result.Completed() {
1111
return result.Output()
1212
}
13-
setOperatorInternalStatus(oc, "Created")
13+
err := setOperatorInternalStatus(oc, "Created")
14+
if err != nil {
15+
oc.ReqLogger.Error(err, "Failed to set operator internal status")
16+
}
1417

1518
if result := oc.ReconcileConfigMap(); result.Completed() {
1619
return result.Output()

pkg/k8sutil/statefulset.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,18 @@ func (oc *OperatorContext) ReconcileStatefulset() (reconcile.Result, error) {
8080
statefulSetDef := generateStatefulSetsDef(objectMeta, statefulSetParams, marklogicServerAsOwner(cr), containerParams)
8181
if err != nil {
8282
if apierrors.IsNotFound(err) {
83-
oc.createStatefulSet(statefulSetDef, cr)
83+
err := oc.createStatefulSet(statefulSetDef, cr)
84+
if err != nil {
85+
logger.Error(err, "Failed to create statefulSet")
86+
return result.Error(err).Output()
87+
}
8488
oc.Recorder.Event(oc.MarklogicGroup, "Normal", "StatefulSetCreated", "MarkLogic statefulSet created successfully")
8589
return result.Done().Output()
8690
}
87-
result.Error(err).Output()
91+
_, outputErr := result.Error(err).Output()
92+
if outputErr != nil {
93+
logger.Error(outputErr, "Failed to process result error")
94+
}
8895
}
8996
if err != nil {
9097
logger.Error(err, "Cannot create standalone statefulSet for MarkLogic")
@@ -490,7 +497,7 @@ func generateVolumes(stsName string, containerParams containerParameters) []core
490497
},
491498
})
492499
}
493-
if containerParams.Tls.CertSecretNames != nil && len(containerParams.Tls.CertSecretNames) > 0 {
500+
if len(containerParams.Tls.CertSecretNames) > 0 {
494501
projectionSources := []corev1.VolumeProjection{}
495502
for i, secretName := range containerParams.Tls.CertSecretNames {
496503
projectionSource := corev1.VolumeProjection{

test/utils/certs.go

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,83 @@ package utils
33
import (
44
"fmt"
55
"os/exec"
6+
"path/filepath"
67
"strings"
78
)
89

10+
// validatePath performs basic validation on file paths to prevent command injection
11+
func validatePath(path string) error {
12+
// Use filepath.Clean to sanitize the path
13+
cleanPath := filepath.Clean(path)
14+
15+
// Ensure the path doesn't contain shell metacharacters
16+
if strings.ContainsAny(cleanPath, ";|&$`(){}[]<>") {
17+
return fmt.Errorf("path contains invalid characters: %s", path)
18+
}
19+
20+
// Ensure it's not an absolute path to prevent access to system directories
21+
if filepath.IsAbs(cleanPath) && !strings.HasPrefix(cleanPath, "/tmp/") {
22+
return fmt.Errorf("path must be relative or in /tmp: %s", path)
23+
}
24+
25+
return nil
26+
}
27+
928
func GenerateCertificates(path string, caPath string) error {
1029
var err error
1130
fmt.Println("====Generating TLS Certificates")
12-
geeTlsKeyCmd := strings.Replace("openssl genpkey -algorithm RSA -out path/tls.key", "path", path, -1)
13-
genCsrCmd := strings.Replace("openssl req -new -key path/tls.key -config path/server.cnf -out path/tls.csr", "path", path, -1)
14-
genCrtCmd := strings.Replace(strings.Replace("openssl x509 -req -CA caPath/cacert.pem -CAkey caPath/ca-private-key.pem -CAcreateserial -CAserial path/cacert.srl -in path/tls.csr -out path/tls.crt -days 365", "path", path, -1), "caPath", caPath, -1)
15-
rvariable := []string{geeTlsKeyCmd, genCsrCmd, genCrtCmd}
16-
for _, j := range rvariable {
17-
cmd := exec.Command("bash", "-c", j)
31+
32+
// Validate paths to prevent command injection
33+
if err := validatePath(path); err != nil {
34+
return fmt.Errorf("invalid path: %w", err)
35+
}
36+
if err := validatePath(caPath); err != nil {
37+
return fmt.Errorf("invalid caPath: %w", err)
38+
}
39+
40+
// Use safer approach with individual commands instead of shell execution
41+
commands := [][]string{
42+
{"openssl", "genpkey", "-algorithm", "RSA", "-out", filepath.Join(path, "tls.key")},
43+
{"openssl", "req", "-new", "-key", filepath.Join(path, "tls.key"), "-config", filepath.Join(path, "server.cnf"), "-out", filepath.Join(path, "tls.csr")},
44+
{"openssl", "x509", "-req", "-CA", filepath.Join(caPath, "cacert.pem"), "-CAkey", filepath.Join(caPath, "ca-private-key.pem"), "-CAcreateserial", "-CAserial", filepath.Join(path, "cacert.srl"), "-in", filepath.Join(path, "tls.csr"), "-out", filepath.Join(path, "tls.crt"), "-days", "365"},
45+
}
46+
47+
for _, cmdArgs := range commands {
48+
// #nosec G204 - Command arguments are constructed from validated paths
49+
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
1850
err = cmd.Run()
51+
if err != nil {
52+
return fmt.Errorf("failed to execute command %v: %w", cmdArgs, err)
53+
}
1954
}
2055
return err
2156
}
2257

2358
func GenerateCACertificate(caPath string) error {
2459
var err error
2560
fmt.Println("====Generating CA Certificates")
26-
genKeyCmd := strings.Replace("openssl genrsa -out caPath/ca-private-key.pem 2048", "caPath", caPath, -1)
27-
genCACertCmd := strings.Replace("openssl req -new -x509 -days 3650 -key caPath/ca-private-key.pem -out caPath/cacert.pem -subj '/CN=TlsTest/C=US/ST=California/L=RedwoodCity/O=Progress/OU=MarkLogic'", "caPath", caPath, -1)
28-
pwdCMD := "pwd"
29-
rvariable := []string{pwdCMD, genKeyCmd, genCACertCmd}
30-
for _, j := range rvariable {
31-
fmt.Println(j)
32-
cmd := exec.Command("bash", "-c", j)
61+
62+
// Validate path to prevent command injection
63+
if err := validatePath(caPath); err != nil {
64+
return fmt.Errorf("invalid caPath: %w", err)
65+
}
66+
67+
// Use safer approach with individual commands instead of shell execution
68+
commands := [][]string{
69+
{"pwd"},
70+
{"openssl", "genrsa", "-out", filepath.Join(caPath, "ca-private-key.pem"), "2048"},
71+
{"openssl", "req", "-new", "-x509", "-days", "3650", "-key", filepath.Join(caPath, "ca-private-key.pem"), "-out", filepath.Join(caPath, "cacert.pem"), "-subj", "/CN=TlsTest/C=US/ST=California/L=RedwoodCity/O=Progress/OU=MarkLogic"},
72+
}
73+
74+
for _, cmdArgs := range commands {
75+
fmt.Println(strings.Join(cmdArgs, " "))
76+
// #nosec G204 - Command arguments are constructed from validated paths
77+
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
3378
err = cmd.Run()
3479
if err != nil {
3580
fmt.Println(err)
81+
return fmt.Errorf("failed to execute command %v: %w", cmdArgs, err)
3682
}
3783
}
38-
return err
84+
return nil
3985
}

test/utils/utils.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"os/exec"
2525
"path/filepath"
26+
"regexp"
2627
"strings"
2728
"testing"
2829
"time"
@@ -48,9 +49,43 @@ func warnError(err error) {
4849
fmt.Fprintf(GinkgoWriter, "warning: %v\n", err)
4950
}
5051

52+
// validateURL performs basic validation on URLs to prevent command injection
53+
func validateURL(url string) error {
54+
if !strings.HasPrefix(url, "https://") {
55+
return fmt.Errorf("URL must use HTTPS: %s", url)
56+
}
57+
// Basic validation to ensure URL doesn't contain shell metacharacters
58+
if strings.ContainsAny(url, ";|&$`(){}[]<>") {
59+
return fmt.Errorf("URL contains invalid characters: %s", url)
60+
}
61+
return nil
62+
}
63+
64+
// validateKindClusterName validates cluster names for kind
65+
func validateKindClusterName(name string) error {
66+
// Kind cluster names should only contain alphanumeric characters and hyphens
67+
if !regexp.MustCompile(`^[a-zA-Z0-9-]+$`).MatchString(name) {
68+
return fmt.Errorf("invalid cluster name: %s", name)
69+
}
70+
return nil
71+
}
72+
73+
// validateImageName validates Docker image names
74+
func validateImageName(name string) error {
75+
// Basic validation for Docker image names
76+
if strings.ContainsAny(name, ";|&$`(){}[]<>") {
77+
return fmt.Errorf("image name contains invalid characters: %s", name)
78+
}
79+
return nil
80+
}
81+
5182
// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics.
5283
func InstallPrometheusOperator() error {
5384
url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
85+
if err := validateURL(url); err != nil {
86+
return fmt.Errorf("invalid prometheus operator URL: %w", err)
87+
}
88+
// #nosec G204 - URL is validated and constructed from trusted constants
5489
cmd := exec.Command("kubectl", "create", "-f", url)
5590
_, err := Run(cmd)
5691
return err
@@ -79,6 +114,11 @@ func Run(cmd *exec.Cmd) ([]byte, error) {
79114
// UninstallPrometheusOperator uninstalls the prometheus
80115
func UninstallPrometheusOperator() {
81116
url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion)
117+
if err := validateURL(url); err != nil {
118+
warnError(fmt.Errorf("invalid prometheus operator URL: %w", err))
119+
return
120+
}
121+
// #nosec G204 - URL is validated and constructed from trusted constants
82122
cmd := exec.Command("kubectl", "delete", "-f", url)
83123
if _, err := Run(cmd); err != nil {
84124
warnError(err)
@@ -88,6 +128,11 @@ func UninstallPrometheusOperator() {
88128
// UninstallCertManager uninstalls the cert manager
89129
func UninstallCertManager() {
90130
url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
131+
if err := validateURL(url); err != nil {
132+
warnError(fmt.Errorf("invalid cert manager URL: %w", err))
133+
return
134+
}
135+
// #nosec G204 - URL is validated and constructed from trusted constants
91136
cmd := exec.Command("kubectl", "delete", "-f", url)
92137
if _, err := Run(cmd); err != nil {
93138
warnError(err)
@@ -97,6 +142,10 @@ func UninstallCertManager() {
97142
// InstallCertManager installs the cert manager bundle.
98143
func InstallCertManager() error {
99144
url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion)
145+
if err := validateURL(url); err != nil {
146+
return fmt.Errorf("invalid cert manager URL: %w", err)
147+
}
148+
// #nosec G204 - URL is validated and constructed from trusted constants
100149
cmd := exec.Command("kubectl", "apply", "-f", url)
101150
if _, err := Run(cmd); err != nil {
102151
return err
@@ -119,6 +168,16 @@ func LoadImageToKindClusterWithName(name string) error {
119168
if v, ok := os.LookupEnv("KIND_CLUSTER"); ok {
120169
cluster = v
121170
}
171+
172+
// Validate inputs to prevent command injection
173+
if err := validateImageName(name); err != nil {
174+
return fmt.Errorf("invalid image name: %w", err)
175+
}
176+
if err := validateKindClusterName(cluster); err != nil {
177+
return fmt.Errorf("invalid cluster name: %w", err)
178+
}
179+
180+
// #nosec G204 - Inputs are validated for safety
122181
kindOptions := []string{"load", "docker-image", name, "--name", cluster}
123182
cmd := exec.Command("kind", kindOptions...)
124183
_, err := Run(cmd)

0 commit comments

Comments
 (0)