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

     1  package rego
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/khulnasoft-lab/defsec/pkg/types"
    11  
    12  	"github.com/khulnasoft-lab/defsec/pkg/scanners/options"
    13  
    14  	"github.com/khulnasoft-lab/defsec/pkg/severity"
    15  
    16  	"github.com/khulnasoft-lab/defsec/test/testutil"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func Test_RegoScanning_Deny(t *testing.T) {
    24  
    25  	srcFS := testutil.CreateFS(t, map[string]string{
    26  		"policies/test.rego": `
    27  package defsec.test
    28  
    29  deny {
    30      input.evil
    31  }
    32  `,
    33  	})
    34  
    35  	scanner := NewScanner(types.SourceJSON)
    36  	require.NoError(
    37  		t,
    38  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
    39  	)
    40  
    41  	results, err := scanner.ScanInput(context.TODO(), Input{
    42  		Path: "/evil.lol",
    43  		Contents: map[string]interface{}{
    44  			"evil": true,
    45  		},
    46  		FS: srcFS,
    47  	})
    48  	require.NoError(t, err)
    49  
    50  	require.Equal(t, 1, len(results.GetFailed()))
    51  	assert.Equal(t, 0, len(results.GetPassed()))
    52  	assert.Equal(t, 0, len(results.GetIgnored()))
    53  
    54  	assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename())
    55  	assert.False(t, results.GetFailed()[0].IsWarning())
    56  }
    57  
    58  func Test_RegoScanning_AbsolutePolicyPath_Deny(t *testing.T) {
    59  
    60  	tmp := t.TempDir()
    61  	require.NoError(t, os.Mkdir(filepath.Join(tmp, "policies"), 0755))
    62  	require.NoError(t, os.WriteFile(filepath.Join(tmp, "policies", "test.rego"), []byte(`package defsec.test
    63  
    64  deny {
    65      input.evil
    66  }`), 0600))
    67  
    68  	srcFS := os.DirFS(tmp)
    69  
    70  	scanner := NewScanner(types.SourceJSON)
    71  	require.NoError(
    72  		t,
    73  		scanner.LoadPolicies(false, false, srcFS, []string{"/policies"}, nil),
    74  	)
    75  
    76  	results, err := scanner.ScanInput(context.TODO(), Input{
    77  		Path: "/evil.lol",
    78  		Contents: map[string]interface{}{
    79  			"evil": true,
    80  		},
    81  		FS: srcFS,
    82  	})
    83  	require.NoError(t, err)
    84  
    85  	require.Equal(t, 1, len(results.GetFailed()))
    86  	assert.Equal(t, 0, len(results.GetPassed()))
    87  	assert.Equal(t, 0, len(results.GetIgnored()))
    88  
    89  	assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename())
    90  	assert.False(t, results.GetFailed()[0].IsWarning())
    91  }
    92  
    93  func Test_RegoScanning_Warn(t *testing.T) {
    94  
    95  	srcFS := testutil.CreateFS(t, map[string]string{
    96  		"policies/test.rego": `
    97  package defsec.test
    98  
    99  warn {
   100      input.evil
   101  }
   102  `,
   103  	})
   104  
   105  	scanner := NewScanner(types.SourceJSON)
   106  	require.NoError(
   107  		t,
   108  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   109  	)
   110  
   111  	results, err := scanner.ScanInput(context.TODO(), Input{
   112  		Path: "/evil.lol",
   113  		Contents: map[string]interface{}{
   114  			"evil": true,
   115  		},
   116  	})
   117  	require.NoError(t, err)
   118  
   119  	require.Equal(t, 1, len(results.GetFailed()))
   120  	require.Equal(t, 0, len(results.GetPassed()))
   121  	require.Equal(t, 0, len(results.GetIgnored()))
   122  
   123  	assert.True(t, results.GetFailed()[0].IsWarning())
   124  }
   125  
   126  func Test_RegoScanning_Allow(t *testing.T) {
   127  	srcFS := testutil.CreateFS(t, map[string]string{
   128  		"policies/test.rego": `
   129  package defsec.test
   130  
   131  deny {
   132      input.evil
   133  }
   134  `,
   135  	})
   136  
   137  	scanner := NewScanner(types.SourceJSON)
   138  	require.NoError(
   139  		t,
   140  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   141  	)
   142  
   143  	results, err := scanner.ScanInput(context.TODO(), Input{
   144  		Path: "/evil.lol",
   145  		Contents: map[string]interface{}{
   146  			"evil": false,
   147  		},
   148  	})
   149  	require.NoError(t, err)
   150  
   151  	assert.Equal(t, 0, len(results.GetFailed()))
   152  	require.Equal(t, 1, len(results.GetPassed()))
   153  	assert.Equal(t, 0, len(results.GetIgnored()))
   154  
   155  	assert.Equal(t, "/evil.lol", results.GetPassed()[0].Metadata().Range().GetFilename())
   156  }
   157  
   158  func Test_RegoScanning_Namespace_Exception(t *testing.T) {
   159  
   160  	srcFS := testutil.CreateFS(t, map[string]string{
   161  		"policies/test.rego": `
   162  package defsec.test
   163  
   164  deny {
   165      input.evil
   166  }
   167  `,
   168  		"policies/exceptions.rego": `
   169  package namespace.exceptions
   170  
   171  import data.namespaces
   172  
   173  exception[ns] {
   174      ns := data.namespaces[_]
   175      startswith(ns, "defsec")
   176  }
   177  `,
   178  	})
   179  
   180  	scanner := NewScanner(types.SourceJSON)
   181  	require.NoError(
   182  		t,
   183  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   184  	)
   185  
   186  	results, err := scanner.ScanInput(context.TODO(), Input{
   187  		Path: "/evil.lol",
   188  		Contents: map[string]interface{}{
   189  			"evil": true,
   190  		},
   191  	})
   192  	require.NoError(t, err)
   193  
   194  	assert.Equal(t, 0, len(results.GetFailed()))
   195  	assert.Equal(t, 0, len(results.GetPassed()))
   196  	assert.Equal(t, 1, len(results.GetIgnored()))
   197  
   198  }
   199  
   200  func Test_RegoScanning_Namespace_Exception_WithoutMatch(t *testing.T) {
   201  
   202  	srcFS := testutil.CreateFS(t, map[string]string{
   203  		"policies/test.rego": `
   204  package defsec.test
   205  
   206  deny {
   207      input.evil
   208  }
   209  `, "policies/something.rego": `
   210  package builtin.test
   211  
   212  deny_something {
   213      input.something
   214  }
   215  `,
   216  		"policies/exceptions.rego": `
   217  package namespace.exceptions
   218  
   219  import data.namespaces
   220  
   221  exception[ns] {
   222      ns := data.namespaces[_]
   223      startswith(ns, "builtin")
   224  }
   225  `,
   226  	})
   227  
   228  	scanner := NewScanner(types.SourceJSON)
   229  	require.NoError(
   230  		t,
   231  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   232  	)
   233  
   234  	results, err := scanner.ScanInput(context.TODO(), Input{
   235  		Path: "/evil.lol",
   236  		Contents: map[string]interface{}{
   237  			"evil": true,
   238  		},
   239  	})
   240  	require.NoError(t, err)
   241  
   242  	assert.Equal(t, 1, len(results.GetFailed()))
   243  	assert.Equal(t, 0, len(results.GetPassed()))
   244  	assert.Equal(t, 1, len(results.GetIgnored()))
   245  
   246  }
   247  
   248  func Test_RegoScanning_Rule_Exception(t *testing.T) {
   249  	srcFS := testutil.CreateFS(t, map[string]string{
   250  		"policies/test.rego": `
   251  package defsec.test
   252  deny_evil {
   253      input.evil
   254  }
   255  `,
   256  		"policies/exceptions.rego": `
   257  package defsec.test
   258  
   259  exception[rules] {
   260      rules := ["evil"]
   261  }
   262  `,
   263  	})
   264  
   265  	scanner := NewScanner(types.SourceJSON)
   266  	require.NoError(
   267  		t,
   268  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   269  	)
   270  
   271  	results, err := scanner.ScanInput(context.TODO(), Input{
   272  		Path: "/evil.lol",
   273  		Contents: map[string]interface{}{
   274  			"evil": true,
   275  		},
   276  	})
   277  	require.NoError(t, err)
   278  
   279  	assert.Equal(t, 0, len(results.GetFailed()))
   280  	assert.Equal(t, 0, len(results.GetPassed()))
   281  	assert.Equal(t, 1, len(results.GetIgnored()))
   282  }
   283  
   284  func Test_RegoScanning_Rule_Exception_WithoutMatch(t *testing.T) {
   285  	srcFS := testutil.CreateFS(t, map[string]string{
   286  		"policies/test.rego": `
   287  package defsec.test
   288  deny_evil {
   289      input.evil
   290  }
   291  `,
   292  		"policies/exceptions.rego": `
   293  package defsec.test
   294  
   295  exception[rules] {
   296      rules := ["good"]
   297  }
   298  `,
   299  	})
   300  
   301  	scanner := NewScanner(types.SourceJSON)
   302  	require.NoError(
   303  		t,
   304  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   305  	)
   306  
   307  	results, err := scanner.ScanInput(context.TODO(), Input{
   308  		Path: "/evil.lol",
   309  		Contents: map[string]interface{}{
   310  			"evil": true,
   311  		},
   312  	})
   313  	require.NoError(t, err)
   314  
   315  	assert.Equal(t, 1, len(results.GetFailed()))
   316  	assert.Equal(t, 0, len(results.GetPassed()))
   317  	assert.Equal(t, 0, len(results.GetIgnored()))
   318  }
   319  
   320  func Test_RegoScanning_WithRuntimeValues(t *testing.T) {
   321  
   322  	_ = os.Setenv("DEFSEC_RUNTIME_VAL", "AOK")
   323  
   324  	srcFS := testutil.CreateFS(t, map[string]string{
   325  		"policies/test.rego": `
   326  package defsec.test
   327  
   328  deny_evil {
   329      output := opa.runtime()
   330  	output.env.DEFSEC_RUNTIME_VAL == "AOK"
   331  }
   332  `,
   333  	})
   334  
   335  	scanner := NewScanner(types.SourceJSON)
   336  	require.NoError(
   337  		t,
   338  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   339  	)
   340  
   341  	results, err := scanner.ScanInput(context.TODO(), Input{
   342  		Path: "/evil.lol",
   343  		Contents: map[string]interface{}{
   344  			"evil": true,
   345  		},
   346  	})
   347  	require.NoError(t, err)
   348  
   349  	assert.Equal(t, 1, len(results.GetFailed()))
   350  	assert.Equal(t, 0, len(results.GetPassed()))
   351  	assert.Equal(t, 0, len(results.GetIgnored()))
   352  }
   353  
   354  func Test_RegoScanning_WithDenyMessage(t *testing.T) {
   355  	srcFS := testutil.CreateFS(t, map[string]string{
   356  		"policies/test.rego": `
   357  package defsec.test
   358  
   359  deny[msg] {
   360      input.evil
   361  	msg := "oh no"
   362  }
   363  `,
   364  	})
   365  
   366  	scanner := NewScanner(types.SourceJSON)
   367  	require.NoError(
   368  		t,
   369  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   370  	)
   371  
   372  	results, err := scanner.ScanInput(context.TODO(), Input{
   373  		Path: "/evil.lol",
   374  		Contents: map[string]interface{}{
   375  			"evil": true,
   376  		},
   377  	})
   378  	require.NoError(t, err)
   379  
   380  	require.Equal(t, 1, len(results.GetFailed()))
   381  	assert.Equal(t, 0, len(results.GetPassed()))
   382  	assert.Equal(t, 0, len(results.GetIgnored()))
   383  
   384  	assert.Equal(t, "oh no", results.GetFailed()[0].Description())
   385  	assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename())
   386  }
   387  
   388  func Test_RegoScanning_WithDenyMetadata_ImpliedPath(t *testing.T) {
   389  	srcFS := testutil.CreateFS(t, map[string]string{
   390  		"policies/test.rego": `
   391  package defsec.test
   392  
   393  deny[res] {
   394      input.evil
   395  	res := {
   396  		"msg": "oh no",
   397  		"startline": 123,
   398  		"endline": 456,
   399  	}
   400  }
   401  `,
   402  	})
   403  
   404  	scanner := NewScanner(types.SourceJSON)
   405  	require.NoError(
   406  		t,
   407  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   408  	)
   409  
   410  	results, err := scanner.ScanInput(context.TODO(), Input{
   411  		Path: "/evil.lol",
   412  		Contents: map[string]interface{}{
   413  			"evil": true,
   414  		},
   415  	})
   416  	require.NoError(t, err)
   417  
   418  	require.Equal(t, 1, len(results.GetFailed()))
   419  	assert.Equal(t, 0, len(results.GetPassed()))
   420  	assert.Equal(t, 0, len(results.GetIgnored()))
   421  
   422  	assert.Equal(t, "oh no", results.GetFailed()[0].Description())
   423  	assert.Equal(t, "/evil.lol", results.GetFailed()[0].Metadata().Range().GetFilename())
   424  	assert.Equal(t, 123, results.GetFailed()[0].Metadata().Range().GetStartLine())
   425  	assert.Equal(t, 456, results.GetFailed()[0].Metadata().Range().GetEndLine())
   426  
   427  }
   428  
   429  func Test_RegoScanning_WithDenyMetadata_PersistedPath(t *testing.T) {
   430  	srcFS := testutil.CreateFS(t, map[string]string{
   431  		"policies/test.rego": `
   432  package defsec.test
   433  
   434  deny[res] {
   435      input.evil
   436  	res := {
   437  		"msg": "oh no",
   438  		"startline": 123,
   439  		"endline": 456,
   440  		"filepath": "/blah.txt",
   441  	}
   442  }
   443  `,
   444  	})
   445  
   446  	scanner := NewScanner(types.SourceJSON)
   447  	require.NoError(
   448  		t,
   449  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   450  	)
   451  
   452  	results, err := scanner.ScanInput(context.TODO(), Input{
   453  		Path: "/evil.lol",
   454  		Contents: map[string]interface{}{
   455  			"evil": true,
   456  		},
   457  	})
   458  	require.NoError(t, err)
   459  
   460  	require.Equal(t, 1, len(results.GetFailed()))
   461  	assert.Equal(t, 0, len(results.GetPassed()))
   462  	assert.Equal(t, 0, len(results.GetIgnored()))
   463  
   464  	assert.Equal(t, "oh no", results.GetFailed()[0].Description())
   465  	assert.Equal(t, "/blah.txt", results.GetFailed()[0].Metadata().Range().GetFilename())
   466  	assert.Equal(t, 123, results.GetFailed()[0].Metadata().Range().GetStartLine())
   467  	assert.Equal(t, 456, results.GetFailed()[0].Metadata().Range().GetEndLine())
   468  
   469  }
   470  
   471  func Test_RegoScanning_WithStaticMetadata(t *testing.T) {
   472  	srcFS := testutil.CreateFS(t, map[string]string{
   473  		"policies/test.rego": `
   474  package defsec.test
   475  
   476  __rego_metadata__ := {
   477  	"id": "AA001",
   478  	"avd_id": "AVD-XX-9999",
   479  	"title": "This is a title",
   480  	"short_code": "short-code",
   481  	"severity": "LOW",
   482  	"type": "Dockerfile Security Check",
   483  	"description": "This is a description",
   484  	"recommended_actions": "This is a recommendation",
   485  	"url": "https://google.com",
   486  }
   487  
   488  deny[res] {
   489      input.evil
   490  	res := {
   491  		"msg": "oh no",
   492  		"startline": 123,
   493  		"endline": 456,
   494  		"filepath": "/blah.txt",
   495  	}
   496  }
   497  `,
   498  	})
   499  
   500  	scanner := NewScanner(types.SourceJSON)
   501  	require.NoError(
   502  		t,
   503  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   504  	)
   505  
   506  	results, err := scanner.ScanInput(context.TODO(), Input{
   507  		Path: "/evil.lol",
   508  		Contents: map[string]interface{}{
   509  			"evil": true,
   510  		},
   511  	})
   512  	require.NoError(t, err)
   513  
   514  	require.Equal(t, 1, len(results.GetFailed()))
   515  	assert.Equal(t, 0, len(results.GetPassed()))
   516  	assert.Equal(t, 0, len(results.GetIgnored()))
   517  
   518  	failure := results.GetFailed()[0]
   519  
   520  	assert.Equal(t, "oh no", failure.Description())
   521  	assert.Equal(t, "/blah.txt", failure.Metadata().Range().GetFilename())
   522  	assert.Equal(t, 123, failure.Metadata().Range().GetStartLine())
   523  	assert.Equal(t, 456, failure.Metadata().Range().GetEndLine())
   524  	assert.Equal(t, "AVD-XX-9999", failure.Rule().AVDID)
   525  	assert.True(t, failure.Rule().HasID("AA001"))
   526  	assert.Equal(t, "This is a title", failure.Rule().Summary)
   527  	assert.Equal(t, severity.Low, failure.Rule().Severity)
   528  	assert.Equal(t, "This is a recommendation", failure.Rule().Resolution)
   529  	assert.Equal(t, "https://google.com", failure.Rule().Links[0])
   530  
   531  }
   532  
   533  func Test_RegoScanning_WithMatchingInputSelector(t *testing.T) {
   534  	srcFS := testutil.CreateFS(t, map[string]string{
   535  		"policies/test.rego": `
   536  package defsec.test
   537  
   538  __rego_input__ := {
   539  	"selector": [{"type": "json"}],
   540  }
   541  
   542  deny {
   543      input.evil
   544  }
   545  
   546  `,
   547  	})
   548  
   549  	scanner := NewScanner(types.SourceJSON)
   550  	require.NoError(
   551  		t,
   552  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   553  	)
   554  
   555  	results, err := scanner.ScanInput(context.TODO(), Input{
   556  		Path: "/evil.lol",
   557  		Contents: map[string]interface{}{
   558  			"evil": true,
   559  		},
   560  	})
   561  	require.NoError(t, err)
   562  
   563  	assert.Equal(t, 1, len(results.GetFailed()))
   564  	assert.Equal(t, 0, len(results.GetPassed()))
   565  	assert.Equal(t, 0, len(results.GetIgnored()))
   566  }
   567  
   568  func Test_RegoScanning_WithNonMatchingInputSelector(t *testing.T) {
   569  	srcFS := testutil.CreateFS(t, map[string]string{
   570  		"policies/test.rego": `
   571  package defsec.test
   572  
   573  __rego_input__ := {
   574  	"selector": [{"type": "testing"}],
   575  }
   576  
   577  deny {
   578      input.evil
   579  }
   580  `,
   581  	})
   582  
   583  	scanner := NewScanner(types.SourceJSON)
   584  	require.NoError(
   585  		t,
   586  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   587  	)
   588  
   589  	results, err := scanner.ScanInput(context.TODO(), Input{
   590  		Path: "/evil.lol",
   591  		Contents: map[string]interface{}{
   592  			"evil": true,
   593  		},
   594  	})
   595  	require.NoError(t, err)
   596  
   597  	assert.Equal(t, 0, len(results.GetFailed()))
   598  	assert.Equal(t, 0, len(results.GetPassed()))
   599  	assert.Equal(t, 0, len(results.GetIgnored()))
   600  }
   601  
   602  func Test_RegoScanning_NoTracingByDefault(t *testing.T) {
   603  
   604  	srcFS := testutil.CreateFS(t, map[string]string{
   605  		"policies/test.rego": `
   606  package defsec.test
   607  
   608  deny {
   609      input.evil
   610  }
   611  `,
   612  	})
   613  
   614  	scanner := NewScanner(types.SourceJSON)
   615  	require.NoError(
   616  		t,
   617  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   618  	)
   619  
   620  	results, err := scanner.ScanInput(context.TODO(), Input{
   621  		Path: "/evil.lol",
   622  		Contents: map[string]interface{}{
   623  			"evil": true,
   624  		},
   625  	})
   626  	require.NoError(t, err)
   627  
   628  	assert.Equal(t, 1, len(results.GetFailed()))
   629  	assert.Equal(t, 0, len(results.GetPassed()))
   630  	assert.Equal(t, 0, len(results.GetIgnored()))
   631  
   632  	assert.Len(t, results.GetFailed()[0].Traces(), 0)
   633  }
   634  
   635  func Test_RegoScanning_GlobalTracingEnabled(t *testing.T) {
   636  
   637  	srcFS := testutil.CreateFS(t, map[string]string{
   638  		"policies/test.rego": `
   639  package defsec.test
   640  
   641  deny {
   642      input.evil
   643  }
   644  `,
   645  	})
   646  
   647  	traceBuffer := bytes.NewBuffer([]byte{})
   648  
   649  	scanner := NewScanner(types.SourceJSON, options.ScannerWithTrace(traceBuffer))
   650  	require.NoError(
   651  		t,
   652  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   653  	)
   654  
   655  	results, err := scanner.ScanInput(context.TODO(), Input{
   656  		Path: "/evil.lol",
   657  		Contents: map[string]interface{}{
   658  			"evil": true,
   659  		},
   660  	})
   661  	require.NoError(t, err)
   662  
   663  	assert.Equal(t, 1, len(results.GetFailed()))
   664  	assert.Equal(t, 0, len(results.GetPassed()))
   665  	assert.Equal(t, 0, len(results.GetIgnored()))
   666  
   667  	assert.Len(t, results.GetFailed()[0].Traces(), 0)
   668  	assert.Greater(t, len(traceBuffer.Bytes()), 0)
   669  }
   670  
   671  func Test_RegoScanning_PerResultTracingEnabled(t *testing.T) {
   672  
   673  	srcFS := testutil.CreateFS(t, map[string]string{
   674  		"policies/test.rego": `
   675  package defsec.test
   676  
   677  deny {
   678      input.evil
   679  }
   680  `,
   681  	})
   682  
   683  	scanner := NewScanner(types.SourceJSON, options.ScannerWithPerResultTracing(true))
   684  	require.NoError(
   685  		t,
   686  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   687  	)
   688  
   689  	results, err := scanner.ScanInput(context.TODO(), Input{
   690  		Path: "/evil.lol",
   691  		Contents: map[string]interface{}{
   692  			"evil": true,
   693  		},
   694  	})
   695  	require.NoError(t, err)
   696  
   697  	assert.Equal(t, 1, len(results.GetFailed()))
   698  	assert.Equal(t, 0, len(results.GetPassed()))
   699  	assert.Equal(t, 0, len(results.GetIgnored()))
   700  
   701  	assert.Greater(t, len(results.GetFailed()[0].Traces()), 0)
   702  }
   703  
   704  func Test_dynamicMetadata(t *testing.T) {
   705  
   706  	srcFS := testutil.CreateFS(t, map[string]string{
   707  		"policies/test.rego": `
   708  package defsec.test
   709  
   710  __rego_metadata__ := {
   711    "title" : sprintf("i am %s",[input.text])
   712  }
   713  
   714  deny {
   715    input.text
   716  }
   717  
   718  `,
   719  	})
   720  
   721  	scanner := NewScanner(types.SourceJSON)
   722  	require.NoError(
   723  		t,
   724  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   725  	)
   726  
   727  	results, err := scanner.ScanInput(context.TODO(), Input{
   728  		Path: "/evil.lol",
   729  		Contents: map[string]interface{}{
   730  			"text": "dynamic",
   731  		},
   732  	})
   733  	require.NoError(t, err)
   734  	assert.Equal(t, results[0].Rule().Summary, "i am dynamic")
   735  }
   736  
   737  func Test_staticMetadata(t *testing.T) {
   738  
   739  	srcFS := testutil.CreateFS(t, map[string]string{
   740  		"policies/test.rego": `
   741  package defsec.test
   742  
   743  __rego_metadata__ := {
   744    "title" : "i am static"
   745  }
   746  
   747  deny {
   748    input.text
   749  }
   750  
   751  `,
   752  	})
   753  
   754  	scanner := NewScanner(types.SourceJSON)
   755  	require.NoError(
   756  		t,
   757  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   758  	)
   759  
   760  	results, err := scanner.ScanInput(context.TODO(), Input{
   761  		Path: "/evil.lol",
   762  		Contents: map[string]interface{}{
   763  			"text": "test",
   764  		},
   765  	})
   766  	require.NoError(t, err)
   767  	assert.Equal(t, results[0].Rule().Summary, "i am static")
   768  }
   769  
   770  func Test_annotationMetadata(t *testing.T) {
   771  
   772  	srcFS := testutil.CreateFS(t, map[string]string{
   773  		"policies/test.rego": `# METADATA
   774  # title: i am a title
   775  # description: i am a description
   776  # related_resources:
   777  # - https://google.com
   778  # custom:
   779  #   id: EG123
   780  #   avd_id: AVD-EG-0123
   781  #   severity: LOW
   782  #   recommended_action: have a cup of tea
   783  package defsec.test
   784  
   785  deny {
   786    input.text
   787  }
   788  
   789  `,
   790  		"policies/test2.rego": `# METADATA
   791  # title: i am another title
   792  package defsec.test2
   793  
   794  deny {
   795    input.blah
   796  }
   797  
   798  `,
   799  	})
   800  
   801  	scanner := NewScanner(types.SourceJSON)
   802  	require.NoError(
   803  		t,
   804  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   805  	)
   806  
   807  	results, err := scanner.ScanInput(context.TODO(), Input{
   808  		Path: "/evil.lol",
   809  		Contents: map[string]interface{}{
   810  			"text": "test",
   811  		},
   812  	})
   813  	require.NoError(t, err)
   814  	require.Len(t, results.GetFailed(), 1)
   815  	failure := results.GetFailed()[0].Rule()
   816  	assert.Equal(t, "i am a title", failure.Summary)
   817  	assert.Equal(t, "i am a description", failure.Explanation)
   818  	require.Len(t, failure.Links, 1)
   819  	assert.Equal(t, "https://google.com", failure.Links[0])
   820  	assert.Equal(t, "AVD-EG-0123", failure.AVDID)
   821  	assert.Equal(t, severity.Low, failure.Severity)
   822  	assert.Equal(t, "have a cup of tea", failure.Resolution)
   823  }
   824  
   825  func Test_RegoScanning_WithInvalidInputSchema(t *testing.T) {
   826  
   827  	srcFS := testutil.CreateFS(t, map[string]string{
   828  		"policies/test.rego": `# METADATA
   829  # schemas:
   830  # - input: schema["input"]
   831  package defsec.test
   832  
   833  deny {
   834      input.evil == "lol"
   835  }
   836  `,
   837  	})
   838  
   839  	scanner := NewScanner(types.SourceDockerfile)
   840  	scanner.SetRegoErrorLimit(0) // override to not allow any errors
   841  	assert.ErrorContains(
   842  		t,
   843  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   844  		"undefined ref: input.evil",
   845  	)
   846  }
   847  
   848  func Test_RegoScanning_WithValidInputSchema(t *testing.T) {
   849  
   850  	srcFS := testutil.CreateFS(t, map[string]string{
   851  		"policies/test.rego": `# METADATA
   852  # schemas:
   853  # - input: schema["input"]
   854  package defsec.test
   855  
   856  deny {
   857      input.Stages[0].Commands[0].Cmd == "lol"
   858  }
   859  `,
   860  	})
   861  
   862  	scanner := NewScanner(types.SourceDockerfile)
   863  	assert.NoError(
   864  		t,
   865  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   866  	)
   867  }
   868  
   869  func Test_RegoScanning_WithFilepathToSchema(t *testing.T) {
   870  	srcFS := testutil.CreateFS(t, map[string]string{
   871  		"policies/test.rego": `# METADATA
   872  # schemas:
   873  # - input: schema["dockerfile"]
   874  package defsec.test
   875  
   876  deny {
   877      input.evil == "lol"
   878  }
   879  `,
   880  	})
   881  	scanner := NewScanner(types.SourceJSON)
   882  	scanner.SetRegoErrorLimit(0) // override to not allow any errors
   883  	assert.ErrorContains(
   884  		t,
   885  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   886  		"undefined ref: input.evil",
   887  	)
   888  }
   889  
   890  func Test_RegoScanning_CustomData(t *testing.T) {
   891  	srcFS := testutil.CreateFS(t, map[string]string{
   892  		"policies/test.rego": `
   893  package defsec.test
   894  import data.settings.DS123.foo_bar_baz
   895  
   896  deny {
   897      not foo_bar_baz
   898  }
   899  `,
   900  	})
   901  
   902  	dataFS := testutil.CreateFS(t, map[string]string{
   903  		"data/data.json": `{
   904  	"settings": {
   905  		"DS123":{
   906  			"foo_bar_baz":false
   907  		}
   908  	}
   909  }`,
   910  		"data/junk.txt": "this file should be ignored",
   911  	})
   912  
   913  	scanner := NewScanner(types.SourceJSON)
   914  	scanner.SetDataFilesystem(dataFS)
   915  	scanner.SetDataDirs(".")
   916  
   917  	require.NoError(
   918  		t,
   919  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   920  	)
   921  
   922  	results, err := scanner.ScanInput(context.TODO(), Input{})
   923  	require.NoError(t, err)
   924  
   925  	assert.Equal(t, 1, len(results.GetFailed()))
   926  	assert.Equal(t, 0, len(results.GetPassed()))
   927  	assert.Equal(t, 0, len(results.GetIgnored()))
   928  }
   929  
   930  func Test_RegoScanning_InvalidFS(t *testing.T) {
   931  	srcFS := testutil.CreateFS(t, map[string]string{
   932  		"policies/test.rego": `
   933  package defsec.test
   934  import data.settings.DS123.foo_bar_baz
   935  
   936  deny {
   937      not foo_bar_baz
   938  }
   939  `,
   940  	})
   941  
   942  	dataFS := testutil.CreateFS(t, map[string]string{
   943  		"data/data.json": `{
   944  	"settings": {
   945  		"DS123":{
   946  			"foo_bar_baz":false
   947  		}
   948  	}
   949  }`,
   950  		"data/junk.txt": "this file should be ignored",
   951  	})
   952  
   953  	scanner := NewScanner(types.SourceJSON)
   954  	scanner.SetDataFilesystem(dataFS)
   955  	scanner.SetDataDirs("X://")
   956  
   957  	require.NoError(
   958  		t,
   959  		scanner.LoadPolicies(false, false, srcFS, []string{"policies"}, nil),
   960  	)
   961  
   962  	results, err := scanner.ScanInput(context.TODO(), Input{})
   963  	require.NoError(t, err)
   964  
   965  	assert.Equal(t, 1, len(results.GetFailed()))
   966  	assert.Equal(t, 0, len(results.GetPassed()))
   967  	assert.Equal(t, 0, len(results.GetIgnored()))
   968  }