github.com/avenga/couper@v1.12.2/config/configload/validate_test.go (about)

     1  package configload
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/hcl/v2/hclsyntax"
     9  
    10  	"github.com/avenga/couper/cache"
    11  	"github.com/avenga/couper/config/runtime"
    12  	"github.com/avenga/couper/errors"
    13  	logrustest "github.com/sirupsen/logrus/hooks/test"
    14  )
    15  
    16  func Test_VerifyBodyAttributes(t *testing.T) {
    17  	type testCase struct {
    18  		name   string
    19  		body   *hclsyntax.Body
    20  		expErr bool
    21  	}
    22  
    23  	for _, tc := range []testCase{
    24  		{"without any body attributes", &hclsyntax.Body{}, false},
    25  		{"body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"body": {Name: "body"}}}, false},
    26  		{"json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"json_body": {Name: "json_body"}}}, false},
    27  		{"form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{"form_body": {Name: "form_body"}}}, false},
    28  		{"body/form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
    29  			"body":      {Name: "body"},
    30  			"form_body": {Name: "form_body"},
    31  		}}, true},
    32  		{"body/json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
    33  			"body":      {Name: "body"},
    34  			"json_body": {Name: "json_body"},
    35  		}}, true},
    36  		{"form_body/json_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
    37  			"form_body": {Name: "form_body"},
    38  			"json_body": {Name: "json_body"},
    39  		}}, true},
    40  		{"body/json_body/form_body", &hclsyntax.Body{Attributes: map[string]*hclsyntax.Attribute{
    41  			"body":      {Name: "body"},
    42  			"json_body": {Name: "json_body"},
    43  			"form_body": {Name: "form_body"},
    44  		}}, true},
    45  	} {
    46  		if err := verifyBodyAttributes(request, tc.body); !tc.expErr && err != nil {
    47  			t.Errorf("Want no error, got: %v", err)
    48  		}
    49  	}
    50  }
    51  
    52  func TestLabels(t *testing.T) {
    53  	tests := []struct {
    54  		name  string
    55  		hcl   string
    56  		error string
    57  	}{
    58  		{
    59  			"missing server",
    60  			`definitions {}`,
    61  			"configuration error: missing 'server' block",
    62  		},
    63  		{
    64  			"server w/o label",
    65  			`server {}`,
    66  			"",
    67  		},
    68  		{
    69  			"multiple servers w/o label",
    70  			`server {
    71  			   hosts = ["*:8888"]
    72  			 }
    73  			 server {
    74  			   hosts = ["*:9999"]
    75  			 }`,
    76  			"",
    77  		},
    78  		{
    79  			"labelled and unlabelled servers",
    80  			`server {
    81  			   hosts = ["*:8888"]
    82  			 }
    83  			 server "test" {
    84  			   hosts = ["*:9999"]
    85  			 }
    86  			 `,
    87  			"",
    88  		},
    89  		{
    90  			"duplicate server labels",
    91  			`server "test" {
    92  			   hosts = ["*:8888"]
    93  			 }
    94  			 server "test" {
    95  			   hosts = ["*:9999"]
    96  			 }`,
    97  			"",
    98  		},
    99  		{
   100  			"unique server label",
   101  			`server "test" {
   102  			   hosts = ["*:8888"]
   103  			 }
   104  			 server "foo" {
   105  			   hosts = ["*:9999"]
   106  			 }
   107  			 definitions {
   108  			   basic_auth "test" {}
   109  			 }`,
   110  			"",
   111  		},
   112  		{
   113  			"anonymous api block",
   114  			`server "test" {
   115  			   api {}
   116  			 }`,
   117  			"",
   118  		},
   119  		{
   120  			"multiple anonymous api blocks, different base_path",
   121  			`server "test" {
   122  			   api {
   123  			     base_path = "/foo"
   124  			   }
   125  			   api {
   126  			     base_path = "/bar"
   127  			   }
   128  			 }`,
   129  			"",
   130  		},
   131  		{
   132  			"multiple anonymous api blocks (sharing base_path)",
   133  			`server "test" {
   134  			   api {}
   135  			   api {}
   136  			 }`,
   137  			"",
   138  		},
   139  		{
   140  			"api blocks sharing base_path",
   141  			`server "test" {
   142  			   api {
   143  			     base_path = "/foo"
   144  			   }
   145  			   api {
   146  			     base_path = "/foo"
   147  			   }
   148  			 }`,
   149  			"",
   150  		},
   151  
   152  		{
   153  			"mixed labelled api blocks",
   154  			`server "test" {
   155  			   api {
   156  			     base_path = "/foo"
   157  			   }
   158  			   api "bar" {
   159  			     base_path = "/bar"
   160  			   }
   161  			 }`,
   162  			"",
   163  		},
   164  		{
   165  			"duplicate api labels",
   166  			`server "test" {
   167  			   api "foo" {
   168  			     base_path = "/foo"
   169  			   }
   170  			   api "foo" {
   171  			     base_path = "/bar"
   172  			   }
   173  			 }`,
   174  			``,
   175  		},
   176  		{
   177  			"uniquely labelled api blocks per server",
   178  			`server "foo" {
   179  			   hosts = ["*:8888"]
   180  			   api "foo" {
   181  			     base_path = "/foo"
   182  			   }
   183  			   api "bar" {
   184  			     base_path = "/bar"
   185  			   }
   186  			 }
   187  			 server "bar" {
   188  			   hosts = ["*:9999"]
   189  			   api "foo" {
   190  			     base_path = "/foo"
   191  			   }
   192  			   api "bar" {
   193  			     base_path = "/bar"
   194  			   }
   195  			 }`,
   196  			"",
   197  		},
   198  	}
   199  
   200  	logger, _ := logrustest.NewNullLogger()
   201  	log := logger.WithContext(context.TODO())
   202  
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(subT *testing.T) {
   205  			conf, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
   206  			if conf != nil {
   207  				tmpStoreCh := make(chan struct{})
   208  				defer close(tmpStoreCh)
   209  
   210  				ctx, cancel := context.WithCancel(conf.Context)
   211  				conf.Context = ctx
   212  				defer cancel()
   213  
   214  				_, err = runtime.NewServerConfiguration(conf, log, cache.New(log, tmpStoreCh))
   215  			}
   216  
   217  			var errMsg string
   218  			if err != nil {
   219  				errMsg = err.Error()
   220  			}
   221  
   222  			if tt.error != errMsg {
   223  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
   224  			}
   225  		})
   226  	}
   227  }
   228  
   229  func Test_validateBody(t *testing.T) {
   230  	tests := []struct {
   231  		name  string
   232  		hcl   string
   233  		error string
   234  	}{
   235  		{
   236  			"missing backend label",
   237  			`server {}
   238  			 definitions {
   239  			   backend {
   240  			   }
   241  			 }`,
   242  			"couper.hcl:3,15-16: missing label; ",
   243  		},
   244  		{
   245  			"empty backend label",
   246  			`server {}
   247  			 definitions {
   248  			   backend "" {
   249  			   }
   250  			 }`,
   251  			"couper.hcl:3,15-17: label is empty; ",
   252  		},
   253  		{
   254  			"whitespace backend label",
   255  			`server {}
   256  			 definitions {
   257  			   backend " 	" {
   258  			   }
   259  			 }`,
   260  			"couper.hcl:3,15-19: label is empty; ",
   261  		},
   262  		{
   263  			"invalid backend label",
   264  			`server {}
   265  			 definitions {
   266  			   backend "foo bar" {
   267  			   }
   268  			 }`,
   269  			"couper.hcl:3,15-24: label contains invalid character(s), allowed are 'a-z', 'A-Z', '0-9' and '_'; ",
   270  		},
   271  		{
   272  			"anonymous_* backend label",
   273  			`server {}
   274  			 definitions {
   275  			   backend "anonymous_foo" {
   276  			   }
   277  			 }`,
   278  			"couper.hcl:3,15-30: backend label must not start with 'anonymous_'; ",
   279  		},
   280  		{
   281  			"duplicate backend labels",
   282  			`server {}
   283  			 definitions {
   284  			   backend "foo" {
   285  			   }
   286  			   backend "foo" {
   287  			   }
   288  			 }`,
   289  			"couper.hcl:5,15-20: backend labels must be unique; ",
   290  		},
   291  		{
   292  			"backend disable cert validation with tls block and server_ca_certificate",
   293  			`server {}
   294  			 definitions {
   295  			   backend "foo" {
   296  					disable_certificate_validation = false # value does not matter
   297  					tls {
   298  						server_ca_certificate = "asdf"
   299  					}
   300  			   }
   301  			 }`,
   302  			"couper.hcl:5,6-7,7: configured 'disable_certificate_validation' along with 'server_ca_certificate' attribute; ",
   303  		},
   304  		{
   305  			"backend disable cert validation with tls block and server_ca_certificate_file",
   306  			`server {}
   307  			 definitions {
   308  			   backend "foo" {
   309  					disable_certificate_validation = true # value does not matter
   310  					tls {
   311  						server_ca_certificate_file = "asdf.crt"
   312  					}
   313  			   }
   314  			 }`,
   315  			"couper.hcl:5,6-7,7: configured 'disable_certificate_validation' along with 'server_ca_certificate_file' attribute; ",
   316  		},
   317  		{
   318  			"backend disable cert validation with tls block and w/o server_ca_certificate*",
   319  			`server {}
   320  			 definitions {
   321  			   backend "foo" {
   322  					disable_certificate_validation = true # value does not matter
   323  					tls {}
   324  			   }
   325  			 }`,
   326  			"",
   327  		},
   328  		{
   329  			"duplicate proxy labels",
   330  			`server {}
   331  			 definitions {
   332  			   proxy "foo" {
   333  			   }
   334  			   proxy "foo" {
   335  			   }
   336  			 }`,
   337  			"couper.hcl:5,13-18: proxy labels must be unique; ",
   338  		},
   339  		{
   340  			"missing basic_auth label",
   341  			`server {}
   342  			 definitions {
   343  			   basic_auth {
   344  			   }
   345  			 }`,
   346  			"couper.hcl:3,18-19: missing label; ",
   347  		},
   348  		{
   349  			"missing beta_oauth2 label",
   350  			`server {}
   351  			 definitions {
   352  			   beta_oauth2 {
   353  			   }
   354  			 }`,
   355  			"couper.hcl:3,19-20: missing label; ",
   356  		},
   357  		{
   358  			"missing jwt label",
   359  			`server {}
   360  			 definitions {
   361  			   jwt {
   362  			   }
   363  			 }`,
   364  			"couper.hcl:3,11-12: missing label; ",
   365  		},
   366  		{
   367  			"missing oidc label",
   368  			`server {}
   369  			 definitions {
   370  			   oidc {
   371  			   }
   372  			 }`,
   373  			"couper.hcl:3,12-13: missing label; ",
   374  		},
   375  		{
   376  			"missing saml label",
   377  			`server {}
   378  			 definitions {
   379  			   saml {
   380  			   }
   381  			 }`,
   382  			"couper.hcl:3,12-13: missing label; ",
   383  		},
   384  		{
   385  			"basic_auth with empty label",
   386  			`server {}
   387  			 definitions {
   388  			   basic_auth "" {
   389  			   }
   390  			 }`,
   391  			"couper.hcl:3,18-20: accessControl requires a label; ",
   392  		},
   393  		{
   394  			"beta_oauth2 with empty label",
   395  			`server {}
   396  			 definitions {
   397  			   beta_oauth2 "" {
   398  			   }
   399  			 }`,
   400  			"couper.hcl:3,19-21: accessControl requires a label; ",
   401  		},
   402  		{
   403  			"jwt with empty label",
   404  			`server {}
   405  			 definitions {
   406  			   jwt "" {
   407  			   }
   408  			 }`,
   409  			"couper.hcl:3,11-13: accessControl requires a label; ",
   410  		},
   411  		{
   412  			"oidc with empty label",
   413  			`server {}
   414  			 definitions {
   415  			   oidc "" {
   416  			   }
   417  			 }`,
   418  			"couper.hcl:3,12-14: accessControl requires a label; ",
   419  		},
   420  		{
   421  			"saml with empty label",
   422  			`server {}
   423  			 definitions {
   424  			   saml "" {
   425  			   }
   426  			 }`,
   427  			"couper.hcl:3,12-14: accessControl requires a label; ",
   428  		},
   429  		{
   430  			"basic_auth with whitespace label",
   431  			`server {}
   432  			 definitions {
   433  			   basic_auth " 	" {
   434  			   }
   435  			 }`,
   436  			"couper.hcl:3,18-22: accessControl requires a label; ",
   437  		},
   438  		{
   439  			"beta_oauth2 with whitespace label",
   440  			`server {}
   441  			 definitions {
   442  			   beta_oauth2 " 	" {
   443  			   }
   444  			 }`,
   445  			"couper.hcl:3,19-23: accessControl requires a label; ",
   446  		},
   447  		{
   448  			"jwt with whitespace label",
   449  			`server {}
   450  			 definitions {
   451  			   jwt " 	" {
   452  			   }
   453  			 }`,
   454  			"couper.hcl:3,11-15: accessControl requires a label; ",
   455  		},
   456  		{
   457  			"oidc with whitespace label",
   458  			`server {}
   459  			 definitions {
   460  			   oidc " 	" {
   461  			   }
   462  			 }`,
   463  			"couper.hcl:3,12-16: accessControl requires a label; ",
   464  		},
   465  		{
   466  			"saml with whitespace label",
   467  			`server {}
   468  			 definitions {
   469  			   saml " 	" {
   470  			   }
   471  			 }`,
   472  			"couper.hcl:3,12-16: accessControl requires a label; ",
   473  		},
   474  		{
   475  			"basic_auth reserved label granted_permissions",
   476  			`server {}
   477  			 definitions {
   478  			   basic_auth "granted_permissions" {
   479  			   }
   480  			 }`,
   481  			"couper.hcl:3,18-39: accessControl uses reserved name as label; ",
   482  		},
   483  		{
   484  			"basic_auth reserved label required_permission",
   485  			`server {}
   486  			 definitions {
   487  			   basic_auth "required_permission" {
   488  			   }
   489  			 }`,
   490  			"couper.hcl:3,18-39: accessControl uses reserved name as label; ",
   491  		},
   492  		{
   493  			"beta_oauth2 reserved label granted_permissions",
   494  			`server {}
   495  			 definitions {
   496  			   beta_oauth2 "granted_permissions" {
   497  			   }
   498  			 }`,
   499  			"couper.hcl:3,19-40: accessControl uses reserved name as label; ",
   500  		},
   501  		{
   502  			"beta_oauth2 reserved label required_permission",
   503  			`server {}
   504  			 definitions {
   505  			   beta_oauth2 "required_permission" {
   506  			   }
   507  			 }`,
   508  			"couper.hcl:3,19-40: accessControl uses reserved name as label; ",
   509  		},
   510  		{
   511  			"jwt reserved label granted_permissions",
   512  			`server {}
   513  			 definitions {
   514  			   jwt "granted_permissions" {
   515  			   }
   516  			 }`,
   517  			"couper.hcl:3,11-32: accessControl uses reserved name as label; ",
   518  		},
   519  		{
   520  			"jwt reserved label required_permission",
   521  			`server {}
   522  			 definitions {
   523  			   jwt "required_permission" {
   524  			   }
   525  			 }`,
   526  			"couper.hcl:3,11-32: accessControl uses reserved name as label; ",
   527  		},
   528  		{
   529  			"oidc reserved label granted_permissions",
   530  			`server {}
   531  			 definitions {
   532  			   oidc "granted_permissions" {
   533  			   }
   534  			 }`,
   535  			"couper.hcl:3,12-33: accessControl uses reserved name as label; ",
   536  		},
   537  		{
   538  			"oidc reserved label required_permission",
   539  			`server {}
   540  			 definitions {
   541  			   oidc "required_permission" {
   542  			   }
   543  			 }`,
   544  			"couper.hcl:3,12-33: accessControl uses reserved name as label; ",
   545  		},
   546  		{
   547  			"saml reserved label granted_permissions",
   548  			`server {}
   549  			 definitions {
   550  			   saml "granted_permissions" {
   551  			   }
   552  			 }`,
   553  			"couper.hcl:3,12-33: accessControl uses reserved name as label; ",
   554  		},
   555  		{
   556  			"saml reserved label required_permission",
   557  			`server {}
   558  			 definitions {
   559  			   saml "required_permission" {
   560  			   }
   561  			 }`,
   562  			"couper.hcl:3,12-33: accessControl uses reserved name as label; ",
   563  		},
   564  		{
   565  			"duplicate AC labels 1",
   566  			`server {}
   567  			 definitions {
   568  			   basic_auth "foo" {
   569  			   }
   570  			   beta_oauth2 "foo" {
   571  			   }
   572  			 }`,
   573  			"couper.hcl:5,19-24: AC labels must be unique; ",
   574  		},
   575  		{
   576  			"duplicate AC labels 2",
   577  			`server {}
   578  			 definitions {
   579  			   beta_oauth2 "foo" {
   580  			   }
   581  			   jwt "foo" {
   582  			   }
   583  			 }`,
   584  			"couper.hcl:5,11-16: AC labels must be unique; ",
   585  		},
   586  		{
   587  			"duplicate AC labels 3",
   588  			`server {}
   589  			 definitions {
   590  			   jwt "foo" {
   591  			   }
   592  			   oidc "foo" {
   593  			   }
   594  			 }`,
   595  			"couper.hcl:5,12-17: AC labels must be unique; ",
   596  		},
   597  		{
   598  			"duplicate AC labels 4",
   599  			`server {}
   600  			 definitions {
   601  			   oidc "foo" {
   602  			   }
   603  			   saml "foo" {
   604  			   }
   605  			 }`,
   606  			"couper.hcl:5,12-17: AC labels must be unique; ",
   607  		},
   608  		{
   609  			"duplicate AC labels 5",
   610  			`server {}
   611  			 definitions {
   612  			   saml "foo" {
   613  			   }
   614  			   basic_auth "foo" {
   615  			   }
   616  			 }`,
   617  			"couper.hcl:5,18-23: AC labels must be unique; ",
   618  		},
   619  		{
   620  			"duplicate signing profile labels 1",
   621  			`server {}
   622  			 definitions {
   623  			   jwt "foo" {
   624  			     signing_ttl = "1m"
   625  			   }
   626  			   jwt_signing_profile "foo" {
   627  			   }
   628  			 }`,
   629  			"couper.hcl:6,27-32: JWT signing profile labels must be unique; ",
   630  		},
   631  		{
   632  			"jwt not used as signing profile",
   633  			`server {}
   634  			 definitions {
   635  			   jwt_signing_profile "foo" {
   636  			     signature_algorithm = "HS256"
   637  			     key = "asdf"
   638  			     ttl = "1m"
   639  			   }
   640  			   jwt "foo" {
   641  			     signature_algorithm = "HS256"
   642  			     key = "sdfg"
   643  			   }
   644  			 }`,
   645  			"",
   646  		},
   647  		{
   648  			"duplicate signing profile labels 2",
   649  			`server {}
   650  			 definitions {
   651  			   jwt_signing_profile "foo" {
   652  			   }
   653  			   jwt "foo" {
   654  			     signing_ttl = "1m"
   655  			   }
   656  			 }`,
   657  			"couper.hcl:5,11-16: JWT signing profile labels must be unique; ",
   658  		},
   659  		{
   660  			"same label for backend and AC",
   661  			`server {}
   662  			 definitions {
   663  			   backend "foo" {
   664  			   }
   665  			   basic_auth "foo" {
   666  			   }
   667  			 }`,
   668  			"",
   669  		},
   670  		{
   671  			"invalid server base_path pattern single dot 1",
   672  			`server {
   673  			   base_path = "./s"
   674  			 }`,
   675  			`couper.hcl:2,20-23: base_path must not contain "." or ".." segments; `,
   676  		},
   677  		{
   678  			"invalid server base_path pattern single dot 2",
   679  			`server {
   680  			   base_path = "/./s"
   681  			 }`,
   682  			`couper.hcl:2,20-24: base_path must not contain "." or ".." segments; `,
   683  		},
   684  		{
   685  			"invalid server base_path pattern single dot 3",
   686  			`server {
   687  			   base_path = "/s/./s"
   688  			 }`,
   689  			`couper.hcl:2,20-26: base_path must not contain "." or ".." segments; `,
   690  		},
   691  		{
   692  			"invalid server base_path pattern single dot 4",
   693  			`server {
   694  			   base_path = "/s/."
   695  			 }`,
   696  			`couper.hcl:2,20-24: base_path must not contain "." or ".." segments; `,
   697  		},
   698  		{
   699  			"invalid server base_path pattern double dot 1",
   700  			`server {
   701  			   base_path = "../s"
   702  			 }`,
   703  			`couper.hcl:2,20-24: base_path must not contain "." or ".." segments; `,
   704  		},
   705  		{
   706  			"invalid server base_path pattern double dot 2",
   707  			`server {
   708  			   base_path = "/../s"
   709  			 }`,
   710  			`couper.hcl:2,20-25: base_path must not contain "." or ".." segments; `,
   711  		},
   712  		{
   713  			"invalid server base_path pattern double dot 3",
   714  			`server {
   715  			   base_path = "/s/../s"
   716  			 }`,
   717  			`couper.hcl:2,20-27: base_path must not contain "." or ".." segments; `,
   718  		},
   719  		{
   720  			"invalid server base_path pattern double dot 4",
   721  			`server {
   722  			   base_path = "/s/.."
   723  			 }`,
   724  			`couper.hcl:2,20-25: base_path must not contain "." or ".." segments; `,
   725  		},
   726  		{
   727  			"invalid api base_path pattern single dot 1",
   728  			`server {
   729  			   api {
   730  			     base_path = "./s"
   731  			   }
   732  			 }`,
   733  			`couper.hcl:3,22-25: base_path must not contain "." or ".." segments; `,
   734  		},
   735  		{
   736  			"invalid api base_path pattern single dot 2",
   737  			`server {
   738  			   api {
   739  			     base_path = "/./s"
   740  			   }
   741  			 }`,
   742  			`couper.hcl:3,22-26: base_path must not contain "." or ".." segments; `,
   743  		},
   744  		{
   745  			"invalid api base_path pattern single dot 3",
   746  			`server {
   747  			   api {
   748  			     base_path = "/s/./s"
   749  			   }
   750  			 }`,
   751  			`couper.hcl:3,22-28: base_path must not contain "." or ".." segments; `,
   752  		},
   753  		{
   754  			"invalid api base_path pattern single dot 4",
   755  			`server {
   756  			   api {
   757  			     base_path = "/s/."
   758  			   }
   759  			 }`,
   760  			`couper.hcl:3,22-26: base_path must not contain "." or ".." segments; `,
   761  		},
   762  		{
   763  			"invalid api base_path pattern double dot 1",
   764  			`server {
   765  			   api {
   766  			     base_path = "../s"
   767  			   }
   768  			 }`,
   769  			`couper.hcl:3,22-26: base_path must not contain "." or ".." segments; `,
   770  		},
   771  		{
   772  			"invalid api base_path pattern double dot 2",
   773  			`server {
   774  			   api {
   775  			     base_path = "/../s"
   776  			   }
   777  			 }`,
   778  			`couper.hcl:3,22-27: base_path must not contain "." or ".." segments; `,
   779  		},
   780  		{
   781  			"invalid api base_path pattern double dot 3",
   782  			`server {
   783  			   api {
   784  			     base_path = "/s/../s"
   785  			   }
   786  			 }`,
   787  			`couper.hcl:3,22-29: base_path must not contain "." or ".." segments; `,
   788  		},
   789  		{
   790  			"invalid api base_path pattern double dot 4",
   791  			`server {
   792  			   api {
   793  			     base_path = "/s/.."
   794  			   }
   795  			 }`,
   796  			`couper.hcl:3,22-27: base_path must not contain "." or ".." segments; `,
   797  		},
   798  		{
   799  			"invalid endpoint pattern single dot 1",
   800  			`server {
   801  			   api {
   802  			     endpoint "./foo" {
   803  			       response {
   804  			         body = "1"
   805  			       }
   806  			     }
   807  			   }
   808  			 }`,
   809  			`couper.hcl:3,18-25: endpoint path pattern must start with "/"; `,
   810  		},
   811  		{
   812  			"invalid endpoint pattern single dot 2",
   813  			`server {
   814  			   api {
   815  			     endpoint "/foo/./bar" {
   816  			       response {
   817  			         body = "1"
   818  			       }
   819  			     }
   820  			   }
   821  			 }`,
   822  			`couper.hcl:3,18-30: endpoint path pattern must not contain "." or ".." segments; `,
   823  		},
   824  		{
   825  			"invalid endpoint pattern single dot 3",
   826  			`server {
   827  			   api {
   828  			     endpoint "/foo/." {
   829  			       response {
   830  			         body = "1"
   831  			       }
   832  			     }
   833  			   }
   834  			 }`,
   835  			`couper.hcl:3,18-26: endpoint path pattern must not contain "." or ".." segments; `,
   836  		},
   837  		{
   838  			"invalid endpoint pattern double dot 1",
   839  			`server {
   840  			   api {
   841  			     endpoint "../foo" {
   842  			       response {
   843  			         body = "1"
   844  			       }
   845  			     }
   846  			   }
   847  			 }`,
   848  			`couper.hcl:3,18-26: endpoint path pattern must start with "/"; `,
   849  		},
   850  		{
   851  			"invalid endpoint pattern double dot 2",
   852  			`server {
   853  			   api {
   854  			     endpoint "/foo/../bar" {
   855  			       response {
   856  			         body = "1"
   857  			       }
   858  			     }
   859  			   }
   860  			 }`,
   861  			`couper.hcl:3,18-31: endpoint path pattern must not contain "." or ".." segments; `,
   862  		},
   863  		{
   864  			"invalid endpoint pattern double dot 3",
   865  			`server {
   866  			   api {
   867  			     endpoint "/foo/.." {
   868  			       response {
   869  			         body = "1"
   870  			       }
   871  			     }
   872  			   }
   873  			 }`,
   874  			`couper.hcl:3,18-27: endpoint path pattern must not contain "." or ".." segments; `,
   875  		},
   876  		{
   877  			"duplicate endpoint pattern 1",
   878  			`server {
   879  			   base_path = "/s"
   880  			   api {
   881  			     endpoint "/" {
   882  			       response {
   883  			         body = "1"
   884  			       }
   885  			     }
   886  			     endpoint "/" {
   887  			       response {
   888  			         body = "2"
   889  			       }
   890  			     }
   891  			   }
   892  			 }`,
   893  			"couper.hcl:9,18-21: duplicate endpoint; ",
   894  		},
   895  		{
   896  			"duplicate endpoint pattern 2",
   897  			`server {
   898  			   base_path = "/s"
   899  			   api {
   900  			     endpoint "/" {
   901  			       response {
   902  			         body = "1"
   903  			       }
   904  			     }
   905  			   }
   906  			   api {
   907  			     endpoint "/" {
   908  			       response {
   909  			         body = "2"
   910  			       }
   911  			     }
   912  			   }
   913  			 }`,
   914  			"couper.hcl:11,18-21: duplicate endpoint; ",
   915  		},
   916  		{
   917  			"duplicate endpoint pattern 3",
   918  			`server {
   919  			   base_path = "/s"
   920  			   api {
   921  			     endpoint "/" {
   922  			       response {
   923  			         body = "1"
   924  			       }
   925  			     }
   926  			   }
   927  			   endpoint "/" {
   928  			     response {
   929  			       body = "2"
   930  			     }
   931  			   }
   932  			 }`,
   933  			"couper.hcl:10,16-19: duplicate endpoint; ",
   934  		},
   935  		{
   936  			"duplicate endpoint pattern 4",
   937  			`server {
   938  			   base_path = "/s"
   939  			   endpoint "/" {
   940  			     response {
   941  			       body = "1"
   942  			     }
   943  			   }
   944  			   api {
   945  			     endpoint "/" {
   946  			       response {
   947  			         body = "2"
   948  			       }
   949  			     }
   950  			   }
   951  			 }`,
   952  			"couper.hcl:9,18-21: duplicate endpoint; ",
   953  		},
   954  		{
   955  			"duplicate endpoint pattern 5",
   956  			`server {
   957  			   base_path = "/s"
   958  			   api {
   959  			     base_path = "/a"
   960  			     endpoint "/b/{c}" {
   961  			       response {
   962  			         body = "1"
   963  			       }
   964  			     }
   965  			   }
   966  			   api {
   967  			     base_path = "/a/b"
   968  			     endpoint "/{d}" {
   969  			       response {
   970  			         body = "2"
   971  			       }
   972  			     }
   973  			   }
   974  			 }`,
   975  			"couper.hcl:13,18-24: duplicate endpoint; ",
   976  		},
   977  		{
   978  			"duplicate endpoint pattern 6",
   979  			`server {
   980  			   endpoint "/a/b" {
   981  			     response {
   982  			       body = "1"
   983  			     }
   984  			   }
   985  			   api {
   986  			     base_path = "/a"
   987  			     endpoint "/b" {
   988  			       response {
   989  			         body = "2"
   990  			       }
   991  			     }
   992  			   }
   993  			 }`,
   994  			"couper.hcl:9,18-22: duplicate endpoint; ",
   995  		},
   996  		{
   997  			"distinct endpoint patterns",
   998  			`server {
   999  			   base_path = "/s"
  1000  			   api {
  1001  			     base_path = "/a"
  1002  			     endpoint "/{b}/c" {
  1003  			       response {
  1004  			         body = "1"
  1005  			       }
  1006  			     }
  1007  			   }
  1008  			   api {
  1009  			     base_path = "/a/b"
  1010  			     endpoint "/c" {
  1011  			       response {
  1012  			         body = "2"
  1013  			       }
  1014  			     }
  1015  			   }
  1016  			 }`,
  1017  			"",
  1018  		},
  1019  	}
  1020  
  1021  	for _, tt := range tests {
  1022  		t.Run(tt.name, func(subT *testing.T) {
  1023  			_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
  1024  
  1025  			var errMsg string
  1026  			if err != nil {
  1027  				errMsg = err.Error()
  1028  			}
  1029  
  1030  			if tt.error != errMsg {
  1031  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
  1032  			}
  1033  		})
  1034  	}
  1035  }
  1036  
  1037  func Test_validateBody_multiple(t *testing.T) {
  1038  	tests := []struct {
  1039  		name   string
  1040  		hcls   []string
  1041  		errors []string
  1042  	}{
  1043  		{
  1044  			"duplicate AC labels",
  1045  			[]string{
  1046  				`server {}
  1047  				 definitions {
  1048  				   saml "foo" {
  1049  				   }
  1050  				 }`,
  1051  				`server {}
  1052  				 definitions {
  1053  				   basic_auth "foo" {
  1054  				   }
  1055  				 }`,
  1056  			},
  1057  			[]string{"couper_0.hcl:3,13-18: AC labels must be unique; ", "couper_1.hcl:3,19-24: AC labels must be unique; "},
  1058  		},
  1059  		{
  1060  			"duplicate endpoint patterns",
  1061  			[]string{
  1062  				`server {
  1063  				   endpoint "/a/b" {
  1064  				     response {
  1065  				       body = "1"
  1066  				     }
  1067  				   }
  1068  				 }`,
  1069  				`server {
  1070  				   api {
  1071  				     base_path = "/a"
  1072  				     endpoint "/b" {
  1073  				       response {
  1074  				         body = "2"
  1075  				       }
  1076  				     }
  1077  				   }
  1078  				 }`,
  1079  			},
  1080  			[]string{"couper_1.hcl:4,19-23: duplicate endpoint; "},
  1081  		},
  1082  		{
  1083  			"duplicate signing profile labels",
  1084  			[]string{
  1085  				`server {}
  1086  				 definitions {
  1087  			       jwt "foo" {
  1088  			         signing_ttl = "1m"
  1089  			       }
  1090  				 }`,
  1091  				`server {}
  1092  				 definitions {
  1093  			       jwt_signing_profile "foo" {
  1094  			       }
  1095  				 }`,
  1096  			},
  1097  			[]string{"couper_0.hcl:3,15-20: JWT signing profile labels must be unique; ", "couper_1.hcl:3,31-36: JWT signing profile labels must be unique; "},
  1098  		},
  1099  	}
  1100  
  1101  	for _, tt := range tests {
  1102  		t.Run(tt.name, func(subT *testing.T) {
  1103  			var testContents []testContent
  1104  			for i, hcl := range tt.hcls {
  1105  				testContents = append(testContents, testContent{fmt.Sprintf("couper_%d.hcl", i), []byte(hcl)})
  1106  			}
  1107  
  1108  			_, err := loadTestContents(testContents)
  1109  
  1110  			var errMsg string
  1111  			if err != nil {
  1112  				errMsg = err.Error()
  1113  			}
  1114  
  1115  			if !oneOfErrorMsgs(tt.errors, errMsg) {
  1116  				subT.Errorf("%q: Unexpected configuration error:\n\tWant one of: %v\n\tGot:         %q", tt.name, tt.errors, errMsg)
  1117  			}
  1118  		})
  1119  	}
  1120  }
  1121  
  1122  func oneOfErrorMsgs(msgs []string, errorMsg string) bool {
  1123  	for _, msg := range msgs {
  1124  		if msg == errorMsg {
  1125  			return true
  1126  		}
  1127  	}
  1128  	return false
  1129  }
  1130  
  1131  func TestAttributeObjectKeys(t *testing.T) {
  1132  	tests := []struct {
  1133  		name  string
  1134  		hcl   string
  1135  		error string
  1136  	}{
  1137  		{
  1138  			"add_request_headers",
  1139  			`server {
  1140    api {
  1141      endpoint "/a" {
  1142        add_request_headers = {
  1143          a = "a"
  1144          A = "A"
  1145        }
  1146      }
  1147    }
  1148  }`,
  1149  			"couper.hcl:4,29-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
  1150  		},
  1151  		{
  1152  			"add_response_headers",
  1153  			`server {
  1154    api {
  1155      endpoint "/a" {
  1156        add_response_headers = {
  1157          a = "a"
  1158          A = "A"
  1159        }
  1160      }
  1161    }
  1162  }`,
  1163  			"couper.hcl:4,30-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
  1164  		},
  1165  		{
  1166  			"required_permission",
  1167  			`server {
  1168    api {
  1169      endpoint "/a" {
  1170        required_permission = {
  1171          get = "a"
  1172          GeT = "A"
  1173        }
  1174      }
  1175    }
  1176  }`,
  1177  			"couper.hcl:4,29-7,8: key in an attribute must be unique: 'get'; Key must be unique for get.",
  1178  		},
  1179  		{
  1180  			"headers",
  1181  			`server {
  1182    api {
  1183      endpoint "/a" {
  1184        request {
  1185          headers = {
  1186            a = "a"
  1187            A = "A"
  1188          }
  1189        }
  1190      }
  1191    }
  1192  }`,
  1193  			"couper.hcl:5,19-8,10: key in an attribute must be unique: 'a'; Key must be unique for a.",
  1194  		},
  1195  		{
  1196  			"set_request_headers",
  1197  			`server {
  1198    api {
  1199      endpoint "/a" {
  1200        set_request_headers = {
  1201          a = "a"
  1202          A = "A"
  1203        }
  1204      }
  1205    }
  1206  }`,
  1207  			"couper.hcl:4,29-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
  1208  		},
  1209  		{
  1210  			"set_response_headers",
  1211  			`server {
  1212    api {
  1213      endpoint "/a" {
  1214        set_response_headers = {
  1215          a = "a"
  1216          A = "A"
  1217        }
  1218      }
  1219    }
  1220  }`,
  1221  			"couper.hcl:4,30-7,8: key in an attribute must be unique: 'a'; Key must be unique for a.",
  1222  		},
  1223  		{
  1224  			"json_body",
  1225  			`server {
  1226    api {
  1227      endpoint "/a" {
  1228        request {
  1229          json_body = {
  1230            a = "a"
  1231            A = "A"
  1232          }
  1233        }
  1234      }
  1235    }
  1236  }`,
  1237  			"",
  1238  		},
  1239  		{
  1240  			"form_body",
  1241  			`server {
  1242    api {
  1243      endpoint "/a" {
  1244        request {
  1245          form_body = {
  1246            a = "a"
  1247            A = "A"
  1248          }
  1249        }
  1250      }
  1251    }
  1252  }`,
  1253  			"",
  1254  		},
  1255  		{
  1256  			"roles_map",
  1257  			`server {}
  1258  definitions {
  1259    jwt "a" {
  1260      signature_algorithm = "HS256"
  1261      key = "asdf"
  1262      roles_map = {
  1263        a = []
  1264        A = []
  1265  	}
  1266    }
  1267  }`,
  1268  			"",
  1269  		},
  1270  		{
  1271  			"permissions_map",
  1272  			`server {}
  1273  definitions {
  1274    jwt "a" {
  1275      signature_algorithm = "HS256"
  1276      key = "asdf"
  1277      permissions_map = {
  1278        a = []
  1279        A = []
  1280  	}
  1281    }
  1282  }`,
  1283  			"",
  1284  		},
  1285  		{
  1286  			"claims",
  1287  			`server {}
  1288  definitions {
  1289    jwt "a" {
  1290      signature_algorithm = "HS256"
  1291      key = "asdf"
  1292      claims = {
  1293        a = "a"
  1294        A = "A"
  1295  	}
  1296    }
  1297  }`,
  1298  			"",
  1299  		},
  1300  		{
  1301  			"custom_log_fields",
  1302  			`server {}
  1303  definitions {
  1304    jwt "a" {
  1305      signature_algorithm = "HS256"
  1306      key = "asdf"
  1307      custom_log_fields = {
  1308        a = "a"
  1309        A = "A"
  1310  	}
  1311    }
  1312  }`,
  1313  			"",
  1314  		},
  1315  		{
  1316  			"environment_variables",
  1317  			`server {}
  1318  defaults {
  1319    environment_variables = {
  1320      a = "a"
  1321      A = "A"
  1322    }
  1323  }`,
  1324  			"",
  1325  		},
  1326  	}
  1327  
  1328  	for _, tt := range tests {
  1329  		t.Run(tt.name, func(subT *testing.T) {
  1330  			_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
  1331  
  1332  			var errMsg string
  1333  			if err != nil {
  1334  				errMsg = err.Error()
  1335  			}
  1336  
  1337  			if tt.error != errMsg {
  1338  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
  1339  			}
  1340  		})
  1341  	}
  1342  }
  1343  
  1344  func TestPermissionMixed(t *testing.T) {
  1345  	tests := []struct {
  1346  		name  string
  1347  		hcl   string
  1348  		error string
  1349  	}{
  1350  		{
  1351  			"mixed config: error",
  1352  			`server {
  1353    api "foo" {
  1354      endpoint "/a" {
  1355        required_permission = "a"
  1356        response {}
  1357      }
  1358      endpoint "/b" {
  1359        response {}
  1360      }
  1361    }
  1362  }`,
  1363  			"configuration error: api with label \"foo\" has endpoint without required permission",
  1364  		},
  1365  		{
  1366  			"no mix: all endpoints with permission set",
  1367  			`server {
  1368    api "foo" {
  1369      endpoint "/a" {
  1370        required_permission = "a"
  1371        response {}
  1372      }
  1373      endpoint "/b" {
  1374        required_permission = ""
  1375        response {}
  1376      }
  1377    }
  1378  }`,
  1379  			"",
  1380  		},
  1381  		{
  1382  			"no mix: permission set by api",
  1383  			`server {
  1384    api "foo" {
  1385      required_permission = "api"
  1386      endpoint "/a" {
  1387        required_permission = "a"
  1388        response {}
  1389      }
  1390      endpoint "/b" {
  1391        response {}
  1392      }
  1393    }
  1394  }`,
  1395  			"",
  1396  		},
  1397  		{
  1398  			"no mix: disable_access_control",
  1399  			`server {
  1400    api "foo" {
  1401      endpoint "/a" {
  1402        required_permission = "a"
  1403        response {}
  1404      }
  1405      endpoint "/b" {
  1406        disable_access_control = ["foo"]
  1407        response {}
  1408      }
  1409    }
  1410  }
  1411  definitions {
  1412    basic_auth "foo" {
  1413      password = "asdf"
  1414    }
  1415  }`,
  1416  			"",
  1417  		},
  1418  		{
  1419  			"no mix: separate apis",
  1420  			`server {
  1421    api "foo" {
  1422      endpoint "/a" {
  1423        required_permission = "a"
  1424        response {}
  1425      }
  1426    }
  1427    api "bar" {
  1428      endpoint "/b" {
  1429        response {}
  1430      }
  1431    }
  1432  }`,
  1433  			"",
  1434  		},
  1435  	}
  1436  
  1437  	for _, tt := range tests {
  1438  		t.Run(tt.name, func(subT *testing.T) {
  1439  			_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
  1440  
  1441  			var errMsg string
  1442  			if err != nil {
  1443  				gErr, _ := err.(errors.GoError)
  1444  				errMsg = gErr.LogError()
  1445  			}
  1446  
  1447  			if tt.error != errMsg {
  1448  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
  1449  			}
  1450  		})
  1451  	}
  1452  }
  1453  
  1454  func TestPathAttr(t *testing.T) {
  1455  	tests := []struct {
  1456  		name  string
  1457  		hcl   string
  1458  		error string
  1459  	}{
  1460  		{
  1461  			"path in endpoint: error",
  1462  			`server {
  1463    endpoint "/**" {
  1464      path = "/a/**"
  1465    }
  1466  }`,
  1467  			"couper.hcl:3,5-9: Unsupported argument; An argument named \"path\" is not expected here. Use the \"path\" attribute in a backend block instead.",
  1468  		},
  1469  		{
  1470  			"path in proxy: error",
  1471  			`server {
  1472    endpoint "/**" {
  1473      proxy {
  1474        path = "/a/**"
  1475      }
  1476    }
  1477  }`,
  1478  			"couper.hcl:4,7-11: Unsupported argument; An argument named \"path\" is not expected here. Use the \"path\" attribute in a backend block instead.",
  1479  		},
  1480  		{
  1481  			"path in referenced backend: ok",
  1482  			`server {
  1483    endpoint "/**" {
  1484      proxy {
  1485        backend = "a"
  1486      }
  1487    }
  1488  }
  1489  definitions {
  1490    backend "a" {
  1491      path = "/a/**"
  1492    }
  1493  }`,
  1494  			"",
  1495  		},
  1496  		{
  1497  			"path in refined backend: ok",
  1498  			`server {
  1499    endpoint "/**" {
  1500      proxy {
  1501        backend "a" {
  1502          path = "/a/**"
  1503        }
  1504      }
  1505    }
  1506  }
  1507  definitions {
  1508    backend "a" {
  1509    }
  1510  }`,
  1511  			"",
  1512  		},
  1513  	}
  1514  
  1515  	for _, tt := range tests {
  1516  		t.Run(tt.name, func(subT *testing.T) {
  1517  			_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
  1518  
  1519  			var errMsg string
  1520  			if err != nil {
  1521  				errMsg = err.Error()
  1522  			}
  1523  
  1524  			if tt.error != errMsg {
  1525  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
  1526  			}
  1527  		})
  1528  	}
  1529  }
  1530  
  1531  func Test_checkReferencedAccessControls(t *testing.T) {
  1532  	tests := []struct {
  1533  		name  string
  1534  		hcl   string
  1535  		error string
  1536  	}{
  1537  		{
  1538  			"missing AC referenced by server access_control",
  1539  			`server {
  1540  			  access_control = ["undefined"]
  1541  			}`,
  1542  			`couper.hcl:2,23-36: referenced access control "undefined" is not defined; `,
  1543  		},
  1544  		{
  1545  			"missing AC referenced by server disable_access_control",
  1546  			`server {
  1547  			  disable_access_control = ["undefined"]
  1548  			}`,
  1549  			`couper.hcl:2,31-44: referenced access control "undefined" is not defined; `,
  1550  		},
  1551  		{
  1552  			"missing AC referenced by api access_control",
  1553  			`server {
  1554  			  api {
  1555  			    access_control = ["undefined"]
  1556  			  }
  1557  			}`,
  1558  			`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
  1559  		},
  1560  		{
  1561  			"missing AC referenced by api disable_access_control",
  1562  			`server {
  1563  			  api {
  1564  			    disable_access_control = ["undefined"]
  1565  			  }
  1566  			}`,
  1567  			`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
  1568  		},
  1569  		{
  1570  			"missing AC referenced by files access_control",
  1571  			`server {
  1572  			  files {
  1573  			    access_control = ["undefined"]
  1574  			    document_root = "htdocs"
  1575  			  }
  1576  			}`,
  1577  			`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
  1578  		},
  1579  		{
  1580  			"missing AC referenced by files disable_access_control",
  1581  			`server {
  1582  			  files {
  1583  			    disable_access_control = ["undefined"]
  1584  			    document_root = "htdocs"
  1585  			  }
  1586  			}`,
  1587  			`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
  1588  		},
  1589  		{
  1590  			"missing AC referenced by spa access_control",
  1591  			`server {
  1592  			  spa {
  1593  			    access_control = ["undefined"]
  1594  			    bootstrap_file = "foo"
  1595  			    paths = ["/**"]
  1596  			  }
  1597  			}`,
  1598  			`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
  1599  		},
  1600  		{
  1601  			"missing AC referenced by spa disable_access_control",
  1602  			`server {
  1603  			  spa {
  1604  			    disable_access_control = ["undefined"]
  1605  			    bootstrap_file = "foo"
  1606  			    paths = ["/**"]
  1607  			  }
  1608  			}`,
  1609  			`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
  1610  		},
  1611  		{
  1612  			"missing AC referenced by endpoint access_control",
  1613  			`server {
  1614  			  endpoint "/" {
  1615  			    access_control = ["undefined"]
  1616  			    response {
  1617  			      body = "OK"
  1618  			    }
  1619  			  }
  1620  			}`,
  1621  			`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
  1622  		},
  1623  		{
  1624  			"missing AC referenced by endpoint disable_access_control",
  1625  			`server {
  1626  			  endpoint "/" {
  1627  			    disable_access_control = ["undefined"]
  1628  			    response {
  1629  			      body = "OK"
  1630  			    }
  1631  			  }
  1632  			}`,
  1633  			`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
  1634  		},
  1635  	}
  1636  
  1637  	for _, tt := range tests {
  1638  		t.Run(tt.name, func(subT *testing.T) {
  1639  			_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")
  1640  
  1641  			var errMsg string
  1642  			if err != nil {
  1643  				errMsg = err.Error()
  1644  			}
  1645  
  1646  			if tt.error != errMsg {
  1647  				subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot:  %q", tt.name, tt.error, errMsg)
  1648  			}
  1649  		})
  1650  	}
  1651  }