github.com/letsencrypt/boulder@v0.20251208.0/ratelimits/names_test.go (about)

     1  package ratelimits
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/letsencrypt/boulder/identifier"
    10  	"github.com/letsencrypt/boulder/test"
    11  )
    12  
    13  func TestNameIsValid(t *testing.T) {
    14  	t.Parallel()
    15  	type args struct {
    16  		name Name
    17  	}
    18  	tests := []struct {
    19  		name string
    20  		args args
    21  		want bool
    22  	}{
    23  		{name: "Unknown", args: args{name: Unknown}, want: false},
    24  		{name: "9001", args: args{name: 9001}, want: false},
    25  		{name: "NewRegistrationsPerIPAddress", args: args{name: NewRegistrationsPerIPAddress}, want: true},
    26  	}
    27  	for _, tt := range tests {
    28  		t.Run(tt.name, func(t *testing.T) {
    29  			got := tt.args.name.isValid()
    30  			test.AssertEquals(t, tt.want, got)
    31  		})
    32  	}
    33  }
    34  
    35  func TestValidateIdForName(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	testCases := []struct {
    39  		limit Name
    40  		desc  string
    41  		id    string
    42  		err   string
    43  	}{
    44  		{
    45  			limit: NewRegistrationsPerIPAddress,
    46  			desc:  "valid IPv4 address",
    47  			id:    "64.112.117.1",
    48  		},
    49  		{
    50  			limit: NewRegistrationsPerIPAddress,
    51  			desc:  "reserved IPv4 address",
    52  			id:    "10.0.0.1",
    53  			err:   "in a reserved address block",
    54  		},
    55  		{
    56  			limit: NewRegistrationsPerIPAddress,
    57  			desc:  "valid IPv6 address",
    58  			id:    "2602:80a:6000::42:42",
    59  		},
    60  		{
    61  			limit: NewRegistrationsPerIPAddress,
    62  			desc:  "IPv6 address in non-canonical form",
    63  			id:    "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
    64  			err:   "must be in canonical form",
    65  		},
    66  		{
    67  			limit: NewRegistrationsPerIPAddress,
    68  			desc:  "empty string",
    69  			id:    "",
    70  			err:   "must be an IP address",
    71  		},
    72  		{
    73  			limit: NewRegistrationsPerIPAddress,
    74  			desc:  "one space",
    75  			id:    " ",
    76  			err:   "must be an IP address",
    77  		},
    78  		{
    79  			limit: NewRegistrationsPerIPAddress,
    80  			desc:  "invalid IPv4 address",
    81  			id:    "10.0.0.9000",
    82  			err:   "must be an IP address",
    83  		},
    84  		{
    85  			limit: NewRegistrationsPerIPAddress,
    86  			desc:  "invalid IPv6 address",
    87  			id:    "2001:0db8:85a3:0000:0000:8a2e:0370:7334:9000",
    88  			err:   "must be an IP address",
    89  		},
    90  		{
    91  			limit: NewRegistrationsPerIPv6Range,
    92  			desc:  "valid IPv6 address range",
    93  			id:    "2602:80a:6000::/48",
    94  		},
    95  		{
    96  			limit: NewRegistrationsPerIPv6Range,
    97  			desc:  "IPv6 address range in non-canonical form",
    98  			id:    "2602:080a:6000::/48",
    99  			err:   "must be in canonical form",
   100  		},
   101  		{
   102  			limit: NewRegistrationsPerIPv6Range,
   103  			desc:  "IPv6 address range with low bits set",
   104  			id:    "2602:080a:6000::1/48",
   105  			err:   "must be in canonical form",
   106  		},
   107  		{
   108  			limit: NewRegistrationsPerIPv6Range,
   109  			desc:  "invalid IPv6 CIDR range",
   110  			id:    "2001:0db8:0000::/128",
   111  			err:   "must be /48",
   112  		},
   113  		{
   114  			limit: NewRegistrationsPerIPv6Range,
   115  			desc:  "invalid IPv6 CIDR",
   116  			id:    "2001:0db8:0000::/48/48",
   117  			err:   "must be an IPv6 CIDR range",
   118  		},
   119  		{
   120  			limit: NewRegistrationsPerIPv6Range,
   121  			desc:  "IPv4 CIDR when we expect IPv6 CIDR range",
   122  			id:    "10.0.0.0/16",
   123  			err:   "must be /48",
   124  		},
   125  		{
   126  			limit: NewRegistrationsPerIPv6Range,
   127  			desc:  "IPv4 CIDR with invalid long mask",
   128  			id:    "10.0.0.0/48",
   129  			err:   "must be an IPv6 CIDR range",
   130  		},
   131  		{
   132  			limit: NewOrdersPerAccount,
   133  			desc:  "valid regId",
   134  			id:    "1234567890",
   135  		},
   136  		{
   137  			limit: NewOrdersPerAccount,
   138  			desc:  "invalid regId",
   139  			id:    "lol",
   140  			err:   "must be an ACME registration Id",
   141  		},
   142  		{
   143  			limit: FailedAuthorizationsPerDomainPerAccount,
   144  			desc:  "transaction: valid regId and domain",
   145  			id:    "12345:example.com",
   146  		},
   147  		{
   148  			limit: FailedAuthorizationsPerDomainPerAccount,
   149  			desc:  "transaction: invalid regId",
   150  			id:    "12ea5:example.com",
   151  			err:   "invalid regId",
   152  		},
   153  		{
   154  			limit: FailedAuthorizationsPerDomainPerAccount,
   155  			desc:  "transaction: invalid domain",
   156  			id:    "12345:examplecom",
   157  			err:   "name needs at least one dot",
   158  		},
   159  		{
   160  			limit: FailedAuthorizationsPerDomainPerAccount,
   161  			desc:  "override: valid regId",
   162  			id:    "12345",
   163  		},
   164  		{
   165  			limit: FailedAuthorizationsPerDomainPerAccount,
   166  			desc:  "override: invalid regId",
   167  			id:    "12ea5",
   168  			err:   "invalid regId",
   169  		},
   170  		{
   171  			limit: FailedAuthorizationsForPausingPerDomainPerAccount,
   172  			desc:  "transaction: valid regId and domain",
   173  			id:    "12345:example.com",
   174  		},
   175  		{
   176  			limit: FailedAuthorizationsForPausingPerDomainPerAccount,
   177  			desc:  "transaction: invalid regId",
   178  			id:    "12ea5:example.com",
   179  			err:   "invalid regId",
   180  		},
   181  		{
   182  			limit: FailedAuthorizationsForPausingPerDomainPerAccount,
   183  			desc:  "transaction: invalid domain",
   184  			id:    "12345:examplecom",
   185  			err:   "name needs at least one dot",
   186  		},
   187  		{
   188  			limit: FailedAuthorizationsForPausingPerDomainPerAccount,
   189  			desc:  "override: valid regId",
   190  			id:    "12345",
   191  		},
   192  		{
   193  			limit: FailedAuthorizationsForPausingPerDomainPerAccount,
   194  			desc:  "override: invalid regId",
   195  			id:    "12ea5",
   196  			err:   "invalid regId",
   197  		},
   198  		{
   199  			limit: CertificatesPerDomainPerAccount,
   200  			desc:  "transaction: valid regId and domain",
   201  			id:    "12345:example.com",
   202  		},
   203  		{
   204  			limit: CertificatesPerDomainPerAccount,
   205  			desc:  "transaction: invalid regId",
   206  			id:    "12ea5:example.com",
   207  			err:   "invalid regId",
   208  		},
   209  		{
   210  			limit: CertificatesPerDomainPerAccount,
   211  			desc:  "transaction: invalid domain",
   212  			id:    "12345:examplecom",
   213  			err:   "name needs at least one dot",
   214  		},
   215  		{
   216  			limit: CertificatesPerDomainPerAccount,
   217  			desc:  "override: valid regId",
   218  			id:    "12345",
   219  		},
   220  		{
   221  			limit: CertificatesPerDomainPerAccount,
   222  			desc:  "override: invalid regId",
   223  			id:    "12ea5",
   224  			err:   "invalid regId",
   225  		},
   226  		{
   227  			limit: CertificatesPerDomain,
   228  			desc:  "valid domain",
   229  			id:    "example.com",
   230  		},
   231  		{
   232  			limit: CertificatesPerDomain,
   233  			desc:  "valid IPv4 address",
   234  			id:    "64.112.117.1",
   235  		},
   236  		{
   237  			limit: CertificatesPerDomain,
   238  			desc:  "valid IPv6 address",
   239  			id:    "2602:80a:6000::",
   240  		},
   241  		{
   242  			limit: CertificatesPerDomain,
   243  			desc:  "IPv6 address with subnet",
   244  			id:    "2602:80a:6000::/64",
   245  			err:   "nor an IP address",
   246  		},
   247  		{
   248  			limit: CertificatesPerDomain,
   249  			desc:  "malformed domain",
   250  			id:    "example:.com",
   251  			err:   "name contains an invalid character",
   252  		},
   253  		{
   254  			limit: CertificatesPerDomain,
   255  			desc:  "empty domain",
   256  			id:    "",
   257  			err:   "Identifier value (name) is empty",
   258  		},
   259  		{
   260  			limit: CertificatesPerFQDNSet,
   261  			desc:  "valid fqdnSet containing a single domain",
   262  			id:    "example.com",
   263  		},
   264  		{
   265  			limit: CertificatesPerFQDNSet,
   266  			desc:  "valid fqdnSet containing a single IPv4 address",
   267  			id:    "64.112.117.1",
   268  		},
   269  		{
   270  			limit: CertificatesPerFQDNSet,
   271  			desc:  "valid fqdnSet containing a single IPv6 address",
   272  			id:    "2602:80a:6000::1",
   273  		},
   274  		{
   275  			limit: CertificatesPerFQDNSet,
   276  			desc:  "valid fqdnSet containing multiple domains",
   277  			id:    "example.com,example.org",
   278  		},
   279  		{
   280  			limit: CertificatesPerFQDNSet,
   281  			desc:  "valid fqdnSet containing multiple domains and IPs",
   282  			id:    "2602:80a:6000::1,64.112.117.1,example.com,example.org",
   283  		},
   284  	}
   285  
   286  	for _, tc := range testCases {
   287  		t.Run(fmt.Sprintf("%s/%s", tc.limit, tc.desc), func(t *testing.T) {
   288  			t.Parallel()
   289  			err := validateIdForName(tc.limit, tc.id)
   290  			if tc.err != "" {
   291  				test.AssertError(t, err, "should have failed")
   292  				test.AssertContains(t, err.Error(), tc.err)
   293  			} else {
   294  				test.AssertNotError(t, err, "should have succeeded")
   295  			}
   296  		})
   297  	}
   298  }
   299  
   300  func TestBuildBucketKey(t *testing.T) {
   301  	t.Parallel()
   302  
   303  	tests := []struct {
   304  		name              Name
   305  		desc              string
   306  		regId             int64
   307  		singleIdent       identifier.ACMEIdentifier
   308  		setOfIdents       identifier.ACMEIdentifiers
   309  		subscriberIP      netip.Addr
   310  		expectErrContains string
   311  		outputTest        func(t *testing.T, key string)
   312  	}{
   313  		// NewRegistrationsPerIPAddress
   314  		{
   315  			name:         NewRegistrationsPerIPAddress,
   316  			desc:         "valid subscriber IPv4 address",
   317  			subscriberIP: netip.MustParseAddr("1.2.3.4"),
   318  			outputTest: func(t *testing.T, key string) {
   319  				test.AssertEquals(t, fmt.Sprintf("%d:1.2.3.4", NewRegistrationsPerIPAddress), key)
   320  			},
   321  		},
   322  		{
   323  			name:         NewRegistrationsPerIPAddress,
   324  			desc:         "valid subscriber IPv6 address",
   325  			subscriberIP: netip.MustParseAddr("2001:db8::1"),
   326  			outputTest: func(t *testing.T, key string) {
   327  				test.AssertEquals(t, fmt.Sprintf("%d:2001:db8::1", NewRegistrationsPerIPAddress), key)
   328  			},
   329  		},
   330  		// NewRegistrationsPerIPv6Range
   331  		{
   332  			name:         NewRegistrationsPerIPv6Range,
   333  			desc:         "valid subscriber IPv6 address",
   334  			subscriberIP: netip.MustParseAddr("2001:db8:abcd:12::1"),
   335  			outputTest: func(t *testing.T, key string) {
   336  				test.AssertEquals(t, fmt.Sprintf("%d:2001:db8:abcd::/48", NewRegistrationsPerIPv6Range), key)
   337  			},
   338  		},
   339  		{
   340  			name:              NewRegistrationsPerIPv6Range,
   341  			desc:              "subscriber IPv4 given for subscriber IPv6 range limit",
   342  			subscriberIP:      netip.MustParseAddr("1.2.3.4"),
   343  			expectErrContains: "requires an IPv6 address",
   344  		},
   345  
   346  		// NewOrdersPerAccount
   347  		{
   348  			name:  NewOrdersPerAccount,
   349  			desc:  "valid registration ID",
   350  			regId: 1337,
   351  			outputTest: func(t *testing.T, key string) {
   352  				test.AssertEquals(t, fmt.Sprintf("%d:1337", NewOrdersPerAccount), key)
   353  			},
   354  		},
   355  		{
   356  			name:              NewOrdersPerAccount,
   357  			desc:              "registration ID missing",
   358  			expectErrContains: "regId is required",
   359  		},
   360  
   361  		// CertificatesPerDomain
   362  		{
   363  			name:        CertificatesPerDomain,
   364  			desc:        "DNS identifier to eTLD+1",
   365  			singleIdent: identifier.NewDNS("www.example.com"),
   366  			outputTest: func(t *testing.T, key string) {
   367  				test.AssertEquals(t, fmt.Sprintf("%d:example.com", CertificatesPerDomain), key)
   368  			},
   369  		},
   370  		{
   371  			name:        CertificatesPerDomain,
   372  			desc:        "valid IPv4 address used as identifier",
   373  			singleIdent: identifier.NewIP(netip.MustParseAddr("5.6.7.8")),
   374  			outputTest: func(t *testing.T, key string) {
   375  				test.AssertEquals(t, fmt.Sprintf("%d:5.6.7.8/32", CertificatesPerDomain), key)
   376  			},
   377  		},
   378  		{
   379  			name:        CertificatesPerDomain,
   380  			desc:        "valid IPv6 address used as identifier",
   381  			singleIdent: identifier.NewIP(netip.MustParseAddr("2001:db8::1")),
   382  			outputTest: func(t *testing.T, key string) {
   383  				test.AssertEquals(t, fmt.Sprintf("%d:2001:db8::/64", CertificatesPerDomain), key)
   384  			},
   385  		},
   386  		{
   387  			name:              CertificatesPerDomain,
   388  			desc:              "identifier missing",
   389  			expectErrContains: "singleIdent is required",
   390  		},
   391  
   392  		// CertificatesPerFQDNSet
   393  		{
   394  			name:        CertificatesPerFQDNSet,
   395  			desc:        "multiple valid DNS identifiers",
   396  			setOfIdents: identifier.NewDNSSlice([]string{"example.com", "example.org"}),
   397  			outputTest: func(t *testing.T, key string) {
   398  				if !strings.HasPrefix(key, fmt.Sprintf("%d:", CertificatesPerFQDNSet)) {
   399  					t.Errorf("expected key to start with %d: got %s", CertificatesPerFQDNSet, key)
   400  				}
   401  			},
   402  		},
   403  		{
   404  			name:        CertificatesPerFQDNSet,
   405  			desc:        "multiple valid DNS and IP identifiers",
   406  			setOfIdents: identifier.ACMEIdentifiers{identifier.NewDNS("example.net"), identifier.NewIP(netip.MustParseAddr("5.6.7.8")), identifier.NewIP(netip.MustParseAddr("2001:db8::1"))},
   407  			outputTest: func(t *testing.T, key string) {
   408  				if !strings.HasPrefix(key, fmt.Sprintf("%d:", CertificatesPerFQDNSet)) {
   409  					t.Errorf("expected key to start with %d: got %s", CertificatesPerFQDNSet, key)
   410  				}
   411  			},
   412  		},
   413  		{
   414  			name:              CertificatesPerFQDNSet,
   415  			desc:              "identifiers missing",
   416  			expectErrContains: "setOfIdents is required",
   417  		},
   418  
   419  		// CertificatesPerDomainPerAccount
   420  		{
   421  			name:  CertificatesPerDomainPerAccount,
   422  			desc:  "only registration ID",
   423  			regId: 1337,
   424  			outputTest: func(t *testing.T, key string) {
   425  				test.AssertEquals(t, fmt.Sprintf("%d:1337", CertificatesPerDomainPerAccount), key)
   426  			},
   427  		},
   428  		{
   429  			name:        CertificatesPerDomainPerAccount,
   430  			desc:        "registration ID and single DNS identifier provided",
   431  			regId:       1337,
   432  			singleIdent: identifier.NewDNS("example.com"),
   433  			outputTest: func(t *testing.T, key string) {
   434  				test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", CertificatesPerDomainPerAccount), key)
   435  			},
   436  		},
   437  		{
   438  			name:              CertificatesPerDomainPerAccount,
   439  			desc:              "single DNS identifier provided without registration ID",
   440  			singleIdent:       identifier.NewDNS("example.com"),
   441  			expectErrContains: "regId is required",
   442  		},
   443  
   444  		// FailedAuthorizationsPerDomainPerAccount
   445  		{
   446  			name:        FailedAuthorizationsPerDomainPerAccount,
   447  			desc:        "registration ID and single DNS identifier",
   448  			regId:       1337,
   449  			singleIdent: identifier.NewDNS("example.com"),
   450  			outputTest: func(t *testing.T, key string) {
   451  				test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", FailedAuthorizationsPerDomainPerAccount), key)
   452  			},
   453  		},
   454  		{
   455  			name:  FailedAuthorizationsPerDomainPerAccount,
   456  			desc:  "only registration ID",
   457  			regId: 1337,
   458  			outputTest: func(t *testing.T, key string) {
   459  				test.AssertEquals(t, fmt.Sprintf("%d:1337", FailedAuthorizationsPerDomainPerAccount), key)
   460  			},
   461  		},
   462  
   463  		// FailedAuthorizationsForPausingPerDomainPerAccount
   464  		{
   465  			name:        FailedAuthorizationsForPausingPerDomainPerAccount,
   466  			desc:        "registration ID and single DNS identifier",
   467  			regId:       1337,
   468  			singleIdent: identifier.NewDNS("example.com"),
   469  			outputTest: func(t *testing.T, key string) {
   470  				test.AssertEquals(t, fmt.Sprintf("%d:1337:example.com", FailedAuthorizationsForPausingPerDomainPerAccount), key)
   471  			},
   472  		},
   473  		{
   474  			name:  FailedAuthorizationsForPausingPerDomainPerAccount,
   475  			desc:  "only registration ID",
   476  			regId: 1337,
   477  			outputTest: func(t *testing.T, key string) {
   478  				test.AssertEquals(t, fmt.Sprintf("%d:1337", FailedAuthorizationsForPausingPerDomainPerAccount), key)
   479  			},
   480  		},
   481  	}
   482  
   483  	for _, tc := range tests {
   484  		t.Run(fmt.Sprintf("%s/%s", tc.name, tc.desc), func(t *testing.T) {
   485  			t.Parallel()
   486  
   487  			key, err := BuildBucketKey(tc.name, tc.regId, tc.singleIdent, tc.setOfIdents, tc.subscriberIP)
   488  			if tc.expectErrContains != "" {
   489  				test.AssertError(t, err, "expected error")
   490  				test.AssertContains(t, err.Error(), tc.expectErrContains)
   491  				return
   492  			}
   493  			test.AssertNotError(t, err, "unexpected error")
   494  			tc.outputTest(t, key)
   495  		})
   496  	}
   497  }