-
Notifications
You must be signed in to change notification settings - Fork 104
Expand file tree
/
Copy pathx509_test.go
More file actions
135 lines (127 loc) · 3.67 KB
/
Copy pathx509_test.go
File metadata and controls
135 lines (127 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (C) 2026 Opsmate, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License, v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// This software is distributed WITHOUT A WARRANTY OF ANY KIND.
// See the Mozilla Public License for details.
package certspotter
import (
"encoding/asn1"
"os/exec"
"slices"
"strings"
"testing"
)
// printableString builds an asn1.RawValue holding a PrintableString, as
// decodeASN1String expects to see it.
func printableString(s string) asn1.RawValue {
return asn1.RawValue{Class: 0, Tag: 19, IsCompound: false, Bytes: []byte(s)}
}
func atv(oid asn1.ObjectIdentifier, value string) AttributeTypeAndValue {
return AttributeTypeAndValue{Type: oid, Value: printableString(value)}
}
// A commonName that is not the first attribute of a multi-valued RDN must still
// be extracted, otherwise a watched name could escape detection.
func TestParseCNsMultiValuedRDN(t *testing.T) {
rdns := RDNSequence{
{
atv(oidOrganization, "Example Org"),
atv(oidCommonName, "watched.example.com"),
},
}
cns, err := rdns.ParseCNs()
if err != nil {
t.Fatal(err)
}
if want := []string{"watched.example.com"}; !slices.Equal(cns, want) {
t.Errorf("ParseCNs() = %q, want %q", cns, want)
}
}
func TestParseCNsMultipleCNs(t *testing.T) {
rdns := RDNSequence{
{atv(oidCommonName, "a.example.com")},
{
atv(oidOrganizationalUnit, "Dept"),
atv(oidCommonName, "b.example.com"),
},
}
cns, err := rdns.ParseCNs()
if err != nil {
t.Fatal(err)
}
if want := []string{"a.example.com", "b.example.com"}; !slices.Equal(cns, want) {
t.Errorf("ParseCNs() = %q, want %q", cns, want)
}
}
func TestRDNSequenceStringEscaping(t *testing.T) {
tests := []struct {
name string
rdns RDNSequence
want string
}{
{
name: "nul byte",
rdns: RDNSequence{{atv(oidCommonName, "ev\x00il")}},
want: `CN=ev\x00il`,
},
{
name: "backslash and comma",
rdns: RDNSequence{{atv(oidOrganization, `a\b,c`)}},
want: `O=a\\b\,c`,
},
{
name: "control characters",
rdns: RDNSequence{{atv(oidCommonName, "a\tb\nc")}},
want: `CN=a\x09b\x0ac`,
},
{
name: "multi-valued rdn joined with plus",
rdns: RDNSequence{{
atv(oidOrganization, "Org"),
atv(oidCommonName, "host.example.com"),
}},
want: "O=Org+CN=host.example.com",
},
{
name: "separate rdns joined with comma",
rdns: RDNSequence{
{atv(oidCommonName, "host.example.com")},
{atv(oidOrganization, "Org")},
},
want: "CN=host.example.com, O=Org",
},
{
name: "printable unicode passes through",
rdns: RDNSequence{{AttributeTypeAndValue{
Type: oidOrganization,
Value: asn1.RawValue{Class: 0, Tag: 12, Bytes: []byte("café")}, // UTF8String
}}},
want: "O=café",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.rdns.String(); got != tt.want {
t.Errorf("String() = %q, want %q", got, tt.want)
}
})
}
}
// Regression test for the denial of service in which a NUL byte in a certificate
// Subject/Issuer DN, placed into a hook script's environment, was rejected by
// os/exec and aborted notification (crashing the whole monitor). The escaped DN
// must be usable as an environment variable value.
func TestRDNSequenceStringIsExecSafe(t *testing.T) {
rdns := RDNSequence{{atv(oidCommonName, "ev\x00il\x01\x02")}}
dn := rdns.String()
if strings.ContainsRune(dn, 0) {
t.Fatalf("DN string still contains a NUL byte: %q", dn)
}
cmd := exec.Command("/bin/sh", "-c", "exit 0")
cmd.Env = []string{"SUBJECT_DN=" + dn}
if err := cmd.Run(); err != nil {
t.Fatalf("exec with SUBJECT_DN=%q failed: %v", dn, err)
}
}