go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/core/resources/versions/deb/version_test.go (about)

     1  // Copyright 2016 clair authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package deb
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  )
    23  
    24  const (
    25  	LESS    = -1
    26  	EQUAL   = 0
    27  	GREATER = 1
    28  )
    29  
    30  func TestParse(t *testing.T) {
    31  	cases := []struct {
    32  		str string
    33  		ver version
    34  		err bool
    35  	}{
    36  		// Test 0
    37  		{"0", version{epoch: 0, version: "0", revision: ""}, false},
    38  		{"0:0", version{epoch: 0, version: "0", revision: ""}, false},
    39  		{"0:0-", version{epoch: 0, version: "0", revision: ""}, false},
    40  		{"0:0-0", version{epoch: 0, version: "0", revision: "0"}, false},
    41  		{"0:0.0-0.0", version{epoch: 0, version: "0.0", revision: "0.0"}, false},
    42  		// Test epoched
    43  		{"1:0", version{epoch: 1, version: "0", revision: ""}, false},
    44  		{"5:1", version{epoch: 5, version: "1", revision: ""}, false},
    45  		// Test multiple hyphens
    46  		{"0:0-0-0", version{epoch: 0, version: "0-0", revision: "0"}, false},
    47  		{"0:0-0-0-0", version{epoch: 0, version: "0-0-0", revision: "0"}, false},
    48  		// Test multiple colons
    49  		{"0:0:0-0", version{epoch: 0, version: "0:0", revision: "0"}, false},
    50  		{"0:0:0:0-0", version{epoch: 0, version: "0:0:0", revision: "0"}, false},
    51  		// Test multiple hyphens and colons
    52  		{"0:0:0-0-0", version{epoch: 0, version: "0:0-0", revision: "0"}, false},
    53  		{"0:0-0:0-0", version{epoch: 0, version: "0-0:0", revision: "0"}, false},
    54  		// Test valid characters in version
    55  		{"0:09azAZ.-+~:_-0", version{epoch: 0, version: "09azAZ.-+~:_", revision: "0"}, false},
    56  		// Test valid characters in debian revision
    57  		{"0:0-azAZ09.+~_", version{epoch: 0, version: "0", revision: "azAZ09.+~_"}, false},
    58  		// Test version with leading and trailing spaces
    59  		{"  	0:0-1", version{epoch: 0, version: "0", revision: "1"}, false},
    60  		{"0:0-1	  ", version{epoch: 0, version: "0", revision: "1"}, false},
    61  		{"	  0:0-1  	", version{epoch: 0, version: "0", revision: "1"}, false},
    62  		// Test empty version
    63  		{"", version{}, true},
    64  		{" ", version{}, true},
    65  		{"0:", version{}, true},
    66  		// Test version with embedded spaces
    67  		{"0:0 0-1", version{}, true},
    68  		// Test version with negative epoch
    69  		{"-1:0-1", version{}, true},
    70  		// Test invalid characters in epoch
    71  		{"a:0-0", version{}, true},
    72  		{"A:0-0", version{}, true},
    73  		// Test version not starting with a digit.
    74  		// While recommended by the specification, this is not strictly required and
    75  		// at least one vulnerable Alpine package deviates from this scheme.
    76  		{"0:abc3-0", version{epoch: 0, version: "abc3", revision: "0"}, false},
    77  	}
    78  	for _, c := range cases {
    79  		v, err := newVersion(c.str)
    80  
    81  		if c.err {
    82  			assert.Error(t, err, "When parsing '%s'", c.str)
    83  		} else {
    84  			assert.Nil(t, err, "When parsing '%s'", c.str)
    85  		}
    86  		assert.Equal(t, c.ver, v, "When parsing '%s'", c.str)
    87  	}
    88  
    89  	// Test invalid characters in version
    90  	versym := []rune{'!', '#', '@', '$', '%', '&', '/', '|', '\\', '<', '>', '(', ')', '[', ']', '{', '}', ';', ',', '=', '*', '^', '\''}
    91  	for _, r := range versym {
    92  		_, err := newVersion(strings.Join([]string{"0:0", string(r), "-0"}, ""))
    93  		assert.Error(t, err, "Parsing with invalid character '%s' in version should have failed", string(r))
    94  	}
    95  
    96  	// Test invalid characters in revision
    97  	versym = []rune{'!', '#', '@', '$', '%', '&', '/', '|', '\\', '<', '>', '(', ')', '[', ']', '{', '}', ':', ';', ',', '=', '*', '^', '\''}
    98  	for _, r := range versym {
    99  		_, err := newVersion(strings.Join([]string{"0:0-", string(r)}, ""))
   100  		assert.Error(t, err, "Parsing with invalid character '%s' in revision should have failed", string(r))
   101  	}
   102  }
   103  
   104  func TestParseAndCompare(t *testing.T) {
   105  	cases := []struct {
   106  		v1       string
   107  		expected int
   108  		v2       string
   109  	}{
   110  		{"7.6p2-4", GREATER, "7.6-0"},
   111  		{"1.0.3-3", GREATER, "1.0-1"},
   112  		{"1.3", GREATER, "1.2.2-2"},
   113  		{"1.3", GREATER, "1.2.2"},
   114  		// Some properties of text strings
   115  		{"0-pre", EQUAL, "0-pre"},
   116  		{"0-pre", LESS, "0-pree"},
   117  		{"1.1.6r2-2", GREATER, "1.1.6r-1"},
   118  		{"2.6b2-1", GREATER, "2.6b-2"},
   119  		{"98.1p5-1", LESS, "98.1-pre2-b6-2"},
   120  		{"0.4a6-2", GREATER, "0.4-1"},
   121  		{"1:3.0.5-2", LESS, "1:3.0.5.1"},
   122  		// epochs
   123  		{"1:0.4", GREATER, "10.3"},
   124  		{"1:1.25-4", LESS, "1:1.25-8"},
   125  		{"0:1.18.36", EQUAL, "1.18.36"},
   126  		{"1.18.36", GREATER, "1.18.35"},
   127  		{"0:1.18.36", GREATER, "1.18.35"},
   128  		// Funky, but allowed, characters in upstream version
   129  		{"9:1.18.36:5.4-20", LESS, "10:0.5.1-22"},
   130  		{"9:1.18.36:5.4-20", LESS, "9:1.18.36:5.5-1"},
   131  		{"9:1.18.36:5.4-20", LESS, " 9:1.18.37:4.3-22"},
   132  		{"1.18.36-0.17.35-18", GREATER, "1.18.36-19"},
   133  		// Junk
   134  		{"1:1.2.13-3", LESS, "1:1.2.13-3.1"},
   135  		{"2.0.7pre1-4", LESS, "2.0.7r-1"},
   136  		// if a version includes a dash, it should be the debrev dash - policy says so
   137  		{"0:0-0-0", GREATER, "0-0"},
   138  		// do we like strange versions? Yes we like strange versions…
   139  		{"0", EQUAL, "0"},
   140  		{"0", EQUAL, "00"},
   141  		// #205960
   142  		{"3.0~rc1-1", LESS, "3.0-1"},
   143  		// #573592 - debian policy 5.6.12
   144  		{"1.0", EQUAL, "1.0-0"},
   145  		{"0.2", LESS, "1.0-0"},
   146  		{"1.0", LESS, "1.0-0+b1"},
   147  		{"1.0", GREATER, "1.0-0~"},
   148  		// "steal" the testcases from (old perl) cupt
   149  		{"1.2.3", EQUAL, "1.2.3"},                           // identical
   150  		{"4.4.3-2", EQUAL, "4.4.3-2"},                       // identical
   151  		{"1:2ab:5", EQUAL, "1:2ab:5"},                       // this is correct...
   152  		{"7:1-a:b-5", EQUAL, "7:1-a:b-5"},                   // and this
   153  		{"57:1.2.3abYZ+~-4-5", EQUAL, "57:1.2.3abYZ+~-4-5"}, // and those too
   154  		{"1.2.3", EQUAL, "0:1.2.3"},                         // zero epoch
   155  		{"1.2.3", EQUAL, "1.2.3-0"},                         // zero revision
   156  		{"009", EQUAL, "9"},                                 // zeroes…
   157  		{"009ab5", EQUAL, "9ab5"},                           // there as well
   158  		{"1.2.3", LESS, "1.2.3-1"},                          // added non-zero revision
   159  		{"1.2.3", LESS, "1.2.4"},                            // just bigger
   160  		{"1.2.4", GREATER, "1.2.3"},                         // order doesn't matter
   161  		{"1.2.24", GREATER, "1.2.3"},                        // bigger, eh?
   162  		{"0.10.0", GREATER, "0.8.7"},                        // bigger, eh?
   163  		{"3.2", GREATER, "2.3"},                             // major number rocks
   164  		{"1.3.2a", GREATER, "1.3.2"},                        // letters rock
   165  		{"0.5.0~git", LESS, "0.5.0~git2"},                   // numbers rock
   166  		{"2a", LESS, "21"},                                  // but not in all places
   167  		{"1.3.2a", LESS, "1.3.2b"},                          // but there is another letter
   168  		{"1:1.2.3", GREATER, "1.2.4"},                       // epoch rocks
   169  		{"1:1.2.3", LESS, "1:1.2.4"},                        // bigger anyway
   170  		{"1.2a+~bCd3", LESS, "1.2a++"},                      // tilde doesn't rock
   171  		{"1.2a+~bCd3", GREATER, "1.2a+~"},                   // but first is longer!
   172  		{"5:2", GREATER, "304-2"},                           // epoch rocks
   173  		{"5:2", LESS, "304:2"},                              // so big epoch?
   174  		{"25:2", GREATER, "3:2"},                            // 25 > 3, obviously
   175  		{"1:2:123", LESS, "1:12:3"},                         // 12 > 2
   176  		{"1.2-5", LESS, "1.2-3-5"},                          // 1.2 < 1.2-3
   177  		{"5.10.0", GREATER, "5.005"},                        // preceding zeroes don't matters
   178  		{"3a9.8", LESS, "3.10.2"},                           // letters are before all letter symbols
   179  		{"3a9.8", GREATER, "3~10"},                          // but after the tilde
   180  		{"1.4+OOo3.0.0~", LESS, "1.4+OOo3.0.0-4"},           // another tilde check
   181  		{"2.4.7-1", LESS, "2.4.7-z"},                        // revision comparing
   182  		{"1.002-1+b2", GREATER, "1.00"},                     // whatever...
   183  
   184  		// custom tests
   185  		{"1:4.2-3.2ubuntu2", LESS, "1:4.5-1ubuntu1"},
   186  		{"4.89-2+deb9u1", LESS, "4.89-2+deb9u3"},
   187  		{"1.12.1+dfsg-19+deb8u5", LESS, "1.15-1+deb9u1"},
   188  		{"2.9.4+dfsg1-2.2+deb9u1", LESS, "2.9.4+dfsg1-2.2+deb9u2"},
   189  		{"3.16.2-5+deb9u1", GREATER, "3.8.7.1-1+deb8u3"},
   190  		{"232-25+deb9u8", GREATER, "232-25+deb9u6"},
   191  		{"5.24.1-3+deb9u3", LESS, "5.24.1-3+deb9u5"},
   192  	}
   193  
   194  	var (
   195  		p   Parser
   196  		cmp int
   197  		err error
   198  	)
   199  	for _, c := range cases {
   200  		cmp, err = p.Compare(c.v1, c.v2)
   201  		assert.Nil(t, err)
   202  		assert.Equal(t, c.expected, cmp, "%s vs. %s, = %d, expected %d", c.v1, c.v2, cmp, c.expected)
   203  
   204  		cmp, err = p.Compare(c.v2, c.v1)
   205  		assert.Nil(t, err)
   206  		assert.Equal(t, -c.expected, cmp, "%s vs. %s, = %d, expected %d", c.v2, c.v1, cmp, -c.expected)
   207  	}
   208  }
   209  
   210  func Test(t *testing.T) {
   211  	affected := "5.24.1-3+deb9u3"
   212  	installed := "5.24.1-3+deb9u5"
   213  
   214  	var p Parser
   215  	cmp, err := p.Compare(affected, installed)
   216  	assert.Nil(t, err)
   217  	assert.Equal(t, LESS, cmp)
   218  }