@@ -29,6 +29,83 @@ nodeOnly(() => {
29
29
await expect ( fetch ( 'https://localhost:4430' ) ) . to . have . responseText ( 'signed response!' ) ;
30
30
} ) ;
31
31
32
+ describe ( "constrained CA" , ( ) => {
33
+ let constrainedCA : CA ;
34
+ let constrainedCaCert : string ;
35
+
36
+ function localhostRequest ( { hostname, port } : { hostname : string ; port : number } ) {
37
+ return https . request ( {
38
+ hostname,
39
+ port,
40
+ ca : [ constrainedCaCert ] ,
41
+ lookup : ( _ , options , callback ) => {
42
+ if ( options . all ) {
43
+ callback ( null , [ { address : "127.0.0.1" , family : 4 } ] ) ;
44
+ } else {
45
+ callback ( null , "127.0.0.1" , 4 ) ;
46
+ }
47
+ } ,
48
+ } ) ;
49
+ }
50
+
51
+ beforeEach ( async ( ) => {
52
+ const rootCa = await generateCACertificate ( {
53
+ nameConstraints : { permitted : [ "example.com" ] } ,
54
+ } ) ;
55
+ constrainedCaCert = rootCa . cert ;
56
+ constrainedCA = new CA ( rootCa ) ;
57
+ } ) ;
58
+
59
+ it ( "can generate a valid certificate for a domain included in a constrained CA" , async ( ) => {
60
+
61
+ const { cert, key } = constrainedCA . generateCertificate ( "hello.example.com" ) ;
62
+
63
+ server = https . createServer ( { cert, key } , ( req : any , res : any ) => {
64
+ res . writeHead ( 200 ) ;
65
+ res . end ( "signed response!" ) ;
66
+ } ) ;
67
+ await new Promise < void > ( ( resolve ) => server . listen ( 4430 , resolve ) ) ;
68
+
69
+ const req = localhostRequest ( { hostname : "hello.example.com" , port : 4430 } ) ;
70
+ return new Promise < void > ( ( resolve , reject ) => {
71
+ req . on ( "response" , ( res ) => {
72
+ expect ( res . statusCode ) . to . equal ( 200 ) ;
73
+ res . on ( "data" , ( data ) => {
74
+ expect ( data . toString ( ) ) . to . equal ( "signed response!" ) ;
75
+ resolve ( ) ;
76
+ } ) ;
77
+ } ) ;
78
+ req . on ( "error" , ( err ) => {
79
+ reject ( err ) ;
80
+ } ) ;
81
+ req . end ( ) ;
82
+ } ) ;
83
+
84
+ } ) ;
85
+
86
+ it ( "can not generate a valid certificate for a domain not included in a constrained CA" , async ( ) => {
87
+ const { cert, key } = constrainedCA . generateCertificate ( "hello.other.com" ) ;
88
+
89
+ server = https . createServer ( { cert, key } , ( req : any , res : any ) => {
90
+ res . writeHead ( 200 ) ;
91
+ res . end ( "signed response!" ) ;
92
+ } ) ;
93
+ await new Promise < void > ( ( resolve ) => server . listen ( 4430 , resolve ) ) ;
94
+
95
+ const req = localhostRequest ( { hostname : "hello.other.com" , port : 4430 } ) ;
96
+ return new Promise < void > ( ( resolve , reject ) => {
97
+ req . on ( "error" , ( err ) => {
98
+ expect ( err . message ) . to . equal ( "permitted subtree violation" ) ;
99
+ resolve ( ) ;
100
+ } ) ;
101
+ req . on ( "response" , ( res ) => {
102
+ expect . fail ( "Unexpected response received" ) ;
103
+ } ) ;
104
+ req . end ( ) ;
105
+ } ) ;
106
+ } ) ;
107
+ } ) ;
108
+
32
109
afterEach ( ( done ) => {
33
110
if ( server ) server . close ( done ) ;
34
111
} ) ;
@@ -176,5 +253,39 @@ nodeOnly(() => {
176
253
expect ( errors . join ( '\n' ) ) . to . equal ( '' ) ;
177
254
} ) ;
178
255
256
+ it ( "should generate a CA cert constrained to a domain that pass lintcert checks" , async function ( ) {
257
+ this . retries ( 3 ) ; // Remote server can be unreliable
258
+
259
+ const caCertificate = await generateCACertificate ( {
260
+ nameConstraints : {
261
+ permitted : [ 'example.com' ]
262
+ }
263
+ } ) ;
264
+
265
+ const { cert } = caCertificate ;
266
+
267
+ const response = await ignoreNetworkError (
268
+ fetch ( 'https://crt.sh/lintcert' , {
269
+ method : 'POST' ,
270
+ headers : { 'content-type' : 'application/x-www-form-urlencoded' } ,
271
+ body : new URLSearchParams ( { 'b64cert' : cert } )
272
+ } ) ,
273
+ { context : this }
274
+ ) ;
275
+
276
+ const lintOutput = await response . text ( ) ;
277
+
278
+ const lintResults = lintOutput
279
+ . split ( '\n' )
280
+ . map ( line => line . split ( '\t' ) . slice ( 1 ) )
281
+ . filter ( line => line . length > 1 ) ;
282
+
283
+ const errors = lintResults
284
+ . filter ( ( [ level ] ) => level === 'ERROR' )
285
+ . map ( ( [ _level , message ] ) => message ) ;
286
+
287
+ expect ( errors . join ( '\n' ) ) . to . equal ( '' ) ;
288
+ } ) ;
289
+
179
290
} ) ;
180
291
} ) ;
0 commit comments