...

Source file src/github.com/sigstore/rekor/pkg/pki/ssh/sign_test.go

Documentation: github.com/sigstore/rekor/pkg/pki/ssh

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package ssh
    17  
    18  import (
    19  	"bytes"
    20  	"os"
    21  	"os/exec"
    22  	"path/filepath"
    23  	"strings"
    24  	"testing"
    25  )
    26  
    27  var (
    28  	// Generated with "ssh-keygen -C test@rekor.dev -f id_rsa"
    29  	sshPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
    30  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
    31  NhAAAAAwEAAQAAAYEA16H5ImoRO7mr41r8Z8JFBdu6jIM+6XU8M0r9F81RuhLYqzr9zw1n
    32  LeGCqFxPXNBKm8ZyH2BCsBHsbXbwe85IMHM3SUh8X/9fI0Lpi5/xbqAproFUpNR+UJYv6s
    33  8AaWk5zpN1rmpBrqGFJfGQKJCioDiiwNGmSdVkUNmQmYIANxJMDWYmNe8vUOh6nYEHB+lz
    34  fGgDAAzVSXTACW994UkSY47AD05swU4rIT/JWA6BkUrEhO//F0QQhFeROCPJiPRhJXGcFf
    35  9SicffJqR/ELzM1zNYnRXMD0bbdTUwDrIcIFFNBbtcfJVOUUCGumSlt+qjUC7y8cvwbHAu
    36  wf5nS6baA7P6LfTYplF2XIAkdWtkN6O1ouoyIHICXMlddDW2vNaJeEXTeKjx51WSM7qPnQ
    37  ZKsBtwjLQeEY/OPkIvu88lNNYSD63qMUA12msohjwVFCIgJVvYLIrkViczZ7t3L7lgy1X0
    38  CJI4e1roOfM/r9jTieyDHchEYpZYcw3L1R2qtePlAAAFiHdJQKl3SUCpAAAAB3NzaC1yc2
    39  EAAAGBANeh+SJqETu5q+Na/GfCRQXbuoyDPul1PDNK/RfNUboS2Ks6/c8NZy3hgqhcT1zQ
    40  SpvGch9gQrAR7G128HvOSDBzN0lIfF//XyNC6Yuf8W6gKa6BVKTUflCWL+rPAGlpOc6Tda
    41  5qQa6hhSXxkCiQoqA4osDRpknVZFDZkJmCADcSTA1mJjXvL1Doep2BBwfpc3xoAwAM1Ul0
    42  wAlvfeFJEmOOwA9ObMFOKyE/yVgOgZFKxITv/xdEEIRXkTgjyYj0YSVxnBX/UonH3yakfx
    43  C8zNczWJ0VzA9G23U1MA6yHCBRTQW7XHyVTlFAhrpkpbfqo1Au8vHL8GxwLsH+Z0um2gOz
    44  +i302KZRdlyAJHVrZDejtaLqMiByAlzJXXQ1trzWiXhF03io8edVkjO6j50GSrAbcIy0Hh
    45  GPzj5CL7vPJTTWEg+t6jFANdprKIY8FRQiICVb2CyK5FYnM2e7dy+5YMtV9AiSOHta6Dnz
    46  P6/Y04nsgx3IRGKWWHMNy9UdqrXj5QAAAAMBAAEAAAGAJyaOcFQnuttUPRxY9ZHNLGofrc
    47  Fqm8KgYoO7/iVWMF2Zn0U/rec2E5t9OIpCEozy7uOR9uZoVUV70sgkk6X5b2qL4C9b/aYF
    48  JQbSFnq8wCQuTTPIJYE7SfBq1Mwuu/TR/RLC7B74u/cxkJkSXnscO9Dso+ussH0hEJjf6y
    49  8yUM1up4Qjbel2gs8i7BPwLdySDkVoPgsWcpbTAyOODGhTAWZ6soy/rD1AEXJeYTGJDtMv
    50  aR+WBihig1TO1g2RWt9bqqiG7PIlljd3ZsjSSU5y3t6ZN/8j5keKD032EtxbZB0WFD3Ar4
    51  FbFwlW+urb2MQ0JyNKOio3nhdjolXYkJa+C6LXdaaml/8BhMR1eLoMe8nS45w76o8mdJWX
    52  wsirB8tvjCLY0QBXgGv/1DTsKu/wEFCW2/Y0e50gF7pHAlYFNmKDcgI9OyORRYhFbV4D82
    53  fI8JLQ42ZJkS/0t6xQma8WC88pbHGEuVSB6CE/p25fyYRX+UPTQ79tWFvLV4kNQAaBAAAA
    54  wEvyd6H8ePyBXImg8JzGxthufB0eXSfZBrabjf6e6bR2ivpJsHmB64gbMkV6MFV7EWYX1B
    55  wYPQxf4gA2Ez7aJvDtfE7uV6pa0WJS3hW1+be8DHEftmLSbTy/TEvDujNb2gqoi7uWQXWJ
    56  yYWZlYO65r1a6HucryQ8+78fTuTRbZALO43vNGz0oXH1hPSddkcbNAhZTsD0rQKNwqVTe5
    57  wl+6Cduy/CQwjHLYrY73MyWy1Vh1LXhAdGMPnWZwGIu/dnkgAAAMEA9KuaoGnfnLQkrjeR
    58  tO4RCRS2quNRvm4L6i4vHgTDsYtoSlR1ujge7SGOOmIPS4XVjZN5zzCOA7+EDVnuz3WWmx
    59  hmkjpG1YxzmJGaWoYdeo3a6UgJtisfMp8eUKqjJT1mhsCliCWtaOQNRoQieDQmgwZzSX/v
    60  ZiGsOIKa6cR37eKvOJSjVrHsAUzdtYrmi8P2gvAUFWyzXobAtpzHcWrwWkOEIm04G0OGXb
    61  J46hfIX3f45E5EKXvFzexGgVOD2I7hAAAAwQDhniYAizfW9YfG7UJWekkl42xMP7Cb8b0W
    62  SindSIuE8bFTukV1yxbmNZp/f0pKvn/DWc2n0I0bwSGZpy8BCY46RKKB2DYQavY/tGcC1N
    63  AynKuvbtWs11A0mTXmq3WwHVXQDozMwJ2nnHpm0UHspPuHqkYpurlP+xoFsocaQ9QwITyp
    64  lL4qHtXBEzaT8okkcGZBHdSx3gk4TzCsEDOP7ZZPLq42lpKMK10zFPTMd0maXtJDYKU/b4
    65  gAATvvPoylyYUAAAAOdGVzdEByZWtvci5kZXYBAgMEBQ==
    66  -----END OPENSSH PRIVATE KEY-----
    67  `
    68  	sshPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDXofkiahE7uavjWvxnwkUF27qMgz7pdTwzSv0XzVG6EtirOv3PDWct4YKoXE9c0EqbxnIfYEKwEextdvB7zkgwczdJSHxf/18jQumLn/FuoCmugVSk1H5Qli/qzwBpaTnOk3WuakGuoYUl8ZAokKKgOKLA0aZJ1WRQ2ZCZggA3EkwNZiY17y9Q6HqdgQcH6XN8aAMADNVJdMAJb33hSRJjjsAPTmzBTishP8lYDoGRSsSE7/8XRBCEV5E4I8mI9GElcZwV/1KJx98mpH8QvMzXM1idFcwPRtt1NTAOshwgUU0Fu1x8lU5RQIa6ZKW36qNQLvLxy/BscC7B/mdLptoDs/ot9NimUXZcgCR1a2Q3o7Wi6jIgcgJcyV10Nba81ol4RdN4qPHnVZIzuo+dBkqwG3CMtB4Rj84+Qi+7zyU01hIPreoxQDXaayiGPBUUIiAlW9gsiuRWJzNnu3cvuWDLVfQIkjh7Wug58z+v2NOJ7IMdyERillhzDcvVHaq14+U= test@rekor.dev
    69  `
    70  	// Generated with "ssh-keygen -C other-test@rekor.dev -f id_rsa"
    71  	otherSSHPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
    72  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
    73  NhAAAAAwEAAQAAAYEAw/WCSWC9TEvCQOwO+T68EvNa3OSIv1Y0+sT8uSvyjPyEO0+p0t8C
    74  g/zy67vOxiQpU5jN6MItjXAjMmeCm8GKMt6gk+cDoaAev/ZfjuzSL7RayExpmhBleh2X3G
    75  KLkkXF9ABFNchlTqSLOZiEjDoNpbFv16KT1sE6CqW8DjxXQkQk9JK65hLH+BxeWMNCEJVa
    76  Cma4X04aJmC7zJAi5yGeeT0SKVqMohavF90O6XiYFCQHuwXPPyHfocqgudmXnozz+6D6ax
    77  JKZMwQsNp3WKumOjlzWnxBCCB1l2jN6Rag8aJ2277iMFXRwjTL/8jaEsW4KkysDf0GjV2/
    78  iqbr0q5b0arDYbv7CrGBR+uH0wGz/Zog1x5iZANObhZULpDrLVJidEMc27HXBb7PMsNDy7
    79  BGYRB1yc0d0y83p8mUqvOlWSArxn1WnAZO04pAgTrclrhEh4ZXOkn2Sn82eu3DpQ8inkol
    80  Y4IfnhIfbOIeemoUNq1tOUquhow9GLRM6INieHLBAAAFkPPnA1jz5wNYAAAAB3NzaC1yc2
    81  EAAAGBAMP1gklgvUxLwkDsDvk+vBLzWtzkiL9WNPrE/Lkr8oz8hDtPqdLfAoP88uu7zsYk
    82  KVOYzejCLY1wIzJngpvBijLeoJPnA6GgHr/2X47s0i+0WshMaZoQZXodl9xii5JFxfQART
    83  XIZU6kizmYhIw6DaWxb9eik9bBOgqlvA48V0JEJPSSuuYSx/gcXljDQhCVWgpmuF9OGiZg
    84  u8yQIuchnnk9EilajKIWrxfdDul4mBQkB7sFzz8h36HKoLnZl56M8/ug+msSSmTMELDad1
    85  irpjo5c1p8QQggdZdozekWoPGidtu+4jBV0cI0y//I2hLFuCpMrA39Bo1dv4qm69KuW9Gq
    86  w2G7+wqxgUfrh9MBs/2aINceYmQDTm4WVC6Q6y1SYnRDHNux1wW+zzLDQ8uwRmEQdcnNHd
    87  MvN6fJlKrzpVkgK8Z9VpwGTtOKQIE63Ja4RIeGVzpJ9kp/Nnrtw6UPIp5KJWOCH54SH2zi
    88  HnpqFDatbTlKroaMPRi0TOiDYnhywQAAAAMBAAEAAAGAYycx4oEhp55Zz1HijblxnsEmQ8
    89  kbbH1pV04fdm7HTxFis0Qu8PVIp5JxNFiWWunnQ1Z5MgI23G9WT+XST4+RpwXBCLWGv9xu
    90  UsGOPpqUC/FdUiZf9MXBIxYgRjJS3xORA1KzsnAQ2sclb2I+B1pEl4d9yQWJesvQ25xa2H
    91  Utzej/LgWkrk/ogSGRl6ZNImj/421wc0DouGyP+gUgtATt0/jT3LrlmAqUVCXVqssLYH2O
    92  r9JTuGUibBJEW2W/c0lsM0jaHa5bGAdL3nhDuF1Q6KFB87mZoNw8c2znYoTzQ3FyWtIEZI
    93  V/9oWrkS7V6242SKSR9tJoEzK0jtrKC/FZwBiI4hPcwoqY6fZbT1701i/n50xWEfEUOLVm
    94  d6VqNKyAbIaZIPN0qfZuD+xdrHuM3V6k/rgFxGl4XTrp/N4AsruiQs0nRQKNTw3fHE0zPq
    95  UTxSeMvjywRCepxhBFCNh8NHydapclHtEPEGdTVHohL3krJehstPO/IuRyKLfSVtL1AAAA
    96  wQCmGA8k+uW6mway9J3jp8mlMhhp3DCX6DAcvalbA/S5OcqMyiTM3c/HD5OJ6OYFDldcqu
    97  MPEgLRL2HfxL29LsbQSzjyOIrfp5PLJlo70P5lXS8u2QPbo4/KQJmQmsIX18LDyU2zRtNA
    98  C2WfBiHSZV+guLhmHms9S5gQYKt2T5OnY/W0tmnInx9lmFCMC+XKS1iSQ2o433IrtCPQJp
    99  IXZd59OQpO9QjJABgJIDtXxFIXt45qpXduDPJuggrhg81stOwAAADBAPX73u/CY+QUPts+
   100  LV185Z4mZ2y+qu2ZMCAU3BnpHktGZZ1vFN1Xq9o8KdnuPZ+QJRdO8eKMWpySqrIdIbTYLm
   101  9nXmVH0uNECIEAvdU+wgKeR+BSHxCRVuTF4YSygmNadgH/z+oRWLgOblGo2ywFBoXsIAKQ
   102  paNu1MFGRUmhz67+dcpkkBUDRU9loAgBKexMo8D9vkR0YiHLOUjCrtmEZRNm0YRZt0gQhD
   103  ZSD1fOH0fZDcCVNpGP2zqAKos4EGLnkwAAAMEAy/AuLtPKA2u9oCA8e18ZnuQRAi27FBVU
   104  rU2D7bMg1eS0IakG8v0gE9K6WdYzyArY1RoKB3ZklK5VmJ1cOcWc2x3Ejc5jcJgc8cC6lZ
   105  wwjpE8HfWL1kIIYgPdcexqFc+l6MdgH6QMKU3nLg1LsM4v5FEldtk/2dmnw620xnFfstpF
   106  VxSZNdKrYfM/v9o6sRaDRqSfH1dG8BvkUxPznTAF+JDxBENcKXYECcq9f6dcl1w5IEnNTD
   107  Wry/EKQvgvOUjbAAAAFG90aGVyLXRlc3RAcmVrb3IuZGV2AQIDBAUG
   108  -----END OPENSSH PRIVATE KEY-----
   109  `
   110  	otherSSHPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDD9YJJYL1MS8JA7A75PrwS81rc5Ii/VjT6xPy5K/KM/IQ7T6nS3wKD/PLru87GJClTmM3owi2NcCMyZ4KbwYoy3qCT5wOhoB6/9l+O7NIvtFrITGmaEGV6HZfcYouSRcX0AEU1yGVOpIs5mISMOg2lsW/XopPWwToKpbwOPFdCRCT0krrmEsf4HF5Yw0IQlVoKZrhfThomYLvMkCLnIZ55PRIpWoyiFq8X3Q7peJgUJAe7Bc8/Id+hyqC52ZeejPP7oPprEkpkzBCw2ndYq6Y6OXNafEEIIHWXaM3pFqDxonbbvuIwVdHCNMv/yNoSxbgqTKwN/QaNXb+KpuvSrlvRqsNhu/sKsYFH64fTAbP9miDXHmJkA05uFlQukOstUmJ0QxzbsdcFvs8yw0PLsEZhEHXJzR3TLzenyZSq86VZICvGfVacBk7TikCBOtyWuESHhlc6SfZKfzZ67cOlDyKeSiVjgh+eEh9s4h56ahQ2rW05Sq6GjD0YtEzog2J4csE= other-test@rekor.dev
   111  `
   112  
   113  	// Generated with ssh-keygen -C test@rekor.dev -t ed25519 -f id_ed25519
   114  	ed25519PrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
   115  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
   116  QyNTUxOQAAACBB45zRHxPPFtabwS3Vd6Lb9vMe+tIHZj2qN5VQ+bgLfQAAAJgyRa3cMkWt
   117  3AAAAAtzc2gtZWQyNTUxOQAAACBB45zRHxPPFtabwS3Vd6Lb9vMe+tIHZj2qN5VQ+bgLfQ
   118  AAAED7y4N/DsVnRQiBZNxEWdsJ9RmbranvtQ3X9jnb6gFed0HjnNEfE88W1pvBLdV3otv2
   119  8x760gdmPao3lVD5uAt9AAAADnRlc3RAcmVrb3IuZGV2AQIDBAUGBw==
   120  -----END OPENSSH PRIVATE KEY-----
   121  `
   122  	ed25519PublicKey = `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEHjnNEfE88W1pvBLdV3otv28x760gdmPao3lVD5uAt9 test@rekor.dev
   123  `
   124  )
   125  
   126  func TestFromOpenSSH(t *testing.T) {
   127  	for _, tt := range []struct {
   128  		name string
   129  		pub  string
   130  		priv string
   131  	}{
   132  		{
   133  			name: "rsa",
   134  			pub:  sshPublicKey,
   135  			priv: sshPrivateKey,
   136  		},
   137  		{
   138  			name: "ed25519",
   139  			pub:  ed25519PublicKey,
   140  			priv: ed25519PrivateKey,
   141  		},
   142  	} {
   143  		if _, err := exec.LookPath("ssh-keygen"); err != nil {
   144  			t.Skip("skip TestFromOpenSSH: missing ssh-keygen in PATH")
   145  		}
   146  		t.Run(tt.name, func(t *testing.T) {
   147  			tt := tt
   148  
   149  			// Test that a signature from the cli can validate here.
   150  			td := t.TempDir()
   151  
   152  			data := []byte("hello, ssh world")
   153  			dataPath := write(t, []byte(data), td, "data")
   154  
   155  			privPath := write(t, []byte(tt.priv), td, "id")
   156  			write(t, []byte(tt.pub), td, "id.pub")
   157  
   158  			sigPath := dataPath + ".sig"
   159  			run(t, nil, "ssh-keygen", "-Y", "sign", "-n", "file", "-f", privPath, dataPath)
   160  
   161  			sigBytes, err := os.ReadFile(sigPath)
   162  			if err != nil {
   163  				t.Fatal(err)
   164  			}
   165  			if err := Verify(bytes.NewReader(data), sigBytes, []byte(tt.pub)); err != nil {
   166  				t.Error(err)
   167  			}
   168  
   169  			// It should not verify if we check against another public key
   170  			if err := Verify(bytes.NewReader(data), sigBytes, []byte(otherSSHPublicKey)); err == nil {
   171  				t.Error("expected error with incorrect key")
   172  			}
   173  
   174  			// It should not verify if the data is tampered
   175  			if err := Verify(strings.NewReader("bad data"), sigBytes, []byte(sshPublicKey)); err == nil {
   176  				t.Error("expected error with incorrect data")
   177  			}
   178  		})
   179  	}
   180  }
   181  
   182  func TestToOpenSSH(t *testing.T) {
   183  	for _, tt := range []struct {
   184  		name string
   185  		pub  string
   186  		priv string
   187  	}{
   188  		{
   189  			name: "rsa",
   190  			pub:  sshPublicKey,
   191  			priv: sshPrivateKey,
   192  		},
   193  		{
   194  			name: "ed25519",
   195  			pub:  ed25519PublicKey,
   196  			priv: ed25519PrivateKey,
   197  		},
   198  	} {
   199  		if _, err := exec.LookPath("ssh-keygen"); err != nil {
   200  			t.Skip("skip TestToOpenSSH: missing ssh-keygen in PATH")
   201  		}
   202  		t.Run(tt.name, func(t *testing.T) {
   203  			tt := tt
   204  			// Test that a signature from here can validate in the CLI.
   205  			td := t.TempDir()
   206  
   207  			data := []byte("hello, ssh world")
   208  			write(t, []byte(data), td, "data")
   209  
   210  			armored, err := Sign(tt.priv, bytes.NewReader(data))
   211  			if err != nil {
   212  				t.Fatal(err)
   213  			}
   214  
   215  			sigPath := write(t, []byte(armored), td, "oursig")
   216  
   217  			// Create an allowed_signers file with two keys to check against.
   218  			allowedSigner := "test@rekor.dev " + tt.pub + "\n"
   219  			allowedSigner += "othertest@rekor.dev " + otherSSHPublicKey + "\n"
   220  			allowedSigners := write(t, []byte(allowedSigner), td, "allowed_signer")
   221  
   222  			// We use the correct principal here so it should work.
   223  			run(t, data, "ssh-keygen", "-Y", "verify", "-f", allowedSigners,
   224  				"-I", "test@rekor.dev", "-n", "file", "-s", sigPath)
   225  
   226  			// Just to be sure, check against the other public key as well.
   227  			runErr(t, data, "ssh-keygen", "-Y", "verify", "-f", allowedSigners,
   228  				"-I", "othertest@rekor.dev", "-n", "file", "-s", sigPath)
   229  
   230  			// It should error if we run it against other data
   231  			data = []byte("other data!")
   232  			runErr(t, data, "ssh-keygen", "-Y", "check-novalidate", "-n", "file", "-s", sigPath)
   233  		})
   234  	}
   235  }
   236  
   237  func TestRoundTrip(t *testing.T) {
   238  	data := []byte("my good data to be signed!")
   239  
   240  	// Create one extra signature for all the tests.
   241  	otherSig, err := Sign(otherSSHPrivateKey, bytes.NewReader(data))
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	for _, tt := range []struct {
   247  		name string
   248  		pub  string
   249  		priv string
   250  	}{
   251  		{
   252  			name: "rsa",
   253  			pub:  sshPublicKey,
   254  			priv: sshPrivateKey,
   255  		},
   256  		{
   257  			name: "ed25519",
   258  			pub:  ed25519PublicKey,
   259  			priv: ed25519PrivateKey,
   260  		},
   261  	} {
   262  		t.Run(tt.name, func(t *testing.T) {
   263  			tt := tt
   264  			sig, err := Sign(tt.priv, bytes.NewReader(data))
   265  			if err != nil {
   266  				t.Fatal(err)
   267  			}
   268  
   269  			// Check the signature against that data and public key
   270  			if err := Verify(bytes.NewReader(data), sig, []byte(tt.pub)); err != nil {
   271  				t.Error(err)
   272  			}
   273  
   274  			// Now check it against invalid data.
   275  			if err := Verify(strings.NewReader("invalid data!"), sig, []byte(tt.pub)); err == nil {
   276  				t.Error("expected error!")
   277  			}
   278  
   279  			// Now check it against the wrong key.
   280  			if err := Verify(bytes.NewReader(data), sig, []byte(otherSSHPublicKey)); err == nil {
   281  				t.Error("expected error!")
   282  			}
   283  
   284  			// Now check it against an invalid signature data.
   285  			if err := Verify(bytes.NewReader(data), []byte("invalid signature!"), []byte(tt.pub)); err == nil {
   286  				t.Error("expected error!")
   287  			}
   288  
   289  			// Once more, use the wrong signature and check it against the original (wrong public key)
   290  			if err := Verify(bytes.NewReader(data), otherSig, []byte(tt.pub)); err == nil {
   291  				t.Error("expected error!")
   292  			}
   293  			// It should work against the correct public key.
   294  			if err := Verify(bytes.NewReader(data), otherSig, []byte(otherSSHPublicKey)); err != nil {
   295  				t.Error(err)
   296  			}
   297  		})
   298  	}
   299  
   300  }
   301  
   302  func write(t *testing.T, d []byte, fp ...string) string {
   303  	p := filepath.Join(fp...)
   304  	if err := os.WriteFile(p, d, 0600); err != nil {
   305  		t.Fatal(err)
   306  	}
   307  	return p
   308  }
   309  
   310  func run(t *testing.T, stdin []byte, args ...string) {
   311  	t.Helper()
   312  	/* #nosec */
   313  	cmd := exec.Command(args[0], args[1:]...)
   314  	cmd.Stdin = bytes.NewReader(stdin)
   315  	out, err := cmd.CombinedOutput()
   316  	t.Logf("cmd %v: %s", cmd, string(out))
   317  	if err != nil {
   318  		t.Fatal(err)
   319  	}
   320  }
   321  
   322  func runErr(t *testing.T, stdin []byte, args ...string) {
   323  	t.Helper()
   324  	/* #nosec */
   325  	cmd := exec.Command(args[0], args[1:]...)
   326  	cmd.Stdin = bytes.NewReader(stdin)
   327  	out, err := cmd.CombinedOutput()
   328  	t.Logf("cmd %v: %s", cmd, string(out))
   329  	if err == nil {
   330  		t.Fatal("expected error")
   331  	}
   332  }
   333  

View as plain text