github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/scanners/kubernetes/scanner_test.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/khulnasoft-lab/defsec/pkg/framework"
    10  	"github.com/khulnasoft-lab/defsec/pkg/scanners/options"
    11  
    12  	"github.com/khulnasoft-lab/defsec/pkg/scan"
    13  
    14  	"github.com/khulnasoft-lab/defsec/test/testutil"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func Test_BasicScan(t *testing.T) {
    22  
    23  	fs := testutil.CreateFS(t, map[string]string{
    24  		"/code/example.yaml": `
    25  apiVersion: v1
    26  kind: Pod
    27  metadata: 
    28    name: hello-cpu-limit
    29  spec: 
    30    containers: 
    31    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
    32      image: busybox
    33      name: hello
    34  `,
    35  		"/rules/lib.k8s.rego": `
    36   package lib.kubernetes
    37  
    38   default is_gatekeeper = false
    39  
    40   is_gatekeeper {
    41   	has_field(input, "review")
    42   	has_field(input.review, "object")
    43   }
    44  
    45   object = input {
    46   	not is_gatekeeper
    47   }
    48  
    49   object = input.review.object {
    50   	is_gatekeeper
    51   }
    52  
    53   format(msg) = gatekeeper_format {
    54   	is_gatekeeper
    55   	gatekeeper_format = {"msg": msg}
    56   }
    57  
    58   format(msg) = msg {
    59   	not is_gatekeeper
    60   }
    61  
    62   name = object.metadata.name
    63  
    64   default namespace = "default"
    65  
    66   namespace = object.metadata.namespace
    67  
    68   #annotations = object.metadata.annotations
    69  
    70   kind = object.kind
    71  
    72   is_pod {
    73   	kind = "Pod"
    74   }
    75  
    76   is_cronjob {
    77   	kind = "CronJob"
    78   }
    79  
    80   default is_controller = false
    81  
    82   is_controller {
    83   	kind = "Deployment"
    84   }
    85  
    86   is_controller {
    87   	kind = "StatefulSet"
    88   }
    89  
    90   is_controller {
    91   	kind = "DaemonSet"
    92   }
    93  
    94   is_controller {
    95   	kind = "ReplicaSet"
    96   }
    97  
    98   is_controller {
    99   	kind = "ReplicationController"
   100   }
   101  
   102   is_controller {
   103   	kind = "Job"
   104   }
   105  
   106   split_image(image) = [image, "latest"] {
   107   	not contains(image, ":")
   108   }
   109  
   110   split_image(image) = [image_name, tag] {
   111   	[image_name, tag] = split(image, ":")
   112   }
   113  
   114   pod_containers(pod) = all_containers {
   115   	keys = {"containers", "initContainers"}
   116   	all_containers = [c | keys[k]; c = pod.spec[k][_]]
   117   }
   118  
   119   containers[container] {
   120   	pods[pod]
   121   	all_containers = pod_containers(pod)
   122   	container = all_containers[_]
   123   }
   124  
   125   containers[container] {
   126   	all_containers = pod_containers(object)
   127   	container = all_containers[_]
   128   }
   129  
   130   pods[pod] {
   131   	is_pod
   132   	pod = object
   133   }
   134  
   135   pods[pod] {
   136   	is_controller
   137   	pod = object.spec.template
   138   }
   139  
   140   pods[pod] {
   141   	is_cronjob
   142   	pod = object.spec.jobTemplate.spec.template
   143   }
   144  
   145   volumes[volume] {
   146   	pods[pod]
   147   	volume = pod.spec.volumes[_]
   148   }
   149  
   150   dropped_capability(container, cap) {
   151   	container.securityContext.capabilities.drop[_] == cap
   152   }
   153  
   154   added_capability(container, cap) {
   155   	container.securityContext.capabilities.add[_] == cap
   156   }
   157  
   158   has_field(obj, field) {
   159   	obj[field]
   160   }
   161  
   162   no_read_only_filesystem(c) {
   163   	not has_field(c, "securityContext")
   164   }
   165  
   166   no_read_only_filesystem(c) {
   167   	has_field(c, "securityContext")
   168   	not has_field(c.securityContext, "readOnlyRootFilesystem")
   169   }
   170  
   171   privilege_escalation_allowed(c) {
   172   	not has_field(c, "securityContext")
   173   }
   174  
   175   privilege_escalation_allowed(c) {
   176   	has_field(c, "securityContext")
   177   	has_field(c.securityContext, "allowPrivilegeEscalation")
   178   }
   179  
   180   annotations[annotation] {
   181   	pods[pod]
   182   	annotation = pod.metadata.annotations
   183   }
   184  
   185   host_ipcs[host_ipc] {
   186   	pods[pod]
   187   	host_ipc = pod.spec.hostIPC
   188   }
   189  
   190   host_networks[host_network] {
   191   	pods[pod]
   192   	host_network = pod.spec.hostNetwork
   193   }
   194  
   195   host_pids[host_pid] {
   196   	pods[pod]
   197   	host_pid = pod.spec.hostPID
   198   }
   199  
   200   host_aliases[host_alias] {
   201   	pods[pod]
   202   	host_alias = pod.spec
   203   }
   204   `,
   205  		"/rules/lib.util.rego": `
   206   package lib.utils
   207  
   208   has_key(x, k) {
   209   	_ = x[k]
   210   }`,
   211  		"/rules/rule.rego": `
   212  package builtin.kubernetes.KSV011
   213  
   214  import data.lib.kubernetes
   215  import data.lib.utils
   216  
   217  default failLimitsCPU = false
   218  
   219  __rego_metadata__ := {
   220  	"id": "KSV011",
   221  	"avd_id": "AVD-KSV-0011",
   222  	"title": "CPU not limited",
   223  	"short_code": "limit-cpu",
   224  	"version": "v1.0.0",
   225  	"severity": "LOW",
   226  	"type": "Kubernetes Security Check",
   227  	"description": "Enforcing CPU limits prevents DoS via resource exhaustion.",
   228  	"recommended_actions": "Set a limit value under 'containers[].resources.limits.cpu'.",
   229  	"url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits",
   230  }
   231  
   232  __rego_input__ := {
   233  	"combine": false,
   234  	"selector": [{"type": "kubernetes"}],
   235  }
   236  
   237  # getLimitsCPUContainers returns all containers which have set resources.limits.cpu
   238  getLimitsCPUContainers[container] {
   239  	allContainers := kubernetes.containers[_]
   240  	utils.has_key(allContainers.resources.limits, "cpu")
   241  	container := allContainers.name
   242  }
   243  
   244  # getNoLimitsCPUContainers returns all containers which have not set
   245  # resources.limits.cpu
   246  getNoLimitsCPUContainers[container] {
   247  	container := kubernetes.containers[_].name
   248  	not getLimitsCPUContainers[container]
   249  }
   250  
   251  # failLimitsCPU is true if containers[].resources.limits.cpu is not set
   252  # for ANY container
   253  failLimitsCPU {
   254  	count(getNoLimitsCPUContainers) > 0
   255  }
   256  
   257  deny[res] {
   258  	failLimitsCPU
   259  
   260  	msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'resources.limits.cpu'", [getNoLimitsCPUContainers[_], kubernetes.kind, kubernetes.name]))
   261  
   262  	res := {
   263  		"msg": msg,
   264  		"id": __rego_metadata__.id,
   265  		"title": __rego_metadata__.title,
   266  		"severity": __rego_metadata__.severity,
   267  		"type": __rego_metadata__.type,
   268          "startline": 6,
   269          "endline": 10,
   270  	}
   271  }
   272  `,
   273  	})
   274  
   275  	scanner := NewScanner(options.ScannerWithPolicyDirs("rules"))
   276  
   277  	results, err := scanner.ScanFS(context.TODO(), fs, "code")
   278  	require.NoError(t, err)
   279  
   280  	require.Len(t, results.GetFailed(), 1)
   281  
   282  	assert.Equal(t, scan.Rule{
   283  		AVDID:          "AVD-KSV-0011",
   284  		Aliases:        []string{"KSV011"},
   285  		ShortCode:      "limit-cpu",
   286  		Summary:        "CPU not limited",
   287  		Explanation:    "Enforcing CPU limits prevents DoS via resource exhaustion.",
   288  		Impact:         "",
   289  		Resolution:     "Set a limit value under 'containers[].resources.limits.cpu'.",
   290  		Provider:       "kubernetes",
   291  		Service:        "general",
   292  		Links:          []string{"https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"},
   293  		Severity:       "LOW",
   294  		Terraform:      &scan.EngineMetadata{},
   295  		CloudFormation: &scan.EngineMetadata{},
   296  		CustomChecks:   scan.CustomChecks{Terraform: (*scan.TerraformCustomCheck)(nil)},
   297  		RegoPackage:    "data.builtin.kubernetes.KSV011",
   298  		Frameworks:     map[framework.Framework][]string{},
   299  	}, results.GetFailed()[0].Rule())
   300  
   301  	failure := results.GetFailed()[0]
   302  	actualCode, err := failure.GetCode()
   303  	require.NoError(t, err)
   304  	for i := range actualCode.Lines {
   305  		actualCode.Lines[i].Highlighted = ""
   306  	}
   307  	assert.Equal(t, []scan.Line{
   308  		{
   309  			Number:     6,
   310  			Content:    "spec: ",
   311  			IsCause:    true,
   312  			FirstCause: true,
   313  			Annotation: "",
   314  		},
   315  		{
   316  			Number:     7,
   317  			Content:    "  containers: ",
   318  			IsCause:    true,
   319  			Annotation: "",
   320  		},
   321  		{
   322  			Number:     8,
   323  			Content:    "  - command: [\"sh\", \"-c\", \"echo 'Hello' && sleep 1h\"]",
   324  			IsCause:    true,
   325  			Annotation: "",
   326  		},
   327  		{
   328  			Number:     9,
   329  			Content:    "    image: busybox",
   330  			IsCause:    true,
   331  			Annotation: "",
   332  		},
   333  		{
   334  			Number:     10,
   335  			Content:    "    name: hello",
   336  			IsCause:    true,
   337  			LastCause:  true,
   338  			Annotation: "",
   339  		},
   340  	}, actualCode.Lines)
   341  }
   342  
   343  func Test_FileScan(t *testing.T) {
   344  
   345  	results, err := NewScanner(options.ScannerWithEmbeddedPolicies(true), options.ScannerWithEmbeddedLibraries(true), options.ScannerWithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(`
   346  apiVersion: v1
   347  kind: Pod
   348  metadata: 
   349    name: hello-cpu-limit
   350  spec: 
   351    containers: 
   352    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   353      image: busybox
   354      name: hello
   355  `))
   356  	require.NoError(t, err)
   357  
   358  	assert.Greater(t, len(results.GetFailed()), 0)
   359  }
   360  
   361  func Test_FileScan_WithSeparator(t *testing.T) {
   362  
   363  	results, err := NewScanner(options.ScannerWithEmbeddedPolicies(true), options.ScannerWithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(`
   364  ---
   365  ---
   366  apiVersion: v1
   367  kind: Pod
   368  metadata: 
   369    name: hello-cpu-limit
   370  spec: 
   371    containers: 
   372    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   373      image: busybox
   374      name: hello
   375  `))
   376  	require.NoError(t, err)
   377  
   378  	assert.Greater(t, len(results.GetFailed()), 0)
   379  }
   380  
   381  func Test_FileScan_MultiManifests(t *testing.T) {
   382  	file := `
   383  ---
   384  apiVersion: v1
   385  kind: Pod
   386  metadata: 
   387    name: hello1-cpu-limit
   388  spec: 
   389    containers: 
   390    - command: ["sh", "-c", "echo 'Hello1' && sleep 1h"]
   391      image: busybox
   392      name: hello1
   393  ---
   394  apiVersion: v1
   395  kind: Pod
   396  metadata: 
   397    name: hello2-cpu-limit
   398  spec: 
   399    containers: 
   400    - command: ["sh", "-c", "echo 'Hello2' && sleep 1h"]
   401      image: busybox
   402      name: hello2
   403  `
   404  
   405  	results, err := NewScanner(
   406  		options.ScannerWithEmbeddedPolicies(true),
   407  		options.ScannerWithEmbeddedLibraries(true),
   408  		options.ScannerWithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(file))
   409  	require.NoError(t, err)
   410  
   411  	assert.Greater(t, len(results.GetFailed()), 1)
   412  	fileLines := strings.Split(file, "\n")
   413  	for _, failure := range results.GetFailed() {
   414  		actualCode, err := failure.GetCode()
   415  		require.NoError(t, err)
   416  		assert.Greater(t, len(actualCode.Lines), 0)
   417  		for _, line := range actualCode.Lines {
   418  			assert.Greater(t, len(fileLines), line.Number)
   419  			assert.Equal(t, line.Content, fileLines[line.Number-1])
   420  		}
   421  	}
   422  }
   423  
   424  func Test_FileScanWithPolicyReader(t *testing.T) {
   425  
   426  	results, err := NewScanner(options.ScannerWithPolicyReader(strings.NewReader(`package defsec
   427  
   428  deny[msg] {
   429    msg = "fail"
   430  }
   431  `))).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(`
   432  apiVersion: v1
   433  kind: Pod
   434  metadata: 
   435    name: hello-cpu-limit
   436  spec: 
   437    containers: 
   438    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   439      image: busybox
   440      name: hello
   441  `))
   442  	require.NoError(t, err)
   443  
   444  	assert.Equal(t, 1, len(results.GetFailed()))
   445  }
   446  
   447  func Test_FileScanJSON(t *testing.T) {
   448  
   449  	results, err := NewScanner(options.ScannerWithPolicyReader(strings.NewReader(`package defsec
   450  
   451  deny[msg] {
   452    input.kind == "Pod"
   453    msg = "fail"
   454  }
   455  `))).ScanReader(context.TODO(), "k8s.json", strings.NewReader(`
   456  {
   457    "kind": "Pod",
   458    "apiVersion": "v1",
   459    "metadata": {
   460      "name": "mongo",
   461      "labels": {
   462        "name": "mongo",
   463        "role": "mongo"
   464      }
   465    },
   466    "spec": {
   467      "volumes": [
   468        {
   469          "name": "mongo-disk",
   470          "gcePersistentDisk": {
   471            "pdName": "mongo-disk",
   472            "fsType": "ext4"
   473          }
   474        }
   475      ],
   476      "containers": [
   477        {
   478          "name": "mongo",
   479          "image": "mongo:latest",
   480          "ports": [
   481            {
   482              "name": "mongo",
   483              "containerPort": 27017
   484            }
   485          ],
   486          "volumeMounts": [
   487            {
   488              "name": "mongo-disk",
   489              "mountPath": "/data/db"
   490            }
   491          ]
   492        }
   493      ]
   494    }
   495  }
   496  `))
   497  	require.NoError(t, err)
   498  
   499  	assert.Equal(t, 1, len(results.GetFailed()))
   500  }
   501  
   502  func Test_FileScanWithMetadata(t *testing.T) {
   503  
   504  	results, err := NewScanner(
   505  		options.ScannerWithDebug(os.Stdout),
   506  		
   507  		options.ScannerWithPolicyReader(strings.NewReader(`package defsec
   508  
   509  deny[msg] {
   510    input.kind == "Pod"
   511    msg := {
   512            "msg": "fail",
   513            "startline": 2,
   514  		  "endline": 2,
   515            "filepath": "chartname/template/serviceAccount.yaml"
   516          }
   517  }
   518  `))).ScanReader(
   519  		context.TODO(),
   520  		"k8s.yaml",
   521  		strings.NewReader(`
   522  apiVersion: v1
   523  kind: Pod
   524  metadata: 
   525    name: hello-cpu-limit
   526  spec: 
   527    containers: 
   528    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   529      image: busybox
   530      name: hello
   531  `))
   532  	require.NoError(t, err)
   533  
   534  	assert.Greater(t, len(results.GetFailed()), 0)
   535  
   536  	firstResult := results.GetFailed()[0]
   537  	assert.Equal(t, 2, firstResult.Metadata().Range().GetStartLine())
   538  	assert.Equal(t, 2, firstResult.Metadata().Range().GetEndLine())
   539  	assert.Equal(t, "chartname/template/serviceAccount.yaml", firstResult.Metadata().Range().GetFilename())
   540  }
   541  
   542  func Test_FileScanExampleWithResultFunction(t *testing.T) {
   543  
   544  	results, err := NewScanner(
   545  		options.ScannerWithDebug(os.Stdout),
   546  		options.ScannerWithTrace(os.Stdout),
   547  		options.ScannerWithEmbeddedPolicies(true), options.ScannerWithEmbeddedLibraries(true),
   548  		options.ScannerWithPolicyReader(strings.NewReader(`package defsec
   549  
   550  import data.lib.kubernetes
   551  
   552  default checkCapsDropAll = false
   553  
   554  __rego_metadata__ := {
   555  "id": "KSV003",
   556  "avd_id": "AVD-KSV-0003",
   557  "title": "Default capabilities not dropped",
   558  "short_code": "drop-default-capabilities",
   559  "version": "v1.0.0",
   560  "severity": "LOW",
   561  "type": "Kubernetes Security Check",
   562  "description": "The container should drop all default capabilities and add only those that are needed for its execution.",
   563  "recommended_actions": "Add 'ALL' to containers[].securityContext.capabilities.drop.",
   564  "url": "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/",
   565  }
   566  
   567  __rego_input__ := {
   568  "combine": false,
   569  "selector": [{"type": "kubernetes"}],
   570  }
   571  
   572  # Get all containers which include 'ALL' in security.capabilities.drop
   573  getCapsDropAllContainers[container] {
   574  allContainers := kubernetes.containers[_]
   575  lower(allContainers.securityContext.capabilities.drop[_]) == "all"
   576  container := allContainers.name
   577  }
   578  
   579  # Get all containers which don't include 'ALL' in security.capabilities.drop
   580  getCapsNoDropAllContainers[container] {
   581  container := kubernetes.containers[_]
   582  not getCapsDropAllContainers[container.name]
   583  }
   584  
   585  deny[res] {
   586  output := getCapsNoDropAllContainers[_]
   587  
   588  msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should add 'ALL' to 'securityContext.capabilities.drop'", [output.name, kubernetes.kind, kubernetes.name]))
   589  
   590  res := result.new(msg, output)
   591  }
   592  
   593  `))).ScanReader(
   594  		context.TODO(),
   595  		"k8s.yaml",
   596  		strings.NewReader(`
   597  apiVersion: v1
   598  kind: Pod
   599  metadata: 
   600    name: hello-cpu-limit
   601  spec: 
   602    containers: 
   603    - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   604      image: busybox
   605      name: hello
   606      securityContext:
   607        capabilities:
   608          drop:
   609          - nothing
   610  `))
   611  	require.NoError(t, err)
   612  
   613  	require.Greater(t, len(results.GetFailed()), 0)
   614  
   615  	firstResult := results.GetFailed()[0]
   616  	assert.Equal(t, 8, firstResult.Metadata().Range().GetStartLine())
   617  	assert.Equal(t, 14, firstResult.Metadata().Range().GetEndLine())
   618  	assert.Equal(t, "k8s.yaml", firstResult.Metadata().Range().GetFilename())
   619  }
   620  
   621  /*
   622  // TODO(simar): Uncomment once all k8s policies have subtype selector added
   623  func Test_checkPolicyIsApplicable(t *testing.T) {
   624  	srcFS := testutil.CreateFS(t, map[string]string{
   625  		"policies/pod_policy.rego": `# METADATA
   626  # title: "Process can elevate its own privileges"
   627  # description: "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node."
   628  # scope: package
   629  # schemas:
   630  # - input: schema["kubernetes"]
   631  # related_resources:
   632  # - https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
   633  # custom:
   634  #   id: KSV001
   635  #   avd_id: AVD-KSV-0999
   636  #   severity: MEDIUM
   637  #   short_code: no-self-privesc
   638  #   recommended_action: "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'."
   639  #   input:
   640  #     selector:
   641  #     - type: kubernetes
   642  #       subtypes:
   643  #         - kind: Pod
   644  package builtin.kubernetes.KSV999
   645  
   646  import data.lib.kubernetes
   647  import data.lib.utils
   648  
   649  default checkAllowPrivilegeEscalation = false
   650  
   651  # getNoPrivilegeEscalationContainers returns the names of all containers which have
   652  # securityContext.allowPrivilegeEscalation set to false.
   653  getNoPrivilegeEscalationContainers[container] {
   654  	allContainers := kubernetes.containers[_]
   655  	allContainers.securityContext.allowPrivilegeEscalation == false
   656  	container := allContainers.name
   657  }
   658  
   659  # getPrivilegeEscalationContainers returns the names of all containers which have
   660  # securityContext.allowPrivilegeEscalation set to true or not set.
   661  getPrivilegeEscalationContainers[container] {
   662  	containerName := kubernetes.containers[_].name
   663  	not getNoPrivilegeEscalationContainers[containerName]
   664  	container := kubernetes.containers[_]
   665  }
   666  
   667  deny[res] {
   668  	output := getPrivilegeEscalationContainers[_]
   669  	msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'securityContext.allowPrivilegeEscalation' to false", [output.name, kubernetes.kind, kubernetes.name]))
   670  	res := result.new(msg, output)
   671  }
   672  
   673  `,
   674  		"policies/namespace_policy.rego": `# METADATA
   675  # title: "The default namespace should not be used"
   676  # description: "ensure that default namespace should not be used"
   677  # scope: package
   678  # schemas:
   679  # - input: schema["kubernetes"]
   680  # related_resources:
   681  # - https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
   682  # custom:
   683  #   id: KSV110
   684  #   avd_id: AVD-KSV-0888
   685  #   severity: LOW
   686  #   short_code: default-namespace-should-not-be-used
   687  #   recommended_action: "Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace."
   688  #   input:
   689  #     selector:
   690  #     - type: kubernetes
   691  #       subtypes:
   692  #         - kind: Namespace
   693  package builtin.kubernetes.KSV888
   694  
   695  import data.lib.kubernetes
   696  
   697  default defaultNamespaceInUse = false
   698  
   699  defaultNamespaceInUse {
   700  	kubernetes.namespace == "default"
   701  }
   702  
   703  deny[res] {
   704  	defaultNamespaceInUse
   705  	msg := sprintf("%s '%s' should not be set with 'default' namespace", [kubernetes.kind, kubernetes.name])
   706  	res := result.new(msg, input.metadata.namespace)
   707  }
   708  
   709  `,
   710  		"test/KSV001/pod.yaml": `apiVersion: v1
   711  kind: Pod
   712  metadata:
   713    name: hello-cpu-limit
   714  spec:
   715    containers:
   716      - command: ["sh", "-c", "echo 'Hello' && sleep 1h"]
   717        image: busybox
   718        name: hello
   719        securityContext:
   720          capabilities:
   721            drop:
   722              - all
   723  `,
   724  	})
   725  
   726  	scanner := NewScanner(
   727  		//options.ScannerWithEmbeddedPolicies(true), options.ScannerWithEmbeddedLibraries(true),
   728  		options.ScannerWithEmbeddedLibraries(true),
   729  		options.ScannerWithPolicyDirs("policies/"),
   730  		options.ScannerWithPolicyFilesystem(srcFS),
   731  	)
   732  	results, err := scanner.ScanFS(context.TODO(), srcFS, "test/KSV001")
   733  	require.NoError(t, err)
   734  
   735  	require.NoError(t, err)
   736  	require.Len(t, results.GetFailed(), 1)
   737  
   738  	failure := results.GetFailed()[0].Rule()
   739  	assert.Equal(t, "Process can elevate its own privileges", failure.Summary)
   740  }
   741  */