package policy import ( "net" "testing" "github.com/google/go-cmp/cmp" "github.com/smallstep/assert" ) func Test_normalizeAndValidateDNSDomainConstraint(t *testing.T) { tests := []struct { name string constraint string want string wantErr bool }{ { name: "fail/empty-constraint", constraint: "", want: "", wantErr: true, }, { name: "fail/wildcard-partial-label", constraint: "*xxxx.local", want: "", wantErr: true, }, { name: "fail/wildcard-in-the-middle", constraint: "x.*.local", want: "", wantErr: true, }, { name: "fail/empty-label", constraint: "..local", want: "", wantErr: true, }, { name: "fail/empty-reverse", constraint: ".", want: "", wantErr: true, }, { name: "fail/no-asterisk", constraint: ".example.com", want: "", wantErr: true, }, { name: "fail/idna-internationalized-domain-name-lookup", constraint: `\00.local`, // invalid IDNA ASCII character want: "", wantErr: true, }, { name: "ok/wildcard", constraint: "*.local", want: ".local", wantErr: false, }, { name: "ok/specific-domain", constraint: "example.local", want: "example.local", wantErr: false, }, { name: "ok/idna-internationalized-domain-name-punycode", constraint: "*.xn--fsq.jp", // Example value from https://www.w3.org/International/articles/idn-and-iri/ want: ".xn--fsq.jp", wantErr: false, }, { name: "ok/idna-internationalized-domain-name-lookup-transformed", constraint: "*.例.jp", // Example value from https://www.w3.org/International/articles/idn-and-iri/ want: ".xn--fsq.jp", wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := normalizeAndValidateDNSDomainConstraint(tt.constraint) if (err != nil) != tt.wantErr { t.Errorf("normalizeAndValidateDNSDomainConstraint() error = %v, wantErr %v", err, tt.wantErr) } if got != tt.want { t.Errorf("normalizeAndValidateDNSDomainConstraint() = %v, want %v", got, tt.want) } }) } } func Test_normalizeAndValidateEmailConstraint(t *testing.T) { tests := []struct { name string constraint string want string wantErr bool }{ { name: "fail/empty-constraint", constraint: "", want: "", wantErr: true, }, { name: "fail/asterisk", constraint: "*.local", want: "", wantErr: true, }, { name: "fail/period", constraint: ".local", want: "", wantErr: true, }, { name: "fail/@period", constraint: "@.local", want: "", wantErr: true, }, { name: "fail/too-many-@s", constraint: "@local@example.com", want: "", wantErr: true, }, { name: "fail/parse-mailbox", constraint: "mail@example.com" + string(byte(0)), want: "", wantErr: true, }, { name: "fail/idna-internationalized-domain", constraint: "mail@xn--bla.local", want: "", wantErr: true, }, { name: "fail/idna-internationalized-domain-name-lookup", constraint: `\00local`, want: "", wantErr: true, }, { name: "fail/parse-domain", constraint: "x..example.com", want: "", wantErr: true, }, { name: "ok/wildcard", constraint: "@local", want: "local", wantErr: false, }, { name: "ok/specific-mail", constraint: "mail@local", want: "mail@local", wantErr: false, }, // TODO(hs): fix the below; doesn't get past parseRFC2821Mailbox; I think it should be allowed. // { // name: "ok/idna-internationalized-local", // constraint: `bücher@local`, // want: "bücher@local", // wantErr: false, // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := normalizeAndValidateEmailConstraint(tt.constraint) if (err != nil) != tt.wantErr { t.Errorf("normalizeAndValidateEmailConstraint() error = %v, wantErr %v", err, tt.wantErr) } if got != tt.want { t.Errorf("normalizeAndValidateEmailConstraint() = %v, want %v", got, tt.want) } }) } } func TestNew(t *testing.T) { type test struct { options []NamePolicyOption want *NamePolicyEngine wantErr bool } var tests = map[string]func(t *testing.T) test{ "fail/with-permitted-dns-domains": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedDNSDomains([]string{"**.local"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-dns-domains": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedDNSDomains([]string{"**.local"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-dns-domain": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedDNSDomain("**.local"), }, want: nil, wantErr: true, } }, "fail/with-excluded-dns-domain": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedDNSDomain("**.local"), }, want: nil, wantErr: true, } }, "fail/with-permitted-cidrs": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedCIDRs([]string{"127.0.0.1//24"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-cidrs": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedCIDRs([]string{"127.0.0.1//24"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-ipsOrCIDRs-cidr": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedIPsOrCIDRs([]string{"127.0.0.1//24"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-ipsOrCIDRs-ip": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedIPsOrCIDRs([]string{"127.0.0:1"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-ipsOrCIDRs-cidr": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedIPsOrCIDRs([]string{"127.0.0.1//24"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-ipsOrCIDRs-ip": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedIPsOrCIDRs([]string{"127.0.0:1"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-cidr": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedCIDR("127.0.0.1//24"), }, want: nil, wantErr: true, } }, "fail/with-excluded-cidr": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedCIDR("127.0.0.1//24"), }, want: nil, wantErr: true, } }, "fail/with-permitted-emails": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedEmailAddresses([]string{"*.local"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-emails": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedEmailAddresses([]string{"*.local"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-email": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedEmailAddress("*.local"), }, want: nil, wantErr: true, } }, "fail/with-excluded-email": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedEmailAddress("*.local"), }, want: nil, wantErr: true, } }, "fail/with-permitted-uris": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedURIDomains([]string{"**.local"}), }, want: nil, wantErr: true, } }, "fail/with-excluded-uris": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedURIDomains([]string{"**.local"}), }, want: nil, wantErr: true, } }, "fail/with-permitted-uri": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithPermittedURIDomain("**.local"), }, want: nil, wantErr: true, } }, "fail/with-excluded-uri": func(t *testing.T) test { return test{ options: []NamePolicyOption{ WithExcludedURIDomain("**.local"), }, want: nil, wantErr: true, } }, "ok/default": func(t *testing.T) test { return test{ options: []NamePolicyOption{}, want: &NamePolicyEngine{}, wantErr: false, } }, "ok/subject-verification": func(t *testing.T) test { options := []NamePolicyOption{ WithSubjectCommonNameVerification(), } return test{ options: options, want: &NamePolicyEngine{ verifySubjectCommonName: true, }, wantErr: false, } }, "ok/literal-wildcards": func(t *testing.T) test { options := []NamePolicyOption{ WithAllowLiteralWildcardNames(), } return test{ options: options, want: &NamePolicyEngine{ allowLiteralWildcardNames: true, }, wantErr: false, } }, "ok/with-permitted-dns-wildcard-domains": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedDNSDomains([]string{"*.local", "*.example.com"}), } return test{ options: options, want: &NamePolicyEngine{ permittedDNSDomains: []string{".local", ".example.com"}, numberOfDNSDomainConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-dns-domains": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedDNSDomains([]string{"*.local", "*.example.com"}), } return test{ options: options, want: &NamePolicyEngine{ excludedDNSDomains: []string{".local", ".example.com"}, numberOfDNSDomainConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-dns-wildcard-domain": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedDNSDomain("*.example.com"), } return test{ options: options, want: &NamePolicyEngine{ permittedDNSDomains: []string{".example.com"}, numberOfDNSDomainConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-dns-domain": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedDNSDomain("www.example.com"), } return test{ options: options, want: &NamePolicyEngine{ permittedDNSDomains: []string{"www.example.com"}, numberOfDNSDomainConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-ip-ranges": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedIPRanges( []*net.IPNet{ nw1, nw2, }, ), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-ip-ranges": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedIPRanges( []*net.IPNet{ nw1, nw2, }, ), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-cidrs": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedCIDRs([]string{"127.0.0.1/24", "192.168.0.1/24"}), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-cidrs": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedCIDRs([]string{"127.0.0.1/24", "192.168.0.1/24"}), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-ipsOrCIDRs-cidr": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.31/32") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedIPsOrCIDRs([]string{"127.0.0.1/24", "192.168.0.31"}), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-ipsOrCIDRs-cidr": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) _, nw2, err := net.ParseCIDR("192.168.0.31/32") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedIPsOrCIDRs([]string{"127.0.0.1/24", "192.168.0.31"}), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, nw2, }, numberOfIPRangeConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-cidr": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedCIDR("127.0.0.1/24"), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-excluded-cidr": func(t *testing.T) test { _, nw1, err := net.ParseCIDR("127.0.0.1/24") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedCIDR("127.0.0.1/24"), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfExcludedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-ipv4": func(t *testing.T) test { ip1, nw1, err := net.ParseCIDR("127.0.0.15/32") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedIP(ip1), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-excluded-ipv4": func(t *testing.T) test { ip1, nw1, err := net.ParseCIDR("127.0.0.15/32") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedIP(ip1), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfExcludedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-ipv6": func(t *testing.T) test { ip1, nw1, err := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/128") assert.FatalError(t, err) options := []NamePolicyOption{ WithPermittedIP(ip1), } return test{ options: options, want: &NamePolicyEngine{ permittedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-excluded-ipv6": func(t *testing.T) test { ip1, nw1, err := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/128") assert.FatalError(t, err) options := []NamePolicyOption{ WithExcludedIP(ip1), } return test{ options: options, want: &NamePolicyEngine{ excludedIPRanges: []*net.IPNet{ nw1, }, numberOfIPRangeConstraints: 1, totalNumberOfExcludedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-emails": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedEmailAddresses([]string{"mail@local", "@example.com"}), } return test{ options: options, want: &NamePolicyEngine{ permittedEmailAddresses: []string{"mail@local", "example.com"}, numberOfEmailAddressConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-emails": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedEmailAddresses([]string{"mail@local", "@example.com"}), } return test{ options: options, want: &NamePolicyEngine{ excludedEmailAddresses: []string{"mail@local", "example.com"}, numberOfEmailAddressConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-email": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedEmailAddress("mail@local"), } return test{ options: options, want: &NamePolicyEngine{ permittedEmailAddresses: []string{"mail@local"}, numberOfEmailAddressConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-excluded-email": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedEmailAddress("mail@local"), } return test{ options: options, want: &NamePolicyEngine{ excludedEmailAddresses: []string{"mail@local"}, numberOfEmailAddressConstraints: 1, totalNumberOfExcludedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-uris": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedURIDomains([]string{"host.local", "*.example.com"}), } return test{ options: options, want: &NamePolicyEngine{ permittedURIDomains: []string{"host.local", ".example.com"}, numberOfURIDomainConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-uris": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedURIDomains([]string{"host.local", "*.example.com"}), } return test{ options: options, want: &NamePolicyEngine{ excludedURIDomains: []string{"host.local", ".example.com"}, numberOfURIDomainConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-permitted-uri": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedURIDomain("host.local"), } return test{ options: options, want: &NamePolicyEngine{ permittedURIDomains: []string{"host.local"}, numberOfURIDomainConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-uri-idna": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedURIDomain("*.bücher.example.com"), } return test{ options: options, want: &NamePolicyEngine{ permittedURIDomains: []string{".xn--bcher-kva.example.com"}, numberOfURIDomainConstraints: 1, totalNumberOfPermittedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-excluded-uri": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedURIDomain("host.local"), } return test{ options: options, want: &NamePolicyEngine{ excludedURIDomains: []string{"host.local"}, numberOfURIDomainConstraints: 1, totalNumberOfExcludedConstraints: 1, totalNumberOfConstraints: 1, }, wantErr: false, } }, "ok/with-permitted-principals": func(t *testing.T) test { options := []NamePolicyOption{ WithPermittedPrincipals([]string{"root", "ops"}), } return test{ options: options, want: &NamePolicyEngine{ permittedPrincipals: []string{"root", "ops"}, numberOfPrincipalConstraints: 2, totalNumberOfPermittedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, "ok/with-excluded-principals": func(t *testing.T) test { options := []NamePolicyOption{ WithExcludedPrincipals([]string{"root", "ops"}), } return test{ options: options, want: &NamePolicyEngine{ excludedPrincipals: []string{"root", "ops"}, numberOfPrincipalConstraints: 2, totalNumberOfExcludedConstraints: 2, totalNumberOfConstraints: 2, }, wantErr: false, } }, } for name, prep := range tests { tc := prep(t) t.Run(name, func(t *testing.T) { got, err := New(tc.options...) if (err != nil) != tc.wantErr { t.Errorf("New() error = %v, wantErr %v", err, tc.wantErr) return } if !cmp.Equal(tc.want, got, cmp.AllowUnexported(NamePolicyEngine{})) { t.Errorf("New() diff =\n %s", cmp.Diff(tc.want, got, cmp.AllowUnexported(NamePolicyEngine{}))) } }) } }