github.com/secure-build/gitlab-runner@v12.5.0+incompatible/helpers/tls/ca_chain/resolver_url_test.go (about)

     1  package ca_chain
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/x509"
     6  	"errors"
     7  	"testing"
     8  
     9  	"github.com/sirupsen/logrus"
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  type fetcherMockFactory func(t *testing.T) fetcher
    14  
    15  func newFetcherMock(url string, data []byte, err error) fetcherMockFactory {
    16  	return func(t *testing.T) fetcher {
    17  		return func(url string) ([]byte, error) {
    18  			assert.Equal(t, url, url)
    19  
    20  			return data, err
    21  		}
    22  	}
    23  }
    24  
    25  type decoderMockFactory func(t *testing.T) decoder
    26  
    27  func newDecoderMock(inputData []byte, cert *x509.Certificate, err error) decoderMockFactory {
    28  	return func(t *testing.T) decoder {
    29  		return func(data []byte) (*x509.Certificate, error) {
    30  			assert.Equal(t, inputData, data)
    31  
    32  			return cert, err
    33  		}
    34  	}
    35  }
    36  
    37  func TestUrlResolver_Resolve(t *testing.T) {
    38  	testError := errors.New("test-error")
    39  	url1 := "url1"
    40  
    41  	testCACertificate := loadCertificate(t, testCACert)
    42  	testCertificate := loadCertificate(t, testCert)
    43  	testCertificateWithURL := loadCertificate(t, testCert)
    44  	testCertificateWithURL.IssuingCertificateURL = []string{url1, "url2"}
    45  
    46  	tests := map[string]struct {
    47  		certs          []*x509.Certificate
    48  		mockLoopLimit  int
    49  		mockFetcher    fetcherMockFactory
    50  		mockDecoder    decoderMockFactory
    51  		expectedError  string
    52  		expectedCerts  []*x509.Certificate
    53  		expectedOutput []string
    54  	}{
    55  		"empty input chain": {
    56  			certs:          nil,
    57  			mockLoopLimit:  defaultURLResolverLoopLimit,
    58  			expectedError:  "",
    59  			expectedCerts:  nil,
    60  			expectedOutput: nil,
    61  		},
    62  		"last certificate without URL": {
    63  			certs:         []*x509.Certificate{testCertificate},
    64  			mockLoopLimit: defaultURLResolverLoopLimit,
    65  			expectedError: "",
    66  			expectedCerts: []*x509.Certificate{testCertificate},
    67  			expectedOutput: []string{
    68  				"Certificate doesn't provide parent URL: exiting the loop",
    69  			},
    70  		},
    71  		"last certificate with URL and fetcher error": {
    72  			certs:         []*x509.Certificate{testCertificateWithURL},
    73  			mockLoopLimit: defaultURLResolverLoopLimit,
    74  			mockFetcher:   newFetcherMock(url1, nil, testError),
    75  			expectedError: "error while fetching issuer certificate: remote fetch failure: test-error",
    76  			expectedCerts: nil,
    77  			expectedOutput: []string{
    78  				"Remote certificate fetching error",
    79  			},
    80  		},
    81  		"last certificate with URL and decoder error": {
    82  			certs:         []*x509.Certificate{testCertificateWithURL},
    83  			mockLoopLimit: defaultURLResolverLoopLimit,
    84  			mockFetcher:   newFetcherMock(url1, []byte("test"), nil),
    85  			mockDecoder:   newDecoderMock([]byte("test"), nil, testError),
    86  			expectedError: "error while fetching issuer certificate: decoding failure: test-error",
    87  			expectedCerts: nil,
    88  			expectedOutput: []string{
    89  				"Certificate decoding error",
    90  			},
    91  		},
    92  		"last certificate with URL with not self signed": {
    93  			certs:         []*x509.Certificate{testCertificateWithURL},
    94  			mockLoopLimit: defaultURLResolverLoopLimit,
    95  			mockFetcher:   newFetcherMock(url1, []byte("test"), nil),
    96  			mockDecoder:   newDecoderMock([]byte("test"), testCertificate, nil),
    97  			expectedError: "",
    98  			expectedCerts: []*x509.Certificate{testCertificateWithURL, testCertificate},
    99  			expectedOutput: []string{
   100  				"Appending the certificate to the chain",
   101  			},
   102  		},
   103  		"last certificate with URL with self signed": {
   104  			certs:         []*x509.Certificate{testCertificateWithURL},
   105  			mockLoopLimit: defaultURLResolverLoopLimit,
   106  			mockFetcher:   newFetcherMock(url1, []byte("test"), nil),
   107  			mockDecoder:   newDecoderMock([]byte("test"), testCACertificate, nil),
   108  			expectedError: "",
   109  			expectedCerts: []*x509.Certificate{testCertificateWithURL, testCACertificate},
   110  			expectedOutput: []string{
   111  				"Fetched issuer certificate is a ROOT certificate so exiting the loop",
   112  			},
   113  		},
   114  		"infinite loop": {
   115  			certs:         []*x509.Certificate{testCertificateWithURL},
   116  			mockLoopLimit: 3,
   117  			mockFetcher:   newFetcherMock(url1, []byte("test"), nil),
   118  			mockDecoder:   newDecoderMock([]byte("test"), testCertificateWithURL, nil),
   119  			expectedError: "",
   120  			expectedCerts: []*x509.Certificate{testCertificateWithURL, testCertificateWithURL, testCertificateWithURL},
   121  			expectedOutput: []string{
   122  				"urlResolver loop limit exceeded; exiting the loop",
   123  			},
   124  		},
   125  	}
   126  
   127  	for tn, tc := range tests {
   128  		t.Run(tn, func(t *testing.T) {
   129  			out := new(bytes.Buffer)
   130  
   131  			logger := logrus.New()
   132  			logger.SetLevel(logrus.DebugLevel)
   133  			logger.SetOutput(out)
   134  
   135  			r := newURLResolver(logger).(*urlResolver)
   136  			r.loopLimit = tc.mockLoopLimit
   137  
   138  			if tc.mockFetcher != nil {
   139  				r.fetcher = tc.mockFetcher(t)
   140  			}
   141  
   142  			if tc.mockDecoder != nil {
   143  				r.decoder = tc.mockDecoder(t)
   144  			}
   145  
   146  			newCerts, err := r.Resolve(tc.certs)
   147  
   148  			if tc.expectedError != "" {
   149  				assert.EqualError(t, err, tc.expectedError)
   150  			} else {
   151  				assert.NoError(t, err)
   152  			}
   153  
   154  			assert.Equal(t, tc.expectedCerts, newCerts)
   155  
   156  			output := out.String()
   157  			if len(tc.expectedOutput) > 0 {
   158  				for _, expectedLine := range tc.expectedOutput {
   159  					assert.Contains(t, output, expectedLine)
   160  				}
   161  			} else {
   162  				assert.Empty(t, output)
   163  			}
   164  		})
   165  	}
   166  }