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