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 }