text:
package sccmatching

import (
	"fmt"
	"reflect"
	"strings"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/require"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/diff"
	"k8s.io/apimachinery/pkg/util/validation/field"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/utils/pointer"

	securityv1 "github.com/openshift/api/security/v1"
	sccutil "github.com/openshift/apiserver-library-go/pkg/securitycontextconstraints/util"
)

func TestCreatePodSecurityContextNonmutating(t *testing.T) {
	// Create a pod with a security context that needs filling in
	createPod := func() *api.Pod {
		return &api.Pod{
			Spec: api.PodSpec{
				SecurityContext: &api.PodSecurityContext{},
			},
		}
	}

	// Create an SCC with strategies that will populate a blank psc
	createSCC := func() *securityv1.SecurityContextConstraints {
		return &securityv1.SecurityContextConstraints{
			ObjectMeta: metav1.ObjectMeta{
				Name: "scc-sa",
			},
			SeccompProfiles: []string{"foo"},
			RunAsUser: securityv1.RunAsUserStrategyOptions{
				Type: securityv1.RunAsUserStrategyRunAsAny,
			},
			SELinuxContext: securityv1.SELinuxContextStrategyOptions{
				Type: securityv1.SELinuxStrategyRunAsAny,
			},
			FSGroup: securityv1.FSGroupStrategyOptions{
				Type: securityv1.FSGroupStrategyRunAsAny,
			},
			SupplementalGroups: securityv1.SupplementalGroupsStrategyOptions{
				Type: securityv1.SupplementalGroupsStrategyRunAsAny,
			},
		}
	}

	pod := createPod()
	scc := createSCC()

	provider, err := NewSimpleProvider(scc)
	if err != nil {
		t.Fatalf("unable to create provider %v", err)
	}
	_, _, err = provider.CreatePodSecurityContext(pod)
	if err != nil {
		t.Fatalf("unable to create psc %v", err)
	}

	// Creating the provider or the security context should not have mutated the scc or pod
	// since all the strategies were permissive
	if !reflect.DeepEqual(createPod(), pod) {
		diff := diff.ObjectDiff(createPod(), pod)
		t.Errorf("pod was mutated by CreatePodSecurityContext. diff:\n%s", diff)
	}
	if !reflect.DeepEqual(createSCC(), scc) {
		t.Error("scc was mutated by CreatePodSecurityContext")
	}
}

func TestCreateContainerSecurityContextNonmutating(t *testing.T) {
	// Create a pod with a security context that needs filling in
	createPod := func() *api.Pod {
		return &api.Pod{
			Spec: api.PodSpec{
				Containers: []api.Container{{
					SecurityContext: &api.SecurityContext{},
				}},
			},
		}
	}

	// Create an SCC with strategies that will populate a blank security context
	createSCC := func() *securityv1.SecurityContextConstraints {
		return &securityv1.SecurityContextConstraints{
			ObjectMeta: metav1.ObjectMeta{
				Name: "scc-sa",
			},
			RunAsUser: securityv1.RunAsUserStrategyOptions{
				Type: securityv1.RunAsUserStrategyRunAsAny,
			},
			SELinuxContext: securityv1.SELinuxContextStrategyOptions{
				Type: securityv1.SELinuxStrategyRunAsAny,
			},
			FSGroup: securityv1.FSGroupStrategyOptions{
				Type: securityv1.FSGroupStrategyRunAsAny,
			},
			SupplementalGroups: securityv1.SupplementalGroupsStrategyOptions{
				Type: securityv1.SupplementalGroupsStrategyRunAsAny,
			},
		}
	}

	pod := createPod()
	scc := createSCC()

	provider, err := NewSimpleProvider(scc)
	if err != nil {
		t.Fatalf("unable to create provider %v", err)
	}
	_, err = provider.CreateContainerSecurityContext(pod, &pod.Spec.Containers[0])
	if err != nil {
		t.Fatalf("unable to create container security context %v", err)
	}

	// Creating the provider or the security context should not have mutated the scc or pod
	// since all the strategies were permissive
	if !reflect.DeepEqual(createPod(), pod) {
		diff := diff.ObjectDiff(createPod(), pod)
		t.Errorf("pod was mutated by CreateContainerSecurityContext. diff:\n%s", diff)
	}
	if !reflect.DeepEqual(createSCC(), scc) {
		t.Error("scc was mutated by CreateContainerSecurityContext")
	}
}

func TestValidatePodSecurityContextFailures(t *testing.T) {
	failHostNetworkPod := defaultPod()
	failHostNetworkPod.Spec.SecurityContext.HostNetwork = true

	failHostPIDPod := defaultPod()
	failHostPIDPod.Spec.SecurityContext.HostPID = true

	failHostIPCPod := defaultPod()
	failHostIPCPod.Spec.SecurityContext.HostIPC = true

	failSupplementalGroupPod := defaultPod()
	failSupplementalGroupPod.Spec.SecurityContext.SupplementalGroups = []int64{999}
	failSupplementalGroupSCC := defaultSCC()
	failSupplementalGroupSCC.SupplementalGroups = securityv1.SupplementalGroupsStrategyOptions{
		Type: securityv1.SupplementalGroupsStrategyMustRunAs,
		Ranges: []securityv1.IDRange{
			{Min: 1, Max: 1},
		},
	}

	failFSGroupPod := defaultPod()
	fsGroup := int64(999)
	failFSGroupPod.Spec.SecurityContext.FSGroup = &fsGroup
	failFSGroupSCC := defaultSCC()
	failFSGroupSCC.FSGroup = securityv1.FSGroupStrategyOptions{
		Type: securityv1.FSGroupStrategyMustRunAs,
		Ranges: []securityv1.IDRange{
			{Min: 1, Max: 1},
		},
	}

	failNilSELinuxPod := defaultPod()
	failSELinuxSCC := defaultSCC()
	failSELinuxSCC.SELinuxContext.Type = securityv1.SELinuxStrategyMustRunAs
	failSELinuxSCC.SELinuxContext.SELinuxOptions = &corev1.SELinuxOptions{
		Level: "foo",
	}

	failInvalidSELinuxPod := defaultPod()
	failInvalidSELinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		Level: "bar",
	}

	failNoSeccompAllowed := defaultPod()
	failNoSeccompAllowed.Annotations[api.SeccompPodAnnotationKey] = "bar"

	failInvalidSeccompProfile := defaultPod()
	failInvalidSeccompProfile.Annotations[api.SeccompPodAnnotationKey] = "bar"

	failInvalidSeccompProfileSCC := defaultSCC()
	failInvalidSeccompProfileSCC.SeccompProfiles = []string{"foo"}

	failHostDirPod := defaultPod()
	failHostDirPod.Spec.Volumes = []api.Volume{
		{
			Name: "bad volume",
			VolumeSource: api.VolumeSource{
				HostPath: &api.HostPathVolumeSource{},
			},
		},
	}

	podWithInvalidFlexVolumeDriver := defaultPod()
	podWithInvalidFlexVolumeDriver.Spec.Volumes = []api.Volume{
		{
			Name: "flex-volume",
			VolumeSource: api.VolumeSource{
				FlexVolume: &api.FlexVolumeSource{
					Driver: "example/unknown",
				},
			},
		},
	}

	failSysctlDisallowedSCC := defaultSCC()
	failSysctlDisallowedSCC.ForbiddenSysctls = []string{"kernel.shm_rmid_forced"}

	failNoSafeSysctlAllowedSCC := defaultSCC()
	failNoSafeSysctlAllowedSCC.ForbiddenSysctls = []string{"*"}

	failAllUnsafeSysctlsSCC := defaultSCC()
	failAllUnsafeSysctlsSCC.AllowedUnsafeSysctls = []string{}

	failSafeSysctlKernelPod := defaultPod()
	failSafeSysctlKernelPod.Spec.SecurityContext.Sysctls = []api.Sysctl{
		{
			Name:  "kernel.shm_rmid_forced",
			Value: "1",
		},
	}

	failUnsafeSysctlPod := defaultPod()
	failUnsafeSysctlPod.Spec.SecurityContext.Sysctls = []api.Sysctl{
		{
			Name:  "kernel.sem",
			Value: "32000",
		},
	}

	failSeccompProfilePod := defaultPod()
	failSeccompProfilePod.Annotations = map[string]string{api.SeccompPodAnnotationKey: "foo"}

	errorCases := map[string]struct {
		pod           *api.Pod
		scc           *securityv1.SecurityContextConstraints
		expectedError string
	}{
		"failHostNetworkSCC": {
			pod:           failHostNetworkPod,
			scc:           defaultSCC(),
			expectedError: "Host network is not allowed to be used",
		},
		"failHostPIDSCC": {
			pod:           failHostPIDPod,
			scc:           defaultSCC(),
			expectedError: "Host PID is not allowed to be used",
		},
		"failHostIPCSCC": {
			pod:           failHostIPCPod,
			scc:           defaultSCC(),
			expectedError: "Host IPC is not allowed to be used",
		},
		"failSupplementalGroupOutOfRange": {
			pod:           failSupplementalGroupPod,
			scc:           failSupplementalGroupSCC,
			expectedError: "999 is not an allowed group",
		},
		"failSupplementalGroupEmpty": {
			pod:           defaultPod(),
			scc:           failSupplementalGroupSCC,
			expectedError: "unable to validate empty groups against required ranges",
		},
		"failFSGroupOutOfRange": {
			pod:           failFSGroupPod,
			scc:           failFSGroupSCC,
			expectedError: "999 is not an allowed group",
		},
		"failFSGroupEmpty": {
			pod:           defaultPod(),
			scc:           failFSGroupSCC,
			expectedError: "unable to validate empty groups against required ranges",
		},
		"failNilSELinux": {
			pod:           failNilSELinuxPod,
			scc:           failSELinuxSCC,
			expectedError: "seLinuxOptions: Required",
		},
		"failInvalidSELinux": {
			pod:           failInvalidSELinuxPod,
			scc:           failSELinuxSCC,
			expectedError: "seLinuxOptions.level: Invalid value",
		},
		"failNoSeccomp": {
			pod:           failNoSeccompAllowed,
			scc:           defaultSCC(),
			expectedError: "seccomp may not be set",
		},
		"failInvalidSeccompPod": {
			pod:           failInvalidSeccompProfile,
			scc:           failInvalidSeccompProfileSCC,
			expectedError: "Forbidden: bar is not an allowed seccomp profile. Valid values are [foo]",
		},
		"failHostDirSCC": {
			pod:           failHostDirPod,
			scc:           defaultSCC(),
			expectedError: "hostPath volumes are not allowed to be used",
		},
		"fail pod with disallowed flexVolume when flex volumes are allowed": {
			pod:           podWithInvalidFlexVolumeDriver,
			scc:           allowFlexVolumesSCC(false, false),
			expectedError: "Flexvolume driver is not allowed to be used",
		},
		"fail pod with disallowed flexVolume when all volumes are allowed": {
			pod:           podWithInvalidFlexVolumeDriver,
			scc:           allowFlexVolumesSCC(false, true),
			expectedError: "Flexvolume driver is not allowed to be used",
		},
		"failSafeSysctlKernelPod with failNoSafeSysctlAllowedSCC": {
			pod:           failSafeSysctlKernelPod,
			scc:           failNoSafeSysctlAllowedSCC,
			expectedError: "sysctl \"kernel.shm_rmid_forced\" is not allowed",
		},
		"failSafeSysctlKernelPod with failSysctlDisallowedSCC": {
			pod:           failSafeSysctlKernelPod,
			scc:           failSysctlDisallowedSCC,
			expectedError: "sysctl \"kernel.shm_rmid_forced\" is not allowed",
		},
		"failUnsafeSysctlPod with failAllUnsafeSysctlsSCC": {
			pod:           failUnsafeSysctlPod,
			scc:           failAllUnsafeSysctlsSCC,
			expectedError: "unsafe sysctl \"kernel.sem\" is not allowed",
		},
		"failInvalidSeccomp": {
			pod:           failSeccompProfilePod,
			scc:           defaultSCC(),
			expectedError: "Forbidden: seccomp may not be set",
		},
	}
	for k, v := range errorCases {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidatePodSecurityContext(v.pod, field.NewPath("spec"))
		if len(errs) == 0 {
			t.Errorf("%s expected validation failure but did not receive errors", k)
			continue
		}
		if !strings.Contains(errs[0].Error(), v.expectedError) {
			t.Errorf("%s received unexpected error %v", k, errs)
		}
	}
}

func TestValidateContainerSecurityContextFailures(t *testing.T) {
	// fail user strat
	failUserSCC := defaultSCC()
	var uid int64 = 999
	var badUID int64 = 1
	failUserSCC.RunAsUser = securityv1.RunAsUserStrategyOptions{
		Type: securityv1.RunAsUserStrategyMustRunAs,
		UID:  &uid,
	}
	failUserPod := defaultPod()
	failUserPod.Spec.Containers[0].SecurityContext.RunAsUser = &badUID

	// fail selinux strat
	failSELinuxSCC := defaultSCC()
	failSELinuxSCC.SELinuxContext = securityv1.SELinuxContextStrategyOptions{
		Type: securityv1.SELinuxStrategyMustRunAs,
		SELinuxOptions: &corev1.SELinuxOptions{
			Level: "foo",
		},
	}
	failSELinuxPod := defaultPod()
	failSELinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		Level: "bar",
	}

	failPrivPod := defaultPod()
	var priv bool = true
	failPrivPod.Spec.Containers[0].SecurityContext.Privileged = &priv

	failCapsPod := defaultPod()
	failCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	failHostPortPod := defaultPod()
	failHostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}

	readOnlyRootFSSCC := defaultSCC()
	readOnlyRootFSSCC.ReadOnlyRootFilesystem = true

	readOnlyRootFSPodFalse := defaultPod()
	readOnlyRootFS := false
	readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFS

	failNoSeccompAllowed := defaultPod()
	failNoSeccompAllowed.Annotations[api.SeccompContainerAnnotationKeyPrefix+failNoSeccompAllowed.Spec.Containers[0].Name] = "bar"
	failNoSeccompAllowedSCC := defaultSCC()
	failNoSeccompAllowedSCC.SeccompProfiles = nil

	failInvalidSeccompProfile := defaultPod()
	failInvalidSeccompProfile.Annotations[api.SeccompContainerAnnotationKeyPrefix+failNoSeccompAllowed.Spec.Containers[0].Name] = "bar"
	failInvalidSeccompProfileSCC := defaultSCC()
	failInvalidSeccompProfileSCC.SeccompProfiles = []string{"foo"}

	failSeccompPod := defaultPod()
	failSeccompPod.Annotations = map[string]string{
		api.SeccompContainerAnnotationKeyPrefix + failSeccompPod.Spec.Containers[0].Name: "foo",
	}

	failSeccompPodInheritPodAnnotation := defaultPod()
	failSeccompPodInheritPodAnnotation.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
	}

	errorCases := map[string]struct {
		pod           *api.Pod
		scc           *securityv1.SecurityContextConstraints
		expectedError string
	}{
		"failUserSCC": {
			pod:           failUserPod,
			scc:           failUserSCC,
			expectedError: "runAsUser: Invalid value",
		},
		"failSELinuxSCC": {
			pod:           failSELinuxPod,
			scc:           failSELinuxSCC,
			expectedError: "seLinuxOptions.level: Invalid value",
		},
		"failPrivSCC": {
			pod:           failPrivPod,
			scc:           defaultSCC(),
			expectedError: "Privileged containers are not allowed",
		},
		"failCapsSCC": {
			pod:           failCapsPod,
			scc:           defaultSCC(),
			expectedError: "capability may not be added",
		},
		"failHostPortSCC": {
			pod:           failHostPortPod,
			scc:           defaultSCC(),
			expectedError: "Host ports are not allowed to be used",
		},
		"failReadOnlyRootFS - nil": {
			pod:           defaultPod(),
			scc:           readOnlyRootFSSCC,
			expectedError: "ReadOnlyRootFilesystem may not be nil and must be set to true",
		},
		"failReadOnlyRootFS - false": {
			pod:           readOnlyRootFSPodFalse,
			scc:           readOnlyRootFSSCC,
			expectedError: "ReadOnlyRootFilesystem must be set to true",
		},
		"failNoSeccompAllowed": {
			pod:           failNoSeccompAllowed,
			scc:           failNoSeccompAllowedSCC,
			expectedError: "seccomp may not be set",
		},
		"failInvalidSeccompPod": {
			pod:           failInvalidSeccompProfile,
			scc:           failInvalidSeccompProfileSCC,
			expectedError: "Forbidden: bar is not an allowed seccomp profile. Valid values are [foo]",
		},
		"failSeccompContainerAnnotation": {
			pod:           failSeccompPod,
			scc:           defaultSCC(),
			expectedError: "Forbidden: seccomp may not be set",
		},
		"failSeccompContainerPodAnnotation": {
			pod:           failSeccompPodInheritPodAnnotation,
			scc:           defaultSCC(),
			expectedError: "Forbidden: seccomp may not be set",
		},
	}

	for k, v := range errorCases {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
		if len(errs) == 0 {
			t.Errorf("%s expected validation failure but did not receive errors", k)
			continue
		}
		if !strings.Contains(errs[0].Error(), v.expectedError) {
			t.Errorf("%s received unexpected error %v", k, errs)
		}
	}
}

func TestValidatePodSecurityContextSuccess(t *testing.T) {
	hostNetworkSCC := defaultSCC()
	hostNetworkSCC.AllowHostNetwork = true
	hostNetworkPod := defaultPod()
	hostNetworkPod.Spec.SecurityContext.HostNetwork = true

	hostPIDSCC := defaultSCC()
	hostPIDSCC.AllowHostPID = true
	hostPIDPod := defaultPod()
	hostPIDPod.Spec.SecurityContext.HostPID = true

	hostIPCSCC := defaultSCC()
	hostIPCSCC.AllowHostIPC = true
	hostIPCPod := defaultPod()
	hostIPCPod.Spec.SecurityContext.HostIPC = true

	supGroupSCC := defaultSCC()
	supGroupSCC.SupplementalGroups = securityv1.SupplementalGroupsStrategyOptions{
		Type: securityv1.SupplementalGroupsStrategyMustRunAs,
		Ranges: []securityv1.IDRange{
			{Min: 1, Max: 5},
		},
	}
	supGroupPod := defaultPod()
	supGroupPod.Spec.SecurityContext.SupplementalGroups = []int64{3}

	fsGroupSCC := defaultSCC()
	fsGroupSCC.FSGroup = securityv1.FSGroupStrategyOptions{
		Type: securityv1.FSGroupStrategyMustRunAs,
		Ranges: []securityv1.IDRange{
			{Min: 1, Max: 5},
		},
	}
	fsGroupPod := defaultPod()
	fsGroup := int64(3)
	fsGroupPod.Spec.SecurityContext.FSGroup = &fsGroup

	seLinuxPod := defaultPod()
	seLinuxPod.Spec.SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		User:  "user",
		Role:  "role",
		Type:  "type",
		Level: "level",
	}
	seLinuxSCC := defaultSCC()
	seLinuxSCC.SELinuxContext.Type = securityv1.SELinuxStrategyMustRunAs
	seLinuxSCC.SELinuxContext.SELinuxOptions = &corev1.SELinuxOptions{
		User:  "user",
		Role:  "role",
		Type:  "type",
		Level: "level",
	}

	seccompNilWithNoProfiles := defaultPod()
	seccompNilWithNoProfilesSCC := defaultSCC()
	seccompNilWithNoProfilesSCC.SeccompProfiles = nil

	seccompEmpty := defaultPod()
	seccompEmpty.Annotations[api.SeccompPodAnnotationKey] = ""

	seccompAllowAnySCC := defaultSCC()
	seccompAllowAnySCC.SeccompProfiles = []string{"*"}

	seccompAllowFooSCC := defaultSCC()
	seccompAllowFooSCC.SeccompProfiles = []string{"foo"}

	seccompFooPod := defaultPod()
	seccompFooPod.Annotations[api.SeccompPodAnnotationKey] = "foo"

	flexVolumePod := defaultPod()
	flexVolumePod.Spec.Volumes = []api.Volume{
		{
			Name: "flex-volume",
			VolumeSource: api.VolumeSource{
				FlexVolume: &api.FlexVolumeSource{
					Driver: "example/bar",
				},
			},
		},
	}

	sysctlAllowAllSCC := defaultSCC()
	sysctlAllowAllSCC.ForbiddenSysctls = []string{}
	sysctlAllowAllSCC.AllowedUnsafeSysctls = []string{"*"}

	safeSysctlKernelPod := defaultPod()
	safeSysctlKernelPod.Spec.SecurityContext.Sysctls = []api.Sysctl{
		{
			Name:  "kernel.shm_rmid_forced",
			Value: "1",
		},
	}

	unsafeSysctlKernelPod := defaultPod()
	unsafeSysctlKernelPod.Spec.SecurityContext.Sysctls = []api.Sysctl{
		{
			Name:  "kernel.sem",
			Value: "32000",
		},
	}

	seccompSCC := defaultSCC()
	seccompSCC.SeccompProfiles = []string{"foo"}

	seccompPod := defaultPod()
	seccompPod.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
	}

	successCases := map[string]struct {
		pod *api.Pod
		scc *securityv1.SecurityContextConstraints
	}{
		"pass hostNetwork validating SCC": {
			pod: hostNetworkPod,
			scc: hostNetworkSCC,
		},
		"pass hostPID validating SCC": {
			pod: hostPIDPod,
			scc: hostPIDSCC,
		},
		"pass hostIPC validating SCC": {
			pod: hostIPCPod,
			scc: hostIPCSCC,
		},
		"pass supplemental group validating SCC": {
			pod: supGroupPod,
			scc: supGroupSCC,
		},
		"pass fs group validating SCC": {
			pod: fsGroupPod,
			scc: fsGroupSCC,
		},
		"pass selinux validating SCC": {
			pod: seLinuxPod,
			scc: seLinuxSCC,
		},
		"pass seccomp nil with no profiles": {
			pod: seccompNilWithNoProfiles,
			scc: seccompNilWithNoProfilesSCC,
		},
		"pass seccomp empty with no profiles": {
			pod: seccompEmpty,
			scc: seccompNilWithNoProfilesSCC,
		},
		"pass seccomp wild card": {
			pod: seccompFooPod,
			scc: seccompAllowAnySCC,
		},
		"pass seccomp specific profile": {
			pod: seccompFooPod,
			scc: seccompAllowFooSCC,
		},
		"flex volume driver in a whitelist (all volumes are allowed)": {
			pod: flexVolumePod,
			scc: allowFlexVolumesSCC(false, true),
		},
		"flex volume driver with empty whitelist (all volumes are allowed)": {
			pod: flexVolumePod,
			scc: allowFlexVolumesSCC(true, true),
		},
		"flex volume driver in a whitelist (only flex volumes are allowed)": {
			pod: flexVolumePod,
			scc: allowFlexVolumesSCC(false, false),
		},
		"flex volume driver with empty whitelist (only flex volumes volumes are allowed)": {
			pod: flexVolumePod,
			scc: allowFlexVolumesSCC(true, false),
		},
		"pass sysctl specific profile with safe kernel sysctl": {
			pod: safeSysctlKernelPod,
			scc: sysctlAllowAllSCC,
		},
		"pass sysctl specific profile with unsafe kernel sysctl": {
			pod: unsafeSysctlKernelPod,
			scc: sysctlAllowAllSCC,
		},
		"pass seccomp validating SCC": {
			pod: seccompPod,
			scc: seccompSCC,
		},
	}

	for k, v := range successCases {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidatePodSecurityContext(v.pod, field.NewPath(""))
		if len(errs) != 0 {
			t.Errorf("%s expected validation pass but received errors %v", k, errs)
			continue
		}
	}
}

func TestValidateContainerSecurityContextSuccess(t *testing.T) {
	// fail user strat
	userSCC := defaultSCC()
	var uid int64 = 999
	userSCC.RunAsUser = securityv1.RunAsUserStrategyOptions{
		Type: securityv1.RunAsUserStrategyMustRunAs,
		UID:  &uid,
	}
	userPod := defaultPod()
	userPod.Spec.Containers[0].SecurityContext.RunAsUser = &uid

	// fail selinux strat
	seLinuxSCC := defaultSCC()
	seLinuxSCC.SELinuxContext = securityv1.SELinuxContextStrategyOptions{
		Type: securityv1.SELinuxStrategyMustRunAs,
		SELinuxOptions: &corev1.SELinuxOptions{
			Level: "foo",
		},
	}
	seLinuxPod := defaultPod()
	seLinuxPod.Spec.Containers[0].SecurityContext.SELinuxOptions = &api.SELinuxOptions{
		Level: "foo",
	}

	privSCC := defaultSCC()
	privSCC.AllowPrivilegedContainer = true
	privPod := defaultPod()
	var priv bool = true
	privPod.Spec.Containers[0].SecurityContext.Privileged = &priv

	capsSCC := defaultSCC()
	capsSCC.AllowedCapabilities = []corev1.Capability{"foo"}
	capsPod := defaultPod()
	capsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	// pod should be able to request caps that are in the required set even if not specified in the allowed set
	requiredCapsSCC := defaultSCC()
	requiredCapsSCC.DefaultAddCapabilities = []corev1.Capability{"foo"}
	requiredCapsPod := defaultPod()
	requiredCapsPod.Spec.Containers[0].SecurityContext.Capabilities = &api.Capabilities{
		Add: []api.Capability{"foo"},
	}

	hostDirSCC := defaultSCC()
	hostDirSCC.Volumes = []securityv1.FSType{securityv1.FSTypeHostPath}
	hostDirPod := defaultPod()
	hostDirPod.Spec.Volumes = []api.Volume{
		{
			Name: "bad volume",
			VolumeSource: api.VolumeSource{
				HostPath: &api.HostPathVolumeSource{},
			},
		},
	}

	hostPortSCC := defaultSCC()
	hostPortSCC.AllowHostPorts = true
	hostPortPod := defaultPod()
	hostPortPod.Spec.Containers[0].Ports = []api.ContainerPort{{HostPort: 1}}

	readOnlyRootFSPodFalse := defaultPod()
	readOnlyRootFSFalse := false
	readOnlyRootFSPodFalse.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSFalse

	readOnlyRootFSPodTrue := defaultPod()
	readOnlyRootFSTrue := true
	readOnlyRootFSPodTrue.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = &readOnlyRootFSTrue

	seccompNilWithNoProfiles := defaultPod()
	seccompNilWithNoProfilesSCC := defaultSCC()
	seccompNilWithNoProfilesSCC.SeccompProfiles = nil

	seccompEmptyWithNoProfiles := defaultPod()
	seccompEmptyWithNoProfiles.Annotations[api.SeccompContainerAnnotationKeyPrefix+seccompEmptyWithNoProfiles.Spec.Containers[0].Name] = ""

	seccompAllowAnySCC := defaultSCC()
	seccompAllowAnySCC.SeccompProfiles = []string{"*"}

	seccompAllowFooSCC := defaultSCC()
	seccompAllowFooSCC.SeccompProfiles = []string{"foo"}

	seccompFooPod := defaultPod()
	seccompFooPod.Annotations[api.SeccompContainerAnnotationKeyPrefix+seccompFooPod.Spec.Containers[0].Name] = "foo"

	seccompPod := defaultPod()
	seccompPod.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
		api.SeccompContainerAnnotationKeyPrefix + seccompPod.Spec.Containers[0].Name: "foo",
	}

	seccompPodInherit := defaultPod()
	seccompPodInherit.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: "foo",
	}

	successCases := map[string]struct {
		pod *api.Pod
		scc *securityv1.SecurityContextConstraints
	}{
		"pass user must run as SCC": {
			pod: userPod,
			scc: userSCC,
		},
		"pass seLinux must run as SCC": {
			pod: seLinuxPod,
			scc: seLinuxSCC,
		},
		"pass priv validating SCC": {
			pod: privPod,
			scc: privSCC,
		},
		"pass allowed caps validating SCC": {
			pod: capsPod,
			scc: capsSCC,
		},
		"pass required caps validating SCC": {
			pod: requiredCapsPod,
			scc: requiredCapsSCC,
		},
		"pass hostDir validating SCC": {
			pod: hostDirPod,
			scc: hostDirSCC,
		},
		"pass hostPort validating SCC": {
			pod: hostPortPod,
			scc: hostPortSCC,
		},
		"pass read only root fs - nil": {
			pod: defaultPod(),
			scc: defaultSCC(),
		},
		"pass read only root fs - false": {
			pod: readOnlyRootFSPodFalse,
			scc: defaultSCC(),
		},
		"pass read only root fs - true": {
			pod: readOnlyRootFSPodTrue,
			scc: defaultSCC(),
		},
		"pass seccomp nil with no profiles": {
			pod: seccompNilWithNoProfiles,
			scc: seccompNilWithNoProfilesSCC,
		},
		"pass seccomp empty with no profiles": {
			pod: seccompEmptyWithNoProfiles,
			scc: seccompNilWithNoProfilesSCC,
		},
		"pass seccomp wild card": {
			pod: seccompFooPod,
			scc: seccompAllowAnySCC,
		},
		"pass seccomp specific profile": {
			pod: seccompFooPod,
			scc: seccompAllowFooSCC,
		},
	}

	for k, v := range successCases {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Fatalf("unable to create provider %v", err)
		}
		errs := provider.ValidateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0], field.NewPath(""))
		if len(errs) != 0 {
			t.Errorf("%s expected validation pass but received errors %v", k, errs)
			continue
		}
	}
}

func TestGenerateContainerSecurityContextReadOnlyRootFS(t *testing.T) {
	trueSCC := defaultSCC()
	trueSCC.ReadOnlyRootFilesystem = true

	trueVal := true
	expectTrue := &trueVal
	falseVal := false
	expectFalse := &falseVal

	falsePod := defaultPod()
	falsePod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = expectFalse

	truePod := defaultPod()
	truePod.Spec.Containers[0].SecurityContext.ReadOnlyRootFilesystem = expectTrue

	tests := map[string]struct {
		pod      *api.Pod
		scc      *securityv1.SecurityContextConstraints
		expected *bool
	}{
		"false scc, nil sc": {
			scc:      defaultSCC(),
			pod:      defaultPod(),
			expected: nil,
		},
		"false scc, false sc": {
			scc:      defaultSCC(),
			pod:      falsePod,
			expected: expectFalse,
		},
		"false scc, true sc": {
			scc:      defaultSCC(),
			pod:      truePod,
			expected: expectTrue,
		},
		"true scc, nil sc": {
			scc:      trueSCC,
			pod:      defaultPod(),
			expected: expectTrue,
		},
		"true scc, false sc": {
			scc: trueSCC,
			pod: falsePod,
			// expect false even though it defaults to true to ensure it doesn't change set values
			// validation catches the mismatch, not generation
			expected: expectFalse,
		},
		"true scc, true sc": {
			scc:      trueSCC,
			pod:      truePod,
			expected: expectTrue,
		},
	}

	for k, v := range tests {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Errorf("%s unable to create provider %v", k, err)
			continue
		}
		sc, err := provider.CreateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0])
		if err != nil {
			t.Errorf("%s unable to create container security context %v", k, err)
			continue
		}

		if v.expected == nil && sc.ReadOnlyRootFilesystem != nil {
			t.Errorf("%s expected a nil ReadOnlyRootFilesystem but got %t", k, *sc.ReadOnlyRootFilesystem)
		}
		if v.expected != nil && sc.ReadOnlyRootFilesystem == nil {
			t.Errorf("%s expected a non nil ReadOnlyRootFilesystem but recieved nil", k)
		}
		if v.expected != nil && sc.ReadOnlyRootFilesystem != nil && (*v.expected != *sc.ReadOnlyRootFilesystem) {
			t.Errorf("%s expected a non nil ReadOnlyRootFilesystem set to %t but got %t", k, *v.expected, *sc.ReadOnlyRootFilesystem)
		}

	}
}

func TestGenerateNonRootSecurityContextOnNonZeroRunAsUser(t *testing.T) {
	userSCC := defaultSCC()
	var minRange int64 = 100
	var maxRange int64 = 900
	userSCC.RunAsUser = securityv1.RunAsUserStrategyOptions{
		Type:        securityv1.RunAsUserStrategyMustRunAsRange,
		UIDRangeMin: &minRange,
		UIDRangeMax: &maxRange,
	}

	rootMinSCC := defaultSCC()
	var rootUID int64 = 0
	rootMinSCC.RunAsUser = securityv1.RunAsUserStrategyOptions{
		Type:        securityv1.RunAsUserStrategyMustRunAsRange,
		UIDRangeMin: &rootUID,
		UIDRangeMax: &maxRange,
	}

	nonRootSCC := defaultSCC()
	nonRootSCC.RunAsUser = securityv1.RunAsUserStrategyOptions{
		Type: securityv1.RunAsUserStrategyMustRunAsNonRoot,
	}

	var useruid int64 = 500
	containerUserPod := defaultPod()
	containerUserPod.Spec.Containers[0].SecurityContext.RunAsUser = &useruid

	podUserPod := defaultPod()
	podUserPod.Spec.SecurityContext.RunAsUser = &useruid

	zeroPodUserPod := defaultPod()
	zeroPodUserPod.Spec.SecurityContext.RunAsUser = &rootUID

	zeroContainerUserPod := defaultPod()
	zeroContainerUserPod.Spec.Containers[0].SecurityContext.RunAsUser = &rootUID

	zeroPodUserPodNonRootSCC := zeroPodUserPod.DeepCopy()
	zeroContainerUserPodNonRootSCC := zeroContainerUserPod.DeepCopy()

	falseVal := false
	trueVal := true
	tests := map[string]struct {
		pod        *api.Pod
		scc        *securityv1.SecurityContextConstraints
		expectedSC *api.SecurityContext
	}{
		"generate non-zero user": {
			pod: defaultPod(),
			scc: userSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    &minRange,
				RunAsNonRoot: &trueVal,
				Privileged:   &falseVal,
			},
		},
		"generate zero user": {
			pod: defaultPod(),
			scc: rootMinSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    &rootUID,
				RunAsNonRoot: nil,
				Privileged:   &falseVal,
			},
		},
		"nonzero user set on pod level": {
			pod: podUserPod,
			scc: userSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    nil,
				RunAsNonRoot: &trueVal,
				Privileged:   &falseVal,
			},
		},
		"nonzero user set on container level": {
			pod: containerUserPod,
			scc: userSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    &useruid,
				RunAsNonRoot: &trueVal,
				Privileged:   &falseVal,
			},
		},
		"zero user set on pod level": {
			pod: zeroPodUserPod,
			scc: rootMinSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    nil,
				RunAsNonRoot: nil,
				Privileged:   &falseVal,
			},
		},
		"zero user set on container level": {
			pod: zeroContainerUserPod,
			scc: rootMinSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    &rootUID,
				RunAsNonRoot: nil,
				Privileged:   &falseVal,
			},
		},
		"no user set, nonroot SCC": {
			pod: defaultPod(),
			scc: nonRootSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    nil,
				RunAsNonRoot: &trueVal,
				Privileged:   &falseVal,
			},
		},
		"zero user set on pod level, nonroot SCC": {
			pod: zeroPodUserPodNonRootSCC,
			scc: nonRootSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    nil,
				RunAsNonRoot: nil,
				Privileged:   &falseVal,
			},
		},
		"zero user set on container level, nonroot SCC": {
			pod: zeroContainerUserPodNonRootSCC,
			scc: nonRootSCC,
			expectedSC: &api.SecurityContext{
				RunAsUser:    &rootUID,
				RunAsNonRoot: nil,
				Privileged:   &falseVal,
			},
		},
	}

	for k, v := range tests {
		provider, err := NewSimpleProvider(v.scc)
		if err != nil {
			t.Errorf("%s unable to create provider %v", k, err)
			continue
		}
		sc, err := provider.CreateContainerSecurityContext(v.pod, &v.pod.Spec.Containers[0])
		if err != nil {
			t.Errorf("%s unable to create container security context %v", k, err)
			continue
		}

		if !equality.Semantic.DeepEqual(v.expectedSC, sc) {
			t.Errorf("%s expected security context does not match the actual: %s", k, diff.ObjectDiff(v.expectedSC, sc))
		}

	}
}

func defaultSCC() *securityv1.SecurityContextConstraints {
	return &securityv1.SecurityContextConstraints{
		ObjectMeta: metav1.ObjectMeta{
			Name:        "scc-sa",
			Annotations: map[string]string{},
		},
		RunAsUser: securityv1.RunAsUserStrategyOptions{
			Type: securityv1.RunAsUserStrategyRunAsAny,
		},
		SELinuxContext: securityv1.SELinuxContextStrategyOptions{
			Type: securityv1.SELinuxStrategyRunAsAny,
		},
		FSGroup: securityv1.FSGroupStrategyOptions{
			Type: securityv1.FSGroupStrategyRunAsAny,
		},
		SupplementalGroups: securityv1.SupplementalGroupsStrategyOptions{
			Type: securityv1.SupplementalGroupsStrategyRunAsAny,
		},
	}
}

func defaultPod() *api.Pod {
	var notPriv bool = false
	return &api.Pod{
		ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}},
		Spec: api.PodSpec{
			SecurityContext: &api.PodSecurityContext{
				// fill in for test cases
			},
			Containers: []api.Container{
				{
					SecurityContext: &api.SecurityContext{
						// expected to be set by defaulting mechanisms
						Privileged: &notPriv,
						// fill in the rest for test cases
					},
				},
			},
		},
	}
}

func allowFlexVolumesSCC(allowAllFlexVolumes, allowAllVolumes bool) *securityv1.SecurityContextConstraints {
	scc := defaultSCC()

	allowedVolumes := []securityv1.AllowedFlexVolume{
		{Driver: "example/foo"},
		{Driver: "example/bar"},
	}
	if allowAllFlexVolumes {
		allowedVolumes = []securityv1.AllowedFlexVolume{}
	}

	allowedVolumeType := securityv1.FSTypeFlexVolume
	if allowAllVolumes {
		allowedVolumeType = securityv1.FSTypeAll
	}

	scc.AllowedFlexVolumes = allowedVolumes
	scc.Volumes = []securityv1.FSType{allowedVolumeType}

	return scc
}

// TestValidateAllowedVolumes will test that for every field of VolumeSource we can create
// a pod with that type of volume and deny it, accept it explicitly, or accept it with
// the FSTypeAll wildcard.
func TestValidateAllowedVolumes(t *testing.T) {
	val := reflect.ValueOf(api.VolumeSource{})

	for i := 0; i < val.NumField(); i++ {
		// reflectively create the volume source
		fieldVal := val.Type().Field(i)

		volumeSource := api.VolumeSource{}
		volumeSourceVolume := reflect.New(fieldVal.Type.Elem())

		reflect.ValueOf(&volumeSource).Elem().FieldByName(fieldVal.Name).Set(volumeSourceVolume)
		volume := api.Volume{VolumeSource: volumeSource}

		// sanity check before moving on
		fsType, err := sccutil.GetVolumeFSType(volume)
		if err != nil {
			t.Errorf("error getting FSType for %s: %s", fieldVal.Name, err.Error())
			continue
		}

		// add the volume to the pod
		pod := defaultPod()
		pod.Spec.Volumes = []api.Volume{volume}

		// create an SCC that allows no volumes
		scc := defaultSCC()

		provider, err := NewSimpleProvider(scc)
		if err != nil {
			t.Errorf("error creating provider for %s: %s", fieldVal.Name, err.Error())
			continue
		}

		// expect a denial for this SCC and test the error message to ensure it's related to the volumesource
		errs := provider.ValidatePodSecurityContext(pod, field.NewPath(""))
		if len(errs) != 1 {
			t.Errorf("expected exactly 1 error for %s but got %v", fieldVal.Name, errs)
		} else {
			if !strings.Contains(errs.ToAggregate().Error(), fmt.Sprintf("%s volumes are not allowed to be used", fsType)) {
				t.Errorf("did not find the expected error, received: %v", errs)
			}
		}

		// now add the fstype directly to the scc and it should validate
		scc.Volumes = []securityv1.FSType{fsType}
		errs = provider.ValidatePodSecurityContext(pod, field.NewPath(""))
		if len(errs) != 0 {
			t.Errorf("directly allowing volume expected no errors for %s but got %v", fieldVal.Name, errs)
		}

		// now change the scc to allow any volumes and the pod should still validate
		scc.Volumes = []securityv1.FSType{securityv1.FSTypeAll}
		errs = provider.ValidatePodSecurityContext(pod, field.NewPath(""))
		if len(errs) != 0 {
			t.Errorf("wildcard volume expected no errors for %s but got %v", fieldVal.Name, errs)
		}
	}
}

func TestValidateProjectedVolume(t *testing.T) {
	pod := defaultPod()
	scc := defaultSCC()
	provider, err := NewSimpleProvider(scc)
	require.NoError(t, err, "error creating provider")

	tests := []struct {
		desc                  string
		allowedFSTypes        []securityv1.FSType
		projectedVolumeSource *api.ProjectedVolumeSource
		wantAllow             bool
	}{
		{
			desc:                  "deny if secret is not allowed",
			allowedFSTypes:        []securityv1.FSType{securityv1.FSTypeEmptyDir},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().ToVolumeSource(),
			wantAllow:             false,
		},
		{
			desc:           "deny if the projected volume has volume source other than the ones in projected volume injected by service account token admission plugin",
			allowedFSTypes: []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: &api.ProjectedVolumeSource{
				Sources: []api.VolumeProjection{
					{
						ConfigMap: &api.ConfigMapProjection{
							LocalObjectReference: api.LocalObjectReference{
								Name: "foo-ca.crt",
							},
							Items: []api.KeyToPath{
								{
									Key:  "ca.crt",
									Path: "ca.crt",
								},
							},
						},
					},
				}},
			wantAllow: false,
		},
		{
			desc:                  "allow if secret is allowed and the projected volume sources equals to the ones injected by service account admission plugin",
			allowedFSTypes:        []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().ToVolumeSource(),
			wantAllow:             true,
		},
		{
			desc:                  "deny if the SA has a slightly different path",
			allowedFSTypes:        []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().WithSA("notAToken").ToVolumeSource(),
			wantAllow:             false,
		},
		{
			desc:           "deny if there's an unknown CM",
			allowedFSTypes: []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().
				WithCM("random-key",
					"unknown-local-reference",
					api.KeyToPath{
						Key:  "ca.crt",
						Path: "ca.crt",
					}).ToVolumeSource(),
			wantAllow: false,
		},
		{
			desc:           "deny if the kube-root-ca has wrong paths",
			allowedFSTypes: []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().
				WithKubeRootCA("openshift-service-ca.crt", api.KeyToPath{
					Key:  "allow-all.crt",
					Path: "ca.crt",
				}).ToVolumeSource(),
			wantAllow: false,
		},
		{
			desc:           "deny if the openshift-ca has wrong paths",
			allowedFSTypes: []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().
				WithOpenShiftServiceCA("openshift-service-ca.crt", api.KeyToPath{
					Key:  "malicious.crt",
					Path: "service-ca.crt",
				}).ToVolumeSource(),
			wantAllow: false,
		},
		{
			desc:           "deny if the downward API sets an unknown fieldPath",
			allowedFSTypes: []securityv1.FSType{securityv1.FSTypeSecret},
			projectedVolumeSource: newProjectedVolumeCreator().FullValid().
				WithDownwardAPI("namespace", &api.ObjectFieldSelector{
					APIVersion: "v1",
					FieldPath:  "spec.serviceAccount",
				}).ToVolumeSource(),
			wantAllow: false,
		},
	}
	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			pod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{Projected: test.projectedVolumeSource}}}
			scc.Volumes = test.allowedFSTypes
			errs := provider.ValidatePodSecurityContext(pod, field.NewPath(""))
			if test.wantAllow {
				require.Empty(t, errs, "projected volumes are allowed if secret volumes is allowed and BoundServiceAccountTokenVolume is enabled")
			} else {
				require.Greaterf(t, len(errs), 0, "expected errors but got none")
				require.Contains(t, errs.ToAggregate().Error(), "projected volumes are not allowed to be used", "did not find the expected error")
			}
		})
	}
}

// TestValidateAllowPrivilegeEscalation will test that when the SecurityContextConstraints
// AllowPrivilegeEscalation is false we cannot set a container's securityContext
// to allowPrivilegeEscalation, but when it is true we can.
func TestValidateAllowPrivilegeEscalation(t *testing.T) {
	yes := true
	no := false

	pod := defaultPod()
	pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &yes

	// create a SCC that does not allow privilege escalation
	scc := defaultSCC()
	scc.AllowPrivilegeEscalation = &no

	provider, err := NewSimpleProvider(scc)
	if err != nil {
		t.Errorf("error creating provider: %v", err.Error())
	}

	// expect a denial for this SCC and test the error message to ensure it's related to allowPrivilegeEscalation
	errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
	if len(errs) != 1 {
		t.Errorf("expected exactly 1 error but got %v", errs)
	} else {
		if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
			t.Errorf("did not find the expected error, received: %v", errs)
		}
	}

	// Now set AllowPrivilegeEscalation
	scc.AllowPrivilegeEscalation = &yes
	errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
	if len(errs) != 0 {
		t.Errorf("directly allowing privilege escalation expected no errors but got %v", errs)
	}

	// Now set the scc spec to false and reset AllowPrivilegeEscalation
	scc.AllowPrivilegeEscalation = &no
	pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
	errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
	if len(errs) != 1 {
		t.Errorf("expected exactly 1 error but got %v", errs)
	} else {
		if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
			t.Errorf("did not find the expected error, received: %v", errs)
		}
	}

	// Now unset both AllowPrivilegeEscalation
	scc.AllowPrivilegeEscalation = &yes
	pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
	errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
	if len(errs) != 0 {
		t.Errorf("resetting allowing privilege escalation expected no errors but got %v", errs)
	}
}

func TestSeccompAnnotationsFieldsGeneration(t *testing.T) {
	noSeccompProvider, err := NewSimpleProvider(defaultSCC())
	require.NoError(t, err)

	sccWildcardSeccomp := defaultSCC()
	sccWildcardSeccomp.SeccompProfiles = []string{"*"}
	wildcardSeccompProvider, err := NewSimpleProvider(sccWildcardSeccomp)
	require.NoError(t, err)

	sccGenerateSeccomp := defaultSCC()
	sccGenerateSeccomp.SeccompProfiles = []string{corev1.SeccompProfileRuntimeDefault}
	generateSeccompProvider, err := NewSimpleProvider(sccGenerateSeccomp)
	require.NoError(t, err)

	podPodSeccompAnnotation := defaultPod()
	podPodSeccompAnnotation.Annotations = map[string]string{
		api.SeccompPodAnnotationKey: corev1.SeccompProfileRuntimeDefault,
	}

	podPodSeccompField := defaultPod()
	podPodSeccompField.Spec.SecurityContext.SeccompProfile = &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault}

	for _, tt := range []struct {
		name                     string
		sccProvider              SecurityContextConstraintsProvider
		pod                      *api.Pod
		expectedPodAnnotations   map[string]string
		expectedPodSeccomp       *api.SeccompProfile
		expectedContainerSeccomp *api.SeccompProfile
	}{
		{
			name:                   "pod - no seccomp, SCC - no seccomp",
			pod:                    defaultPod(),
			sccProvider:            noSeccompProvider,
			expectedPodAnnotations: map[string]string{},
		},
		{
			name:                   "pod - no seccomp, SCC - wildcard seccomp",
			pod:                    defaultPod(),
			sccProvider:            wildcardSeccompProvider,
			expectedPodAnnotations: map[string]string{},
		},
		{
			name:        "pod - no seccomp, SCC - generate seccomp",
			pod:         defaultPod(),
			sccProvider: generateSeccompProvider,
			expectedPodAnnotations: map[string]string{
				api.SeccompPodAnnotationKey: "runtime/default",
			},
			expectedPodSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault},
		},
		{
			name:                   "pod - pod annotation, SCC - no seccomp",
			pod:                    withAnnotations(defaultPod(), map[string]string{api.SeccompPodAnnotationKey: "unconfined"}),
			sccProvider:            noSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod annotation, SCC - wildcard seccomp",
			pod:                    withAnnotations(defaultPod(), map[string]string{api.SeccompPodAnnotationKey: "unconfined"}),
			sccProvider:            wildcardSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod annotation, SCC - generate seccomp",
			pod:                    withAnnotations(defaultPod(), map[string]string{api.SeccompPodAnnotationKey: "unconfined"}),
			sccProvider:            generateSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod field, SCC - no seccomp",
			pod:                    withPodSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider:            noSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod field, SCC - wildcard seccomp",
			pod:                    withPodSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider:            wildcardSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod field, SCC - generate seccomp",
			pod:                    withPodSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider:            generateSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                     "pod - container annotation, SCC - no seccomp",
			pod:                      withAnnotations(defaultPod(), map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"}),
			sccProvider:              noSeccompProvider,
			expectedPodAnnotations:   map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                     "pod - container annotation, SCC - wildcard seccomp",
			pod:                      withAnnotations(defaultPod(), map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"}),
			sccProvider:              wildcardSeccompProvider,
			expectedPodAnnotations:   map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:        "pod - container annotation, SCC - generate seccomp",
			pod:         withAnnotations(defaultPod(), map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"}),
			sccProvider: generateSeccompProvider,
			expectedPodAnnotations: map[string]string{
				api.SeccompPodAnnotationKey:                  "runtime/default",
				api.SeccompContainerAnnotationKeyPrefix + "": "unconfined",
			},
			expectedPodSeccomp:       &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                     "pod - container field, SCC - no seccomp",
			pod:                      withContainerSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider:              noSeccompProvider,
			expectedPodAnnotations:   map[string]string{},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                     "pod - container field, SCC - wildcard seccomp",
			pod:                      withContainerSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider:              wildcardSeccompProvider,
			expectedPodAnnotations:   map[string]string{},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:        "pod - container field, SCC - generate seccomp",
			pod:         withContainerSeccomp(defaultPod(), &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined}),
			sccProvider: generateSeccompProvider,
			expectedPodAnnotations: map[string]string{
				api.SeccompPodAnnotationKey: "runtime/default",
			},
			expectedPodSeccomp:       &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:                   "pod - pod field and annotation different, SCC - generate seccomp",
			pod:                    withPodSeccomp(withAnnotations(defaultPod(), map[string]string{api.SeccompPodAnnotationKey: "unconfined"}), &api.SeccompProfile{Type: api.SeccompProfileTypeLocalhost, LocalhostProfile: pointer.String("somelocal")}),
			sccProvider:            generateSeccompProvider,
			expectedPodAnnotations: map[string]string{api.SeccompPodAnnotationKey: "unconfined"},
			expectedPodSeccomp:     &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
		{
			name:        "pod - container field and annotation different, SCC - generate seccomp",
			pod:         withContainerSeccomp(withAnnotations(defaultPod(), map[string]string{api.SeccompContainerAnnotationKeyPrefix + "": "unconfined"}), &api.SeccompProfile{Type: api.SeccompProfileTypeLocalhost, LocalhostProfile: pointer.String("somelocal")}),
			sccProvider: generateSeccompProvider,
			expectedPodAnnotations: map[string]string{
				api.SeccompContainerAnnotationKeyPrefix + "": "unconfined",
				api.SeccompPodAnnotationKey:                  "runtime/default",
			},
			expectedPodSeccomp:       &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault},
			expectedContainerSeccomp: &api.SeccompProfile{Type: api.SeccompProfileTypeUnconfined},
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			podSecurityContext, podAnnotations, err := tt.sccProvider.CreatePodSecurityContext(tt.pod)
			require.NoError(t, err)

			if !reflect.DeepEqual(tt.expectedPodAnnotations, podAnnotations) {
				t.Errorf("pod annotations differ: %s", cmp.Diff(tt.expectedPodAnnotations, podAnnotations))
			}

			if !reflect.DeepEqual(tt.expectedPodSeccomp, podSecurityContext.SeccompProfile) {
				t.Errorf("pod seccomp profiles differ - expected %v; got %v", tt.expectedPodSeccomp, podSecurityContext.SeccompProfile)
			}

			containerSecurityContext, err := tt.sccProvider.CreateContainerSecurityContext(tt.pod, &tt.pod.Spec.Containers[0])
			require.NoError(t, err)

			if !reflect.DeepEqual(tt.expectedContainerSeccomp, containerSecurityContext.SeccompProfile) {
				t.Errorf("container seccomp profiles differ - expected %v; got %v", tt.expectedContainerSeccomp, containerSecurityContext.SeccompProfile)
			}
		})
	}
}

func withAnnotations(pod *api.Pod, annotations map[string]string) *api.Pod {
	pod.Annotations = annotations
	return pod
}

func withPodSeccomp(pod *api.Pod, seccompProfile *api.SeccompProfile) *api.Pod {
	pod.Spec.SecurityContext.SeccompProfile = seccompProfile
	return pod
}

func withContainerSeccomp(pod *api.Pod, seccompProfile *api.SeccompProfile) *api.Pod {
	pod.Spec.Containers[0].SecurityContext.SeccompProfile = seccompProfile
	return pod
}

type projectedVolumeCreator struct {
	volumes map[string]api.VolumeProjection
}

func newProjectedVolumeCreator() *projectedVolumeCreator {
	return &projectedVolumeCreator{
		volumes: map[string]api.VolumeProjection{},
	}
}

func (p *projectedVolumeCreator) FullValid() *projectedVolumeCreator {
	return p.WithSA("token").
		WithKubeRootCA("kube-root-ca.crt", api.KeyToPath{
			Key:  "ca.crt",
			Path: "ca.crt",
		}).
		WithOpenShiftServiceCA("openshift-service-ca.crt", api.KeyToPath{
			Key:  "service-ca.crt",
			Path: "service-ca.crt",
		}).
		WithDownwardAPI("namespace", &api.ObjectFieldSelector{
			APIVersion: "v1",
			FieldPath:  "metadata.namespace",
		})
}

func (p *projectedVolumeCreator) ToVolumeSource() *api.ProjectedVolumeSource {
	ret := &api.ProjectedVolumeSource{
		Sources: []api.VolumeProjection{},
	}

	for _, v := range p.volumes {
		ret.Sources = append(ret.Sources, v)
	}

	return ret
}

func (p *projectedVolumeCreator) WithSA(path string) *projectedVolumeCreator {
	p.volumes["sa"] = api.VolumeProjection{
		ServiceAccountToken: &api.ServiceAccountTokenProjection{
			Path:              path,
			ExpirationSeconds: 3607,
		},
	}
	return p
}

func (p *projectedVolumeCreator) WithOpenShiftServiceCA(refName string, paths ...api.KeyToPath) *projectedVolumeCreator {
	return p.WithCM("openshift-service-ca", refName, paths...)
}

func (p *projectedVolumeCreator) WithKubeRootCA(refName string, paths ...api.KeyToPath) *projectedVolumeCreator {
	return p.WithCM("kube-root-ca", refName, paths...)
}

func (p *projectedVolumeCreator) WithCM(key, refName string, paths ...api.KeyToPath) *projectedVolumeCreator {
	p.volumes[key] = api.VolumeProjection{
		ConfigMap: &api.ConfigMapProjection{
			LocalObjectReference: api.LocalObjectReference{
				Name: refName,
			},
			Items: paths,
		},
	}

	return p
}

func (p *projectedVolumeCreator) WithDownwardAPI(path string, fieldRef *api.ObjectFieldSelector) *projectedVolumeCreator {
	p.volumes["downward-api"] = api.VolumeProjection{
		DownwardAPI: &api.DownwardAPIProjection{
			Items: []api.DownwardAPIVolumeFile{
				{
					Path:     path,
					FieldRef: fieldRef,
				},
			},
		},
	}
	return p
}

repository: soltysh/apiserver-library-go

repo_name: soltysh/apiserver-library-go


text:
DESCRIPTION = "A Python Wrapper for libid3."
SECTION = "devel/python"
PRIORITY = "optional"
MAINTAINER = "Jason Haslup <openembedded@haslup.com>"
LICENSE = "LGPL"
DEPENDS = "id3lib"
SRCNAME = "pyid3lib"

SRC_URI = "${SOURCEFORGE_MIRROR}/pyid3lib/${SRCNAME}-${PV}.tar.bz2"
S = "${WORKDIR}/${SRCNAME}-${PV}"

inherit distutils

license: MIT

license_type: permissive

branch_name: refs/heads/master

extension: bb


text:
SUMMARY = "A suite of libcamera-based apps"
DESCRIPTION = "This is a small suite of libcamera-based apps that aim to \
copy the functionality of the existing \"raspicam\" apps."
HOMEPAGE = "https://github.com/raspberrypi/libcamera-apps"
SECTION = "console/utils"

LICENSE = "BSD-2-Clause"
LIC_FILES_CHKSUM = "file://license.txt;md5=a0013d1b383d72ba4bdc5b750e7d1d77"

SRC_URI = "\
    git://github.com/raspberrypi/libcamera-apps.git;protocol=https;branch=main \
    file://0001-utils-version.py-use-usr-bin-env-in-shebang.patch \
"
PV = "1.2.1+git${SRCPV}"
SRCREV = "1c1d1c1a2a86d70cf873edc8bb72d174f037973a"

S = "${WORKDIR}/git"

DEPENDS = "libcamera libexif jpeg tiff libpng boost"

PACKAGECONFIG ??= "drm"
PACKAGECONFIG[libav] = "-Denable_libav=true, -Denable_libav=false, libav"
PACKAGECONFIG[drm] = "-Denable_drm=true, -Denable_drm=false, libdrm"
PACKAGECONFIG[egl] = "-Denable_egl=true, -Denable_egl=false, virtual/egl"
PACKAGECONFIG[qt] = "-Denable_qt=true, -Denable_qt=false, qtbase"
PACKAGECONFIG[opencv] = "-Denable_opencv=true, -Denable_opencv=false, opencv"
PACKAGECONFIG[tflite] = "-Denable_tflite=true, -Denable_tflite=false, tensorflow-lite"

inherit meson pkgconfig

NEON_FLAGS = ""
NEON_FLAGS:aarch64 = "-Dneon_flags=arm64"
NEON_FLAGS:arm:raspberrypi3 = "-Dneon_flags=armv8-neon"
NEON_FLAGS:arm:raspberrypi4 = "-Dneon_flags=armv8-neon"
EXTRA_OEMESON += "${NEON_FLAGS}"

# QA Issue: /usr/bin/camera-bug-report contained in package libcamera-apps requires /usr/bin/python3
do_install:append() {
    rm -v ${D}/${bindir}/camera-bug-report
}

repository: YoeDistro/meta-raspberrypi

repo_name: YoeDistro/meta-raspberrypi

gha_license_id: MIT


text:
package aws

import (
	"github.com/aws/aws-sdk-go/service/sts"
	"testing"
)

func TestEnvironmentVariables(t *testing.T) {
	accessKeyId := "llama"
	secretAccessKey := "alpaca"
	sessionToken := "guanaco"
	assumedRoleArn := "arn:aws:iam::1234123123:role/sso-vicuña-role"

	creds := sts.AssumeRoleWithSAMLOutput{
		AssumedRoleUser: &sts.AssumedRoleUser{
			Arn: &assumedRoleArn,
		},
		Credentials: &sts.Credentials{
			AccessKeyId:     &accessKeyId,
			SecretAccessKey: &secretAccessKey,
			SessionToken:    &sessionToken,
		},
	}

	subject := EnvironmentVariables(&creds)

	if subject["AWS_ACCESS_KEY_ID"] != accessKeyId {
		t.Log("---------------")
		t.Log("Did not correctly set AWS_ACCESS_KEY_ID")
		t.Logf("Expected: %s", accessKeyId)
		t.Logf("Got: %s", subject["AWS_ACCESS_KEY_ID"])
		t.Fail()
	}

	if subject["AWS_SECRET_ACCESS_KEY"] != secretAccessKey {
		t.Log("---------------")
		t.Log("Did not correctly set AWS_SECRET_ACCESS_KEY")
		t.Logf("Expected: %s", secretAccessKey)
		t.Logf("Got: %s", subject["AWS_SECRET_ACCESS_KEY"])
		t.Fail()
	}

	if subject["AWS_SESSION_TOKEN"] != sessionToken {
		t.Log("---------------")
		t.Log("Did not correctly set AWS_SESSION_TOKEN")
		t.Logf("Expected: %s", sessionToken)
		t.Logf("Got: %s", subject["AWS_SESSION_TOKEN"])
		t.Fail()
	}

	if subject["AWS_METADATA_USER_ARN"] != assumedRoleArn {
		t.Log("---------------")
		t.Log("Did not correctly set AWS_METADATA_USER_ARN")
		t.Logf("Expected: %s", assumedRoleArn)
		t.Logf("Got: %s", subject["AWS_METADATA_USER_ARN"])
		t.Fail()
	}
}

repository: redbubble/yak

repo_name: redbubble/yak

gha_language: Go

extension: go


text:
@book{biggs02,
  title={Discrete Mathematics},
  author={Biggs, N.},
  isbn={9780198507178},
  lccn={85008964},
  series={Oxford science publications},
  url={https://books.google.ie/books?id=Mj9gzZMrXDIC},
  year={2002},
  publisher={OUP Oxford}
}

@book{sipser96,
 author = {Sipser, Michael},
 title = {Introduction to the Theory of Computation},
 year = {1996},
 isbn = {053494728X},
 edition = {3rd},
 publisher = {International Thomson Publishing},
}

repository: ianmcloughlin/problems-finite-automata

repo_name: ianmcloughlin/problems-finite-automata

extension: bib


text:
# Ceres Solver - A fast non-linear least squares minimizer
# Copyright 2015 Google Inc. All rights reserved.
# http://ceres-solver.org/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of Google Inc. nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: alexs.mac@gmail.com (Alex Stewart)
#

# FindGlog.cmake - Find Google glog logging library.
#
# This module defines the following variables:
#
# GLOG_FOUND: TRUE iff glog is found.
# GLOG_INCLUDE_DIRS: Include directories for glog.
# GLOG_LIBRARIES: Libraries required to link glog.
# FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION: True iff the version of glog found
#                                           was built & installed / exported
#                                           as a CMake package.
#
# The following variables control the behaviour of this module:
#
# GLOG_PREFER_EXPORTED_GLOG_CMAKE_CONFIGURATION: TRUE/FALSE, iff TRUE then
#                           then prefer using an exported CMake configuration
#                           generated by glog > 0.3.4 over searching for the
#                           glog components manually.  Otherwise (FALSE)
#                           ignore any exported glog CMake configurations and
#                           always perform a manual search for the components.
#                           Default: TRUE iff user does not define this variable
#                           before we are called, and does NOT specify either
#                           GLOG_INCLUDE_DIR_HINTS or GLOG_LIBRARY_DIR_HINTS
#                           otherwise FALSE.
# GLOG_INCLUDE_DIR_HINTS: List of additional directories in which to
#                         search for glog includes, e.g: /timbuktu/include.
# GLOG_LIBRARY_DIR_HINTS: List of additional directories in which to
#                         search for glog libraries, e.g: /timbuktu/lib.
#
# The following variables are also defined by this module, but in line with
# CMake recommended FindPackage() module style should NOT be referenced directly
# by callers (use the plural variables detailed above instead).  These variables
# do however affect the behaviour of the module via FIND_[PATH/LIBRARY]() which
# are NOT re-called (i.e. search for library is not repeated) if these variables
# are set with valid values _in the CMake cache_. This means that if these
# variables are set directly in the cache, either by the user in the CMake GUI,
# or by the user passing -DVAR=VALUE directives to CMake when called (which
# explicitly defines a cache variable), then they will be used verbatim,
# bypassing the HINTS variables and other hard-coded search locations.
#
# GLOG_INCLUDE_DIR: Include directory for glog, not including the
#                   include directory of any dependencies.
# GLOG_LIBRARY: glog library, not including the libraries of any
#               dependencies.

# Reset CALLERS_CMAKE_FIND_LIBRARY_PREFIXES to its value when
# FindGlog was invoked.
macro(GLOG_RESET_FIND_LIBRARY_PREFIX)
  if (MSVC AND CALLERS_CMAKE_FIND_LIBRARY_PREFIXES)
    set(CMAKE_FIND_LIBRARY_PREFIXES "${CALLERS_CMAKE_FIND_LIBRARY_PREFIXES}")
  endif()
endmacro(GLOG_RESET_FIND_LIBRARY_PREFIX)

# Called if we failed to find glog or any of it's required dependencies,
# unsets all public (designed to be used externally) variables and reports
# error message at priority depending upon [REQUIRED/QUIET/<NONE>] argument.
macro(GLOG_REPORT_NOT_FOUND REASON_MSG)
  unset(GLOG_FOUND)
  unset(GLOG_INCLUDE_DIRS)
  unset(GLOG_LIBRARIES)
  # Make results of search visible in the CMake GUI if glog has not
  # been found so that user does not have to toggle to advanced view.
  mark_as_advanced(CLEAR GLOG_INCLUDE_DIR
                         GLOG_LIBRARY)

  glog_reset_find_library_prefix()

  # Note <package>_FIND_[REQUIRED/QUIETLY] variables defined by FindPackage()
  # use the camelcase library name, not uppercase.
  if (Glog_FIND_QUIETLY)
    message(STATUS "Failed to find glog - " ${REASON_MSG} ${ARGN})
  elseif (Glog_FIND_REQUIRED)
    message(FATAL_ERROR "Failed to find glog - " ${REASON_MSG} ${ARGN})
  else()
    # Neither QUIETLY nor REQUIRED, use no priority which emits a message
    # but continues configuration and allows generation.
    message("-- Failed to find glog - " ${REASON_MSG} ${ARGN})
  endif ()
  return()
endmacro(GLOG_REPORT_NOT_FOUND)

# glog_message([mode] "message text")
#
# Wraps the standard cmake 'message' command, but suppresses output
# if the QUIET flag was passed to the find_package(Glog ...) call.
function(GLOG_MESSAGE)
  if (NOT Glog_FIND_QUIETLY)
    message(${ARGN})
  endif()
endfunction()

# Protect against any alternative find_package scripts for this library having
# been called previously (in a client project) which set GLOG_FOUND, but not
# the other variables we require / set here which could cause the search logic
# here to fail.
unset(GLOG_FOUND)

# -----------------------------------------------------------------
# By default, if the user has expressed no preference for using an exported
# glog CMake configuration over performing a search for the installed
# components, and has not specified any hints for the search locations, then
# prefer a glog exported configuration if available.
if (NOT DEFINED GLOG_PREFER_EXPORTED_GLOG_CMAKE_CONFIGURATION
    AND NOT GLOG_INCLUDE_DIR_HINTS
    AND NOT GLOG_LIBRARY_DIR_HINTS)
  glog_message(STATUS "No preference for use of exported glog CMake "
    "configuration set, and no hints for include/library directories provided. "
    "Defaulting to preferring an installed/exported glog CMake configuration "
    "if available.")
  set(GLOG_PREFER_EXPORTED_GLOG_CMAKE_CONFIGURATION TRUE)
endif()

# On macOS, add the Homebrew prefix (with appropriate suffixes) to the
# respective HINTS directories (after any user-specified locations).  This
# handles Homebrew installations into non-standard locations (not /usr/local).
# We do not use CMAKE_PREFIX_PATH for this as given the search ordering of
# find_xxx(), doing so would override any user-specified HINTS locations with
# the Homebrew version if it exists.
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
  find_program(HOMEBREW_EXECUTABLE brew)
  mark_as_advanced(FORCE HOMEBREW_EXECUTABLE)
  if (HOMEBREW_EXECUTABLE)
    # Detected a Homebrew install, query for its install prefix.
    execute_process(COMMAND ${HOMEBREW_EXECUTABLE} --prefix
      OUTPUT_VARIABLE HOMEBREW_INSTALL_PREFIX
      OUTPUT_STRIP_TRAILING_WHITESPACE)
    glog_message(STATUS "Detected Homebrew with install prefix: "
      "${HOMEBREW_INSTALL_PREFIX}, adding to CMake search paths.")
    list(APPEND GLOG_INCLUDE_DIR_HINTS "${HOMEBREW_INSTALL_PREFIX}/include")
    list(APPEND GLOG_LIBRARY_DIR_HINTS "${HOMEBREW_INSTALL_PREFIX}/lib")
  endif()
endif()

if (GLOG_PREFER_EXPORTED_GLOG_CMAKE_CONFIGURATION)
  # Try to find an exported CMake configuration for glog, as generated by
  # glog versions > 0.3.4
  #
  # We search twice, s/t we can invert the ordering of precedence used by
  # find_package() for exported package build directories, and installed
  # packages (found via CMAKE_SYSTEM_PREFIX_PATH), listed as items 6) and 7)
  # respectively in [1].
  #
  # By default, exported build directories are (in theory) detected first, and
  # this is usually the case on Windows.  However, on OS X & Linux, the install
  # path (/usr/local) is typically present in the PATH environment variable
  # which is checked in item 4) in [1] (i.e. before both of the above, unless
  # NO_SYSTEM_ENVIRONMENT_PATH is passed).  As such on those OSs installed
  # packages are usually detected in preference to exported package build
  # directories.
  #
  # To ensure a more consistent response across all OSs, and as users usually
  # want to prefer an installed version of a package over a locally built one
  # where both exist (esp. as the exported build directory might be removed
  # after installation), we first search with NO_CMAKE_PACKAGE_REGISTRY which
  # means any build directories exported by the user are ignored, and thus
  # installed directories are preferred.  If this fails to find the package
  # we then research again, but without NO_CMAKE_PACKAGE_REGISTRY, so any
  # exported build directories will now be detected.
  #
  # To prevent confusion on Windows, we also pass NO_CMAKE_BUILDS_PATH (which
  # is item 5) in [1]), to not preferentially use projects that were built
  # recently with the CMake GUI to ensure that we always prefer an installed
  # version if available.
  #
  # NOTE: We use the NAMES option as glog erroneously uses 'google-glog' as its
  #       project name when built with CMake, but exports itself as just 'glog'.
  #       On Linux/OS X this does not break detection as the project name is
  #       not used as part of the install path for the CMake package files,
  #       e.g. /usr/local/lib/cmake/glog, where the <glog> suffix is hardcoded
  #       in glog's CMakeLists.  However, on Windows the project name *is*
  #       part of the install prefix: C:/Program Files/google-glog/[include,lib].
  #       However, by default CMake checks:
  #       C:/Program Files/<FIND_PACKAGE_ARGUMENT_NAME='glog'> which does not
  #       exist and thus detection fails.  Thus we use the NAMES to force the
  #       search to use both google-glog & glog.
  #
  # [1] http://www.cmake.org/cmake/help/v2.8.11/cmake.html#command:find_package
  find_package(glog QUIET
                    NAMES google-glog glog
                    HINTS ${glog_DIR} ${HOMEBREW_INSTALL_PREFIX}
                    NO_MODULE
                    NO_CMAKE_PACKAGE_REGISTRY
                    NO_CMAKE_BUILDS_PATH)
  if (glog_FOUND)
    glog_message(STATUS "Found installed version of glog: ${glog_DIR}")
  else()
    # Failed to find an installed version of glog, repeat search allowing
    # exported build directories.
    glog_message(STATUS "Failed to find installed glog CMake configuration, "
      "searching for glog build directories exported with CMake.")
    # Again pass NO_CMAKE_BUILDS_PATH, as we know that glog is exported and
    # do not want to treat projects built with the CMake GUI preferentially.
    find_package(glog QUIET
                      NAMES google-glog glog
                      NO_MODULE
                      NO_CMAKE_BUILDS_PATH)
    if (glog_FOUND)
      glog_message(STATUS "Found exported glog build directory: ${glog_DIR}")
    endif(glog_FOUND)
  endif(glog_FOUND)

  set(FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION ${glog_FOUND})

  if (FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION)
    glog_message(STATUS "Detected glog version: ${glog_VERSION}")
    set(GLOG_FOUND ${glog_FOUND})
    # glog wraps the include directories into the exported glog::glog target.
    set(GLOG_INCLUDE_DIR "")
    set(GLOG_LIBRARY glog::glog)
  else (FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION)
    glog_message(STATUS "Failed to find an installed/exported CMake "
      "configuration for glog, will perform search for installed glog "
      "components.")
  endif (FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION)
endif(GLOG_PREFER_EXPORTED_GLOG_CMAKE_CONFIGURATION)

if (NOT GLOG_FOUND)
  # Either failed to find an exported glog CMake configuration, or user
  # told us not to use one.  Perform a manual search for all glog components.

  # Handle possible presence of lib prefix for libraries on MSVC, see
  # also GLOG_RESET_FIND_LIBRARY_PREFIX().
  if (MSVC)
    # Preserve the caller's original values for CMAKE_FIND_LIBRARY_PREFIXES
    # s/t we can set it back before returning.
    set(CALLERS_CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}")
    # The empty string in this list is important, it represents the case when
    # the libraries have no prefix (shared libraries / DLLs).
    set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "" "${CMAKE_FIND_LIBRARY_PREFIXES}")
  endif (MSVC)

  # Search user-installed locations first, so that we prefer user installs
  # to system installs where both exist.
  list(APPEND GLOG_CHECK_INCLUDE_DIRS
    /usr/local/include
    /usr/local/homebrew/include # Mac OS X
    /opt/local/var/macports/software # Mac OS X.
    /opt/local/include
    /usr/include)
  # Windows (for C:/Program Files prefix).
  list(APPEND GLOG_CHECK_PATH_SUFFIXES
    glog/include
    glog/Include
    Glog/include
    Glog/Include
    google-glog/include # CMake installs with project name prefix.
    google-glog/Include)

  list(APPEND GLOG_CHECK_LIBRARY_DIRS
    /usr/local/lib
    /usr/local/homebrew/lib # Mac OS X.
    /opt/local/lib
    /usr/lib)
  # Windows (for C:/Program Files prefix).
  list(APPEND GLOG_CHECK_LIBRARY_SUFFIXES
    glog/lib
    glog/Lib
    Glog/lib
    Glog/Lib
    google-glog/lib # CMake installs with project name prefix.
    google-glog/Lib)

  # Search supplied hint directories first if supplied.
  find_path(GLOG_INCLUDE_DIR
    NAMES glog/logging.h
    HINTS ${GLOG_INCLUDE_DIR_HINTS}
    PATHS ${GLOG_CHECK_INCLUDE_DIRS}
    PATH_SUFFIXES ${GLOG_CHECK_PATH_SUFFIXES})
  if (NOT GLOG_INCLUDE_DIR OR
      NOT EXISTS ${GLOG_INCLUDE_DIR})
    glog_report_not_found(
      "Could not find glog include directory, set GLOG_INCLUDE_DIR "
      "to directory containing glog/logging.h")
  endif (NOT GLOG_INCLUDE_DIR OR
    NOT EXISTS ${GLOG_INCLUDE_DIR})

  find_library(GLOG_LIBRARY NAMES glog
    HINTS ${GLOG_LIBRARY_DIR_HINTS}
    PATHS ${GLOG_CHECK_LIBRARY_DIRS}
    PATH_SUFFIXES ${GLOG_CHECK_LIBRARY_SUFFIXES})
  if (NOT GLOG_LIBRARY OR
      NOT EXISTS ${GLOG_LIBRARY})
    glog_report_not_found(
      "Could not find glog library, set GLOG_LIBRARY "
      "to full path to libglog.")
  endif (NOT GLOG_LIBRARY OR
    NOT EXISTS ${GLOG_LIBRARY})

  # Mark internally as found, then verify. GLOG_REPORT_NOT_FOUND() unsets
  # if called.
  set(GLOG_FOUND TRUE)

  # Glog does not seem to provide any record of the version in its
  # source tree, thus cannot extract version.

  # Catch case when caller has set GLOG_INCLUDE_DIR in the cache / GUI and
  # thus FIND_[PATH/LIBRARY] are not called, but specified locations are
  # invalid, otherwise we would report the library as found.
  if (GLOG_INCLUDE_DIR AND
      NOT EXISTS ${GLOG_INCLUDE_DIR}/glog/logging.h)
    glog_report_not_found(
      "Caller defined GLOG_INCLUDE_DIR:"
      " ${GLOG_INCLUDE_DIR} does not contain glog/logging.h header.")
  endif (GLOG_INCLUDE_DIR AND
    NOT EXISTS ${GLOG_INCLUDE_DIR}/glog/logging.h)
  # TODO: This regex for glog library is pretty primitive, we use lowercase
  #       for comparison to handle Windows using CamelCase library names, could
  #       this check be better?
  string(TOLOWER "${GLOG_LIBRARY}" LOWERCASE_GLOG_LIBRARY)
  if (GLOG_LIBRARY AND
      NOT "${LOWERCASE_GLOG_LIBRARY}" MATCHES ".*glog[^/]*")
    glog_report_not_found(
      "Caller defined GLOG_LIBRARY: "
      "${GLOG_LIBRARY} does not match glog.")
  endif (GLOG_LIBRARY AND
    NOT "${LOWERCASE_GLOG_LIBRARY}" MATCHES ".*glog[^/]*")

  # add glog::glog target
  add_library(glog::glog INTERFACE IMPORTED)
  target_include_directories(glog::glog INTERFACE ${GLOG_INCLUDE_DIRS})
  target_link_libraries(glog::glog INTERFACE ${GLOG_LIBRARY})

  glog_reset_find_library_prefix()

endif(NOT GLOG_FOUND)

# Set standard CMake FindPackage variables if found.
if (GLOG_FOUND)
  set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR})
  set(GLOG_LIBRARIES ${GLOG_LIBRARY})
endif (GLOG_FOUND)

# If we are using an exported CMake glog target, the include directories are
# wrapped into the target itself, and do not have to be (and are not)
# separately specified.  In which case, we should not add GLOG_INCLUDE_DIRS
# to the list of required variables in order that glog be reported as found.
if (FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION)
  set(GLOG_REQUIRED_VARIABLES GLOG_LIBRARIES)
else()
  set(GLOG_REQUIRED_VARIABLES GLOG_INCLUDE_DIRS GLOG_LIBRARIES)
endif()

# Handle REQUIRED / QUIET optional arguments.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Glog DEFAULT_MSG
  ${GLOG_REQUIRED_VARIABLES})

# Only mark internal variables as advanced if we found glog, otherwise
# leave them visible in the standard GUI for the user to set manually.
if (GLOG_FOUND)
  mark_as_advanced(FORCE GLOG_INCLUDE_DIR
                         GLOG_LIBRARY
                         glog_DIR) # Autogenerated by find_package(glog)
endif (GLOG_FOUND)

repository: ceres-solver/ceres-solver

repo_name: ceres-solver/ceres-solver

gha_license_id: NOASSERTION

extension: cmake