Skip to content

Commit 94fc389

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#29837 from thockin/validate-dotdot-in-projections
Automatic merge from submit-queue Validate .. in projections, fix tests I noticed this when reviewing another PR. I fixed it but the test was beyond comprehension, so I fixed that too. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.kubernetes.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.kubernetes.io/reviews/kubernetes/kubernetes/29837) <!-- Reviewable:end -->
2 parents 70d6fe6 + ef4bccf commit 94fc389

File tree

2 files changed

+1058
-277
lines changed

2 files changed

+1058
-277
lines changed

pkg/api/validation/validation.go

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ func validateGitRepoVolumeSource(gitRepo *api.GitRepoVolumeSource, fldPath *fiel
598598
allErrs = append(allErrs, field.Required(fldPath.Child("repository"), ""))
599599
}
600600

601-
pathErrs := validateVolumeSourcePath(gitRepo.Directory, fldPath.Child("directory"))
601+
pathErrs := validateLocalDescendingPath(gitRepo.Directory, fldPath.Child("directory"))
602602
allErrs = append(allErrs, pathErrs...)
603603
return allErrs
604604
}
@@ -660,6 +660,11 @@ func validateSecretVolumeSource(secretSource *api.SecretVolumeSource, fldPath *f
660660
if len(secretSource.SecretName) == 0 {
661661
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
662662
}
663+
itemsPath := fldPath.Child("items")
664+
for i, kp := range secretSource.Items {
665+
itemPath := itemsPath.Index(i)
666+
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
667+
}
663668
return allErrs
664669
}
665670

@@ -668,6 +673,23 @@ func validateConfigMapVolumeSource(configMapSource *api.ConfigMapVolumeSource, f
668673
if len(configMapSource.Name) == 0 {
669674
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
670675
}
676+
itemsPath := fldPath.Child("items")
677+
for i, kp := range configMapSource.Items {
678+
itemPath := itemsPath.Index(i)
679+
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
680+
}
681+
return allErrs
682+
}
683+
684+
func validateKeyToPath(kp *api.KeyToPath, fldPath *field.Path) field.ErrorList {
685+
allErrs := field.ErrorList{}
686+
if len(kp.Key) == 0 {
687+
allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
688+
}
689+
if len(kp.Path) == 0 {
690+
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
691+
}
692+
allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
671693
return allErrs
672694
}
673695

@@ -715,22 +737,26 @@ func validateFlockerVolumeSource(flocker *api.FlockerVolumeSource, fldPath *fiel
715737
return allErrs
716738
}
717739

718-
var validDownwardAPIFieldPathExpressions = sets.NewString("metadata.name", "metadata.namespace", "metadata.labels", "metadata.annotations")
740+
var validDownwardAPIFieldPathExpressions = sets.NewString(
741+
"metadata.name",
742+
"metadata.namespace",
743+
"metadata.labels",
744+
"metadata.annotations")
719745

720746
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
721747
allErrs := field.ErrorList{}
722-
for _, downwardAPIVolumeFile := range downwardAPIVolume.Items {
723-
if len(downwardAPIVolumeFile.Path) == 0 {
748+
for _, file := range downwardAPIVolume.Items {
749+
if len(file.Path) == 0 {
724750
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
725751
}
726-
allErrs = append(allErrs, validateVolumeSourcePath(downwardAPIVolumeFile.Path, fldPath.Child("path"))...)
727-
if downwardAPIVolumeFile.FieldRef != nil {
728-
allErrs = append(allErrs, validateObjectFieldSelector(downwardAPIVolumeFile.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
729-
if downwardAPIVolumeFile.ResourceFieldRef != nil {
752+
allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
753+
if file.FieldRef != nil {
754+
allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
755+
if file.ResourceFieldRef != nil {
730756
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
731757
}
732-
} else if downwardAPIVolumeFile.ResourceFieldRef != nil {
733-
allErrs = append(allErrs, validateContainerResourceFieldSelector(downwardAPIVolumeFile.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
758+
} else if file.ResourceFieldRef != nil {
759+
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
734760
} else {
735761
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
736762
}
@@ -740,44 +766,33 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
740766

741767
// This validate will make sure targetPath:
742768
// 1. is not abs path
743-
// 2. does not start with '../'
744-
// 3. does not contain '/../'
745-
// 4. does not end with '/..'
746-
func validateSubPath(targetPath string, fldPath *field.Path) field.ErrorList {
769+
// 2. does not have any element which is ".."
770+
func validateLocalDescendingPath(targetPath string, fldPath *field.Path) field.ErrorList {
747771
allErrs := field.ErrorList{}
748772
if path.IsAbs(targetPath) {
749773
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
750774
}
751-
if strings.HasPrefix(targetPath, "../") {
752-
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '../'"))
753-
}
754-
if strings.Contains(targetPath, "/../") {
755-
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '/../'"))
756-
}
757-
if strings.HasSuffix(targetPath, "/..") {
758-
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not end with '/..'"))
775+
776+
// TODO: this assumes the OS of apiserver & nodes are the same
777+
parts := strings.Split(targetPath, string(os.PathSeparator))
778+
for _, item := range parts {
779+
if item == ".." {
780+
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
781+
break // even for `../../..`, one error is sufficient to make the point
782+
}
759783
}
760784
return allErrs
761785
}
762786

763787
// This validate will make sure targetPath:
764788
// 1. is not abs path
765-
// 2. does not contain '..'
789+
// 2. does not contain any '..' elements
766790
// 3. does not start with '..'
767-
func validateVolumeSourcePath(targetPath string, fldPath *field.Path) field.ErrorList {
791+
func validateLocalNonReservedPath(targetPath string, fldPath *field.Path) field.ErrorList {
768792
allErrs := field.ErrorList{}
769-
if path.IsAbs(targetPath) {
770-
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must be a relative path"))
771-
}
772-
// TODO assume OS of api server & nodes are the same for now
773-
items := strings.Split(targetPath, string(os.PathSeparator))
774-
775-
for _, item := range items {
776-
if item == ".." {
777-
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not contain '..'"))
778-
}
779-
}
780-
if strings.HasPrefix(items[0], "..") && len(items[0]) > 2 {
793+
allErrs = append(allErrs, validateLocalDescendingPath(targetPath, fldPath)...)
794+
// Don't report this error if the check for .. elements already caught it.
795+
if strings.HasPrefix(targetPath, "..") && !strings.HasPrefix(targetPath, "../") {
781796
allErrs = append(allErrs, field.Invalid(fldPath, targetPath, "must not start with '..'"))
782797
}
783798
return allErrs
@@ -1262,7 +1277,7 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath
12621277
}
12631278
mountpoints.Insert(mnt.MountPath)
12641279
if len(mnt.SubPath) > 0 {
1265-
allErrs = append(allErrs, validateSubPath(mnt.SubPath, fldPath.Child("subPath"))...)
1280+
allErrs = append(allErrs, validateLocalDescendingPath(mnt.SubPath, fldPath.Child("subPath"))...)
12661281
}
12671282
}
12681283
return allErrs
@@ -1918,7 +1933,7 @@ func validateSeccompProfile(p string, fldPath *field.Path) field.ErrorList {
19181933
return nil
19191934
}
19201935
if strings.HasPrefix(p, "localhost/") {
1921-
return validateSubPath(strings.TrimPrefix(p, "localhost/"), fldPath)
1936+
return validateLocalDescendingPath(strings.TrimPrefix(p, "localhost/"), fldPath)
19221937
}
19231938
return field.ErrorList{field.Invalid(fldPath, p, "must be a valid seccomp profile")}
19241939
}

0 commit comments

Comments
 (0)