github.com/rigado/snapd@v2.42.5-go-mod+incompatible/asserts/repair_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package asserts_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os/exec" 26 "path/filepath" 27 "strings" 28 "time" 29 30 "github.com/snapcore/snapd/asserts" 31 . "gopkg.in/check.v1" 32 ) 33 34 var ( 35 _ = Suite(&repairSuite{}) 36 ) 37 38 type repairSuite struct { 39 modelsLine string 40 ts time.Time 41 tsLine string 42 43 repairStr string 44 } 45 46 const script = `#!/bin/sh 47 set -e 48 echo "Unpack embedded payload" 49 match=$(grep --text --line-number '^PAYLOAD:$' $0 | cut -d ':' -f 1) 50 payload_start=$((match + 1)) 51 # Using "base64" as its part of coreutils which should be available 52 # everywhere 53 tail -n +$payload_start $0 | base64 --decode - | tar -xzf - 54 # run embedded content 55 ./hello 56 exit 0 57 # payload generated with, may contain binary data 58 # printf '#!/bin/sh\necho hello from the inside\n' > hello 59 # chmod +x hello 60 # tar czf - hello | base64 - 61 PAYLOAD: 62 H4sIAJJt+FgAA+3STQrCMBDF8ax7ihEP0CkxyXn8iCZQE2jr/W11Iwi6KiL8f5u3mLd4i0mx76tZ 63 l86Cc0t2welrPu2c6awGr95bG4x26rw1oivveriN034QMfFSy6fet/uf2m7aQy7tmJp4TFXS8g5y 64 HupVphQllzGfYvPrkQAAAAAAAAAAAAAAAACAN3dTp9TNACgAAA== 65 ` 66 67 var repairExample = fmt.Sprintf("type: repair\n"+ 68 "authority-id: acme\n"+ 69 "brand-id: acme\n"+ 70 "summary: example repair\n"+ 71 "architectures:\n"+ 72 " - amd64\n"+ 73 " - arm64\n"+ 74 "repair-id: 42\n"+ 75 "series:\n"+ 76 " - 16\n"+ 77 "MODELSLINE"+ 78 "TSLINE"+ 79 "body-length: %v\n"+ 80 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij"+ 81 "\n\n"+ 82 script+"\n\n"+ 83 "AXNpZw==", len(script)) 84 85 func (s *repairSuite) SetUpTest(c *C) { 86 s.modelsLine = "models:\n - acme/frobinator\n" 87 s.ts = time.Now().Truncate(time.Second).UTC() 88 s.tsLine = "timestamp: " + s.ts.Format(time.RFC3339) + "\n" 89 90 s.repairStr = strings.Replace(repairExample, "MODELSLINE", s.modelsLine, 1) 91 s.repairStr = strings.Replace(s.repairStr, "TSLINE", s.tsLine, 1) 92 } 93 94 func (s *repairSuite) TestDecodeOK(c *C) { 95 a, err := asserts.Decode([]byte(s.repairStr)) 96 c.Assert(err, IsNil) 97 c.Check(a.Type(), Equals, asserts.RepairType) 98 repair := a.(*asserts.Repair) 99 c.Check(repair.Timestamp(), Equals, s.ts) 100 c.Check(repair.BrandID(), Equals, "acme") 101 c.Check(repair.RepairID(), Equals, 42) 102 c.Check(repair.Summary(), Equals, "example repair") 103 c.Check(repair.Series(), DeepEquals, []string{"16"}) 104 c.Check(repair.Architectures(), DeepEquals, []string{"amd64", "arm64"}) 105 c.Check(repair.Models(), DeepEquals, []string{"acme/frobinator"}) 106 c.Check(string(repair.Body()), Equals, script) 107 } 108 109 const ( 110 repairErrPrefix = "assertion repair: " 111 ) 112 113 func (s *repairSuite) TestDisabled(c *C) { 114 disabledTests := []struct { 115 disabled, expectedErr string 116 dis bool 117 }{ 118 {"true", "", true}, 119 {"false", "", false}, 120 {"foo", `"disabled" header must be 'true' or 'false'`, false}, 121 } 122 123 for _, test := range disabledTests { 124 repairStr := strings.Replace(repairExample, "MODELSLINE", fmt.Sprintf("disabled: %s\n", test.disabled), 1) 125 repairStr = strings.Replace(repairStr, "TSLINE", s.tsLine, 1) 126 127 a, err := asserts.Decode([]byte(repairStr)) 128 if test.expectedErr != "" { 129 c.Check(err, ErrorMatches, repairErrPrefix+test.expectedErr) 130 } else { 131 c.Assert(err, IsNil) 132 repair := a.(*asserts.Repair) 133 c.Check(repair.Disabled(), Equals, test.dis) 134 } 135 } 136 } 137 138 func (s *repairSuite) TestDecodeInvalid(c *C) { 139 invalidTests := []struct{ original, invalid, expectedErr string }{ 140 {"series:\n - 16\n", "series: \n", `"series" header must be a list of strings`}, 141 {"series:\n - 16\n", "series: something\n", `"series" header must be a list of strings`}, 142 {"architectures:\n - amd64\n - arm64\n", "architectures: foo\n", `"architectures" header must be a list of strings`}, 143 {"models:\n - acme/frobinator\n", "models: \n", `"models" header must be a list of strings`}, 144 {"models:\n - acme/frobinator\n", "models: something\n", `"models" header must be a list of strings`}, 145 {"repair-id: 42\n", "repair-id: no-number\n", `"repair-id" header contains invalid characters: "no-number"`}, 146 {"repair-id: 42\n", "repair-id: 0\n", `"repair-id" header contains invalid characters: "0"`}, 147 {"repair-id: 42\n", "repair-id: 01\n", `"repair-id" header contains invalid characters: "01"`}, 148 {"repair-id: 42\n", "repair-id: 99999999999999999999\n", `repair-id too large:.*`}, 149 {"brand-id: acme\n", "brand-id: brand-id-not-eq-authority-id\n", `authority-id and brand-id must match, repair assertions are expected to be signed by the brand: "acme" != "brand-id-not-eq-authority-id"`}, 150 {"summary: example repair\n", "", `"summary" header is mandatory`}, 151 {"summary: example repair\n", "summary: \n", `"summary" header should not be empty`}, 152 {"summary: example repair\n", "summary:\n multi\n line\n", `"summary" header cannot have newlines`}, 153 {s.tsLine, "", `"timestamp" header is mandatory`}, 154 {s.tsLine, "timestamp: \n", `"timestamp" header should not be empty`}, 155 {s.tsLine, "timestamp: 12:30\n", `"timestamp" header is not a RFC3339 date: .*`}, 156 } 157 158 for _, test := range invalidTests { 159 invalid := strings.Replace(s.repairStr, test.original, test.invalid, 1) 160 _, err := asserts.Decode([]byte(invalid)) 161 c.Check(err, ErrorMatches, repairErrPrefix+test.expectedErr) 162 } 163 } 164 165 // FIXME: move to a different layer later 166 func (s *repairSuite) TestRepairCanEmbeddScripts(c *C) { 167 a, err := asserts.Decode([]byte(s.repairStr)) 168 c.Assert(err, IsNil) 169 c.Check(a.Type(), Equals, asserts.RepairType) 170 repair := a.(*asserts.Repair) 171 172 tmpdir := c.MkDir() 173 repairScript := filepath.Join(tmpdir, "repair") 174 err = ioutil.WriteFile(repairScript, []byte(repair.Body()), 0755) 175 c.Assert(err, IsNil) 176 cmd := exec.Command(repairScript) 177 cmd.Dir = tmpdir 178 output, err := cmd.CombinedOutput() 179 c.Check(err, IsNil) 180 c.Check(string(output), Equals, `Unpack embedded payload 181 hello from the inside 182 `) 183 }