...

Source file src/github.com/in-toto/in-toto-golang/in_toto/runlib_test.go

Documentation: github.com/in-toto/in-toto-golang/in_toto

     1  package in_toto
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"reflect"
    10  	"runtime"
    11  	"testing"
    12  
    13  	"github.com/secure-systems-lab/go-securesystemslib/dsse"
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  // Helper function checking whether running environment is Windows
    18  func testOSisWindows() bool {
    19  	os := runtime.GOOS
    20  	return os == "windows"
    21  }
    22  
    23  func TestRecordArtifact(t *testing.T) {
    24  	// Test successfully record one artifact
    25  	result, err := RecordArtifact("foo.tar.gz", []string{"sha256"}, testOSisWindows())
    26  	expected := map[string]interface{}{
    27  		"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
    28  	}
    29  	if !reflect.DeepEqual(result, expected) || err != nil {
    30  		t.Errorf("RecordArtifact returned '(%s, %s)', expected '(%s, nil)'",
    31  			result, err, expected)
    32  	}
    33  
    34  	// Test error by recording nonexistent artifact
    35  	result, err = RecordArtifact("file-does-not-exist", []string{"sha256"}, testOSisWindows())
    36  	if !os.IsNotExist(err) {
    37  		t.Errorf("RecordArtifact returned '(%s, %s)', expected '(nil, %s)'",
    38  			result, err, os.ErrNotExist)
    39  	}
    40  
    41  	result, err = RecordArtifact("foo.tar.gz", []string{"invalid"}, testOSisWindows())
    42  	if !errors.Is(err, ErrUnsupportedHashAlgorithm) {
    43  		t.Errorf("RecordArtifact returned '(%s, %s)', expected '(nil, %s)'", result, err, ErrUnsupportedHashAlgorithm)
    44  	}
    45  }
    46  
    47  // copy helper function for building more complex test cases
    48  // for our TestGitPathSpec
    49  func copy(src, dst string) (int64, error) {
    50  	sourceFileStat, err := os.Stat(src)
    51  	if err != nil {
    52  		return 0, err
    53  	}
    54  
    55  	if !sourceFileStat.Mode().IsRegular() {
    56  		return 0, fmt.Errorf("%s is not a regular file", src)
    57  	}
    58  
    59  	source, err := os.Open(src)
    60  	if err != nil {
    61  		return 0, err
    62  	}
    63  	defer source.Close()
    64  
    65  	destination, err := os.Create(dst)
    66  	if err != nil {
    67  		return 0, err
    68  	}
    69  	defer destination.Close()
    70  	nBytes, err := io.Copy(destination, source)
    71  	return nBytes, err
    72  }
    73  
    74  func TestGitPathSpec(t *testing.T) {
    75  	// Create a more complex test scenario via building subdirectories
    76  	// and copying existing files.
    77  	directoriesToBeCreated := []string{
    78  		"pathSpecTest",
    79  		"pathSpecTest/alpha",
    80  		"pathSpecTest/beta",
    81  		"pathSpecTest/alpha/charlie",
    82  	}
    83  	for _, v := range directoriesToBeCreated {
    84  		if err := os.Mkdir(v, 0700); err != nil {
    85  			t.Errorf("could not create tmpdir: %s", err)
    86  		}
    87  	}
    88  	filesToBeCreated := map[string]string{
    89  		"heidi.pub":  "pathSpecTest/heidi.pub",
    90  		"foo.tar.gz": "pathSpecTest/beta/foo.tar.gz",
    91  		"dan":        "pathSpecTest/alpha/charlie/dan",
    92  		"dan.pub":    "pathSpecTest/beta/dan.pub",
    93  	}
    94  	for k, v := range filesToBeCreated {
    95  		_, err := copy(k, v)
    96  		if err != nil {
    97  			t.Errorf("could not copy file: %s", err)
    98  		}
    99  	}
   100  
   101  	expected := map[string]interface{}{}
   102  	// Let's start our test in the test/data directory
   103  	result, err := RecordArtifacts([]string{"pathSpecTest"}, []string{"sha256"}, []string{
   104  		"*.pub",           // Match all .pub files (even the ones in subdirectories)
   105  		"beta/foo.tar.gz", // Match full path
   106  		"alpha/**",        // Match all directories and files beneath alpha
   107  	}, nil, testOSisWindows(), false)
   108  	if !reflect.DeepEqual(result, expected) {
   109  		t.Errorf("RecordArtifacts returned '(%s, %s)', expected '(%s, nil)'",
   110  			result, err, expected)
   111  	}
   112  
   113  	// clean up
   114  	err = os.RemoveAll("pathSpecTest")
   115  	if err != nil {
   116  		t.Errorf("could not clean up pathSpecTest directory: %s", err)
   117  	}
   118  }
   119  
   120  // TestSymlinkToFile checks if we can follow symlinks to a file
   121  // Note: Symlink files are invisible for InToto right now.
   122  // Therefore if we have a symlink like: foo.tar.gz.sym -> foo.tar.gz
   123  // We will only calculate the hash for for.tar.gz
   124  // The symlink will not be added to the list right now, nor will we calculate a checksum for it.
   125  func TestSymlinkToFile(t *testing.T) {
   126  	if err := os.Symlink("foo.tar.gz", "foo.tar.gz.sym"); err != nil {
   127  		t.Errorf("could not create a symlink: %s", err)
   128  	}
   129  
   130  	expected := map[string]interface{}{
   131  		"foo.tar.gz.sym": map[string]interface{}{
   132  			"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   133  		},
   134  	}
   135  	result, err := RecordArtifacts([]string{"foo.tar.gz.sym"}, []string{"sha256"}, nil, nil, testOSisWindows(), false)
   136  	if !reflect.DeepEqual(result, expected) {
   137  		t.Errorf("RecordArtifacts returned '(%s, %s)', expected '(%s, nil)'",
   138  			result, err, expected)
   139  	}
   140  
   141  	if err := os.Remove("foo.tar.gz.sym"); err != nil {
   142  		t.Errorf("could not remove foo.tar.gz.sym: %s", err)
   143  	}
   144  }
   145  
   146  // TestIndirectSymlinkCycles() tests for indirect symlink cycles in the form:
   147  // symTestA/linkToB -> symTestB and symTestB/linkToA -> symTestA
   148  func TestIndirectSymlinkCycles(t *testing.T) {
   149  	if err := os.Mkdir("symTestA", 0700); err != nil {
   150  		t.Errorf("could not create tmpdir: %s", err)
   151  	}
   152  	if err := os.Mkdir("symTestB", 0700); err != nil {
   153  		t.Errorf("could not create tmpdir: %s", err)
   154  	}
   155  
   156  	// we need to get the current working directory here, otherwise
   157  	// os.Symlink() will create a wrong symlink
   158  	dir, err := os.Getwd()
   159  	if err != nil {
   160  		t.Error(err)
   161  	}
   162  
   163  	linkB := filepath.FromSlash("symTestA/linkToB.sym")
   164  	linkA := filepath.FromSlash("symTestB/linkToA.sym")
   165  
   166  	if err := os.Symlink(dir+"/symTestA", linkA); err != nil {
   167  		t.Errorf("could not create a symlink: %s", err)
   168  	}
   169  	if err := os.Symlink(dir+"/symTestB", linkB); err != nil {
   170  		t.Errorf("could not create a symlink: %s", err)
   171  	}
   172  
   173  	// provoke "symlink cycle detected" error
   174  	_, err = RecordArtifacts([]string{"symTestA/linkToB.sym", "symTestB/linkToA.sym", "foo.tar.gz"}, []string{"sha256"}, nil, nil, testOSisWindows(), true)
   175  	if !errors.Is(err, ErrSymCycle) {
   176  		t.Errorf("we expected: %s, we got: %s", ErrSymCycle, err)
   177  	}
   178  
   179  	// make sure to clean up everything
   180  	if err := os.Remove("symTestA/linkToB.sym"); err != nil {
   181  		t.Errorf("could not remove path: %s", err)
   182  	}
   183  
   184  	if err := os.Remove("symTestB/linkToA.sym"); err != nil {
   185  		t.Errorf("could not remove path: %s", err)
   186  	}
   187  
   188  	if err := os.Remove("symTestA"); err != nil {
   189  		t.Errorf("could not remove path: %s", err)
   190  	}
   191  
   192  	if err := os.Remove("symTestB"); err != nil {
   193  		t.Errorf("could not remove path: %s", err)
   194  	}
   195  
   196  }
   197  
   198  // TestSymlinkToFolder checks if we are successfully following symlinks to folders
   199  func TestSymlinkToFolder(t *testing.T) {
   200  	if err := os.MkdirAll("symTest/symTest2", 0700); err != nil {
   201  		t.Errorf("could not create tmpdir: %s", err)
   202  	}
   203  
   204  	if err := os.Symlink("symTest/symTest2", "symTmpfile.sym"); err != nil {
   205  		t.Errorf("could not create a symlink: %s", err)
   206  	}
   207  
   208  	// create a filepath from slash, because otherwise
   209  	// our tests are going to fail, because the path matching will
   210  	// not work correctly on Windows
   211  	p := filepath.FromSlash("symTest/symTest2/symTmpfile")
   212  
   213  	if err := os.WriteFile(p, []byte("abc"), 0400); err != nil {
   214  		t.Errorf("could not write symTmpfile: %s", err)
   215  	}
   216  
   217  	result, err := RecordArtifacts([]string{"symTmpfile.sym"}, []string{"sha256"}, nil, nil, testOSisWindows(), true)
   218  	if err != nil {
   219  		t.Error(err)
   220  	}
   221  
   222  	expected := map[string]interface{}{
   223  		filepath.Join("symTmpfile.sym", "symTmpfile"): map[string]interface{}{
   224  			"sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
   225  		},
   226  	}
   227  
   228  	if !reflect.DeepEqual(result, expected) {
   229  		t.Errorf("RecordArtifacts returned '(%s, %s)', expected '(%s, nil)'",
   230  			result, err, expected)
   231  	}
   232  
   233  	// make sure to clean up everything
   234  	if err := os.Remove("symTest/symTest2/symTmpfile"); err != nil {
   235  		t.Errorf("could not remove path symTest/symTest2/symTmpfile: %s", err)
   236  	}
   237  
   238  	if err := os.Remove("symTmpfile.sym"); err != nil {
   239  		t.Errorf("could not remove path symTest/symTest2/symTmpfile.sym: %s", err)
   240  	}
   241  
   242  	if err := os.Remove("symTest/symTest2"); err != nil {
   243  		t.Errorf("could not remove path symTest/symTest2: %s", err)
   244  	}
   245  
   246  	if err := os.Remove("symTest/"); err != nil {
   247  		t.Errorf("could not remove path symTest: %s", err)
   248  	}
   249  }
   250  
   251  // This test provokes a symlink cycle
   252  func TestSymlinkCycle(t *testing.T) {
   253  	if err := os.Mkdir("symlinkCycle/", 0700); err != nil {
   254  		t.Errorf("could not create tmpdir: %s", err)
   255  	}
   256  
   257  	// we need to get the current working directory here, otherwise
   258  	// os.Symlink() will create a wrong symlink
   259  	dir, err := os.Getwd()
   260  	if err != nil {
   261  		t.Error(err)
   262  	}
   263  	// create a cycle ./symlinkCycle/symCycle.sym -> ./symlinkCycle/
   264  	if err := os.Symlink(dir+"/symlinkCycle", "symlinkCycle/symCycle.sym"); err != nil {
   265  		t.Errorf("could not create a symlink: %s", err)
   266  	}
   267  
   268  	// provoke "symlink cycle detected" error
   269  	_, err = RecordArtifacts([]string{"symlinkCycle/symCycle.sym", "foo.tar.gz"}, []string{"sha256"}, nil, nil, testOSisWindows(), true)
   270  	if !errors.Is(err, ErrSymCycle) {
   271  		t.Errorf("we expected: %s, we got: %s", ErrSymCycle, err)
   272  	}
   273  
   274  	if err := os.Remove("symlinkCycle/symCycle.sym"); err != nil {
   275  		t.Errorf("could not remove path symlinkCycle/symCycle.sym: %s", err)
   276  	}
   277  
   278  	if err := os.Remove("symlinkCycle"); err != nil {
   279  		t.Errorf("could not remove path symlinkCycle: %s", err)
   280  	}
   281  }
   282  
   283  func TestRecordArtifacts(t *testing.T) {
   284  	// Test successfully record multiple artifacts including temporary subdir
   285  	if err := os.Mkdir("tmpdir", 0700); err != nil {
   286  		t.Errorf("could not create tmpdir: %s", err)
   287  	}
   288  	if err := os.WriteFile("tmpdir/tmpfile", []byte("abc"), 0400); err != nil {
   289  		t.Errorf("could not write tmpfile: %s", err)
   290  	}
   291  	result, err := RecordArtifacts([]string{"foo.tar.gz",
   292  		"tmpdir/tmpfile"}, []string{"sha256"}, nil, []string{"tmpdir/"}, testOSisWindows(), false)
   293  	expected := map[string]interface{}{
   294  		"foo.tar.gz": map[string]interface{}{
   295  			"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   296  		},
   297  		"tmpfile": map[string]interface{}{
   298  			"sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
   299  		},
   300  	}
   301  	if !reflect.DeepEqual(result, expected) {
   302  		t.Errorf("RecordArtifacts returned '(%s, %s)', expected '(%s, nil)'",
   303  			result, err, expected)
   304  	}
   305  	// Test duplicated artifact after left strip
   306  	if err := os.WriteFile("tmpdir/foo.tar.gz", []byte("abc"), 0400); err != nil {
   307  		t.Errorf("could not write tmpfile: %s", err)
   308  	}
   309  	_, err = RecordArtifacts([]string{"foo.tar.gz",
   310  		"tmpdir/foo.tar.gz"}, []string{"sha256"}, nil, []string{"tmpdir/"}, testOSisWindows(), false)
   311  	if err == nil {
   312  		t.Error("duplicated path error expected")
   313  	}
   314  
   315  	if err := os.RemoveAll("tmpdir"); err != nil {
   316  		t.Errorf("could not remove tmpdir: %s", err)
   317  	}
   318  
   319  	// Test error by recording nonexistent artifact
   320  	result, err = RecordArtifacts([]string{"file-does-not-exist"}, []string{"sha256"}, nil, nil, testOSisWindows(), false)
   321  	if !os.IsNotExist(err) {
   322  		t.Errorf("RecordArtifacts returned '(%s, %s)', expected '(nil, %s)'",
   323  			result, err, os.ErrNotExist)
   324  	}
   325  }
   326  
   327  func TestWaitErrToExitCode(t *testing.T) {
   328  	// TODO: Find way to test/mock ExitError
   329  	// Test exit code from error assessment
   330  	parameters := []error{
   331  		nil,
   332  		errors.New(""),
   333  		// &exec.ExitError{ProcessState: &os.ProcessState},
   334  	}
   335  	expected := []int{
   336  		0,
   337  		-1,
   338  		// -1,
   339  	}
   340  
   341  	for i := 0; i < len(parameters); i++ {
   342  		result := waitErrToExitCode(parameters[i])
   343  		if result != expected[i] {
   344  			t.Errorf("waitErrToExitCode returned %d, expected %d",
   345  				result, expected[i])
   346  		}
   347  	}
   348  }
   349  
   350  func TestRunCommand(t *testing.T) {
   351  	// Successfully run command and return metadata
   352  	parameters := [][]string{
   353  		{"sh", "-c", "true"},
   354  		{"sh", "-c", "false"},
   355  		{"sh", "-c", "printf out"},
   356  		{"sh", "-c", "printf err >&2"},
   357  	}
   358  	expected := []map[string]interface{}{
   359  		{"return-value": float64(0), "stdout": "", "stderr": ""},
   360  		{"return-value": float64(1), "stdout": "", "stderr": ""},
   361  		{"return-value": float64(0), "stdout": "out", "stderr": ""},
   362  		{"return-value": float64(0), "stdout": "", "stderr": "err"},
   363  	}
   364  	for i := 0; i < len(parameters); i++ {
   365  		result, err := RunCommand(parameters[i], "")
   366  		if !reflect.DeepEqual(result, expected[i]) || err != nil {
   367  			t.Errorf("RunCommand returned '(%s, %s)', expected '(%s, nil)'",
   368  				result, err, expected[i])
   369  		}
   370  	}
   371  
   372  	// Fail run command
   373  	result, err := RunCommand([]string{"command-does-not-exist"}, "")
   374  	if result != nil || err == nil {
   375  		t.Errorf("RunCommand returned '(%s, %s)', expected '(nil, *exec.Error)'",
   376  			result, err)
   377  	}
   378  }
   379  
   380  func TestRunCommandErrors(t *testing.T) {
   381  	tables := []struct {
   382  		CmdArgs       []string
   383  		RunDir        string
   384  		ExpectedError error
   385  	}{
   386  		{nil, "", ErrEmptyCommandArgs},
   387  		{[]string{}, "", ErrEmptyCommandArgs},
   388  	}
   389  	for _, table := range tables {
   390  		_, err := RunCommand(table.CmdArgs, table.RunDir)
   391  		if !errors.Is(err, ErrEmptyCommandArgs) {
   392  			t.Errorf("RunCommand did not provoke expected error. Got: %s, want: %s", err, ErrEmptyCommandArgs)
   393  		}
   394  	}
   395  }
   396  
   397  func TestInTotoRun(t *testing.T) {
   398  	// Successfully run InTotoRun
   399  	linkName := "Name"
   400  
   401  	var validKey Key
   402  	if err := validKey.LoadKey("carol", "ed25519", []string{"sha256", "sha512"}); err != nil {
   403  		t.Error(err)
   404  	}
   405  
   406  	tablesCorrect := []struct {
   407  		materialPaths  []string
   408  		productPaths   []string
   409  		cmdArgs        []string
   410  		key            Key
   411  		hashAlgorithms []string
   412  		useDSSE        bool
   413  		result         Metadata
   414  	}{
   415  		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, []string{"sh", "-c", "printf out; printf err >&2"}, validKey, []string{"sha256"}, false, &Metablock{
   416  			Signed: Link{
   417  				Name: linkName,
   418  				Type: "link",
   419  				Materials: map[string]interface{}{
   420  					"alice.pub": map[string]interface{}{
   421  						"sha256": "f051e8b561835b7b2aa7791db7bc72f2613411b0b7d428a0ac33d45b8c518039",
   422  					},
   423  				},
   424  				Products: map[string]interface{}{
   425  					"foo.tar.gz": map[string]interface{}{
   426  						"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   427  					},
   428  				},
   429  				ByProducts: map[string]interface{}{
   430  					"return-value": float64(0), "stdout": "out", "stderr": "err",
   431  				},
   432  				Command:     []string{"sh", "-c", "printf out; printf err >&2"},
   433  				Environment: map[string]interface{}{},
   434  			},
   435  			Signatures: []Signature{{
   436  				KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   437  				Sig:   "71dfec1af747d02f6463d4baf3bb2c1d903c107470be86c12349433f780b1030e5ca36a10ee5c5d74de16344fe16b459154fd2be05a58fb556dff934d6682403",
   438  			}},
   439  		},
   440  		},
   441  		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, []string{}, validKey, []string{"sha256"}, false, &Metablock{
   442  			Signed: Link{
   443  				Name: linkName,
   444  				Type: "link",
   445  				Materials: map[string]interface{}{
   446  					"alice.pub": map[string]interface{}{
   447  						"sha256": "f051e8b561835b7b2aa7791db7bc72f2613411b0b7d428a0ac33d45b8c518039",
   448  					},
   449  				},
   450  				Products: map[string]interface{}{
   451  					"foo.tar.gz": map[string]interface{}{
   452  						"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   453  					},
   454  				},
   455  				ByProducts:  map[string]interface{}{},
   456  				Command:     []string{},
   457  				Environment: map[string]interface{}{},
   458  			},
   459  			Signatures: []Signature{{
   460  				KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   461  				Sig:   "f4a2d468965d595b4d29615fb2083ef7ac22a948e1530925612d73ba580ce9765d93db7b7ed1b9755d96f13a6a1e858c64693c2f7adcb311afb28cb57fbadc0c",
   462  			}},
   463  		},
   464  		},
   465  		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, []string{}, validKey, []string{"sha256"}, true, &Envelope{
   466  			envelope: &dsse.Envelope{
   467  				Payload:     "eyJfdHlwZSI6ImxpbmsiLCJieXByb2R1Y3RzIjp7fSwiY29tbWFuZCI6W10sImVudmlyb25tZW50Ijp7fSwibWF0ZXJpYWxzIjp7ImFsaWNlLnB1YiI6eyJzaGEyNTYiOiJmMDUxZThiNTYxODM1YjdiMmFhNzc5MWRiN2JjNzJmMjYxMzQxMWIwYjdkNDI4YTBhYzMzZDQ1YjhjNTE4MDM5In19LCJuYW1lIjoiTmFtZSIsInByb2R1Y3RzIjp7ImZvby50YXIuZ3oiOnsic2hhMjU2IjoiNTI5NDdjYjc4YjkxYWQwMWZlODFjZDZhZWY0MmQxZjY4MTdlOTJiOWU2OTM2YzFlNWFhYmI3Yzk4NTE0ZjM1NSJ9fX0=",
   468  				PayloadType: PayloadType,
   469  				Signatures: []dsse.Signature{{
   470  					KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   471  					Sig:   "XgNp1Q5N/ivFxNyuUNHcjOarMIj3WXZpb00/ZVy2pxdiAeOZYKpJkXPa7wRAM5auuwrVph9TrwoJQuDpJrZaCw==",
   472  				}},
   473  			},
   474  		}},
   475  	}
   476  
   477  	for _, table := range tablesCorrect {
   478  		result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, table.useDSSE)
   479  		if table.useDSSE {
   480  			assert.Equal(t, table.result.(*Envelope).envelope, result.(*Envelope).envelope, fmt.Sprintf("InTotoRun returned '(%s, %s)', expected '(%s, nil)'", result, err, table.result))
   481  		} else {
   482  			assert.True(t, reflect.DeepEqual(result.(*Metablock), table.result.(*Metablock)), fmt.Sprintf("InTotoRun returned '(%s, %s)', expected '(%s, nil)'", result, err, table.result))
   483  		}
   484  
   485  		if result != nil {
   486  			if err := result.Dump(linkName + ".link"); err != nil {
   487  				t.Errorf("error while dumping link metablock to file")
   488  			}
   489  			loadedResult, err := LoadMetadata(linkName + ".link")
   490  			if err != nil {
   491  				t.Errorf("error while loading link metablock from file")
   492  			}
   493  			if table.useDSSE {
   494  				assert.Equal(t, result.(*Envelope).envelope, loadedResult.(*Envelope).envelope, fmt.Sprintf("dump and loading of signed Link failed. Loaded result: '%s', dumped result '%s'", loadedResult, result))
   495  			} else {
   496  				assert.True(t, reflect.DeepEqual(loadedResult, result), fmt.Sprintf("dump and loading of signed Link failed. Loaded result: '%s', dumped result '%s'", loadedResult, result))
   497  			}
   498  
   499  			if err := os.Remove(linkName + ".link"); err != nil {
   500  				t.Errorf("removing created link file failed")
   501  			}
   502  		}
   503  	}
   504  
   505  	// Run InToToRun with errors
   506  	tablesInvalid := []struct {
   507  		materialPaths  []string
   508  		productPaths   []string
   509  		cmdArgs        []string
   510  		key            Key
   511  		hashAlgorithms []string
   512  	}{
   513  		{[]string{"material-does-not-exist"}, []string{""}, []string{"sh", "-c", "printf test"}, Key{}, []string{"sha256"}},
   514  		{[]string{"demo.layout"}, []string{"product-does-not-exist"}, []string{"sh", "-c", "printf test"}, Key{}, []string{"sha256"}},
   515  		{[]string{""}, []string{"/invalid-path/"}, []string{"sh", "-c", "printf test"}, Key{}, []string{"sha256"}},
   516  		{[]string{}, []string{}, []string{"command-does-not-exist"}, Key{}, []string{"sha256"}},
   517  		{[]string{"demo.layout"}, []string{"foo.tar.gz"}, []string{"sh", "-c", "printf out; printf err >&2"}, Key{
   518  			KeyID:               "this-is-invalid",
   519  			KeyIDHashAlgorithms: nil,
   520  			KeyType:             "",
   521  			KeyVal:              KeyVal{},
   522  			Scheme:              "",
   523  		}, []string{"sha256"}},
   524  	}
   525  
   526  	for _, table := range tablesInvalid {
   527  		result, err := InTotoRun(linkName, "", table.materialPaths, table.productPaths, table.cmdArgs, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, false)
   528  		if err == nil {
   529  			t.Errorf("InTotoRun returned '(%s, %s)', expected error",
   530  				result, err)
   531  		}
   532  	}
   533  }
   534  
   535  func TestInTotoRecord(t *testing.T) {
   536  	// Successfully run InTotoRecordStart
   537  	linkName := "Name"
   538  
   539  	var validKey Key
   540  	if err := validKey.LoadKey("carol", "ed25519", []string{"sha256", "sha512"}); err != nil {
   541  		t.Error(err)
   542  	}
   543  
   544  	tablesCorrect := []struct {
   545  		materialPaths  []string
   546  		productPaths   []string
   547  		key            Key
   548  		hashAlgorithms []string
   549  		useDSSE        bool
   550  		startResult    Metadata
   551  		stopResult     Metadata
   552  	}{
   553  		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, validKey, []string{"sha256"}, false, &Metablock{
   554  			Signed: Link{
   555  				Name: linkName,
   556  				Type: "link",
   557  				Materials: map[string]interface{}{
   558  					"alice.pub": map[string]interface{}{
   559  						"sha256": "f051e8b561835b7b2aa7791db7bc72f2613411b0b7d428a0ac33d45b8c518039",
   560  					},
   561  				},
   562  				Products:    map[string]interface{}{},
   563  				ByProducts:  map[string]interface{}{},
   564  				Command:     []string{},
   565  				Environment: map[string]interface{}{},
   566  			},
   567  			Signatures: []Signature{{
   568  				KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   569  				Sig:   "f02db2e08d065840f266df850eaef7cfb5364bbe1808708945eb45373f4757cfe70c86f7ad5e4d5f746d41489410e0407921b4480788cfae5a7d695e3aa62f06",
   570  			}},
   571  		}, &Metablock{
   572  			Signed: Link{
   573  				Name: linkName,
   574  				Type: "link",
   575  				Materials: map[string]interface{}{
   576  					"alice.pub": map[string]interface{}{
   577  						"sha256": "f051e8b561835b7b2aa7791db7bc72f2613411b0b7d428a0ac33d45b8c518039",
   578  					},
   579  				},
   580  				Products: map[string]interface{}{
   581  					"foo.tar.gz": map[string]interface{}{
   582  						"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   583  					},
   584  				},
   585  				ByProducts:  map[string]interface{}{},
   586  				Command:     []string{},
   587  				Environment: map[string]interface{}{},
   588  			},
   589  			Signatures: []Signature{{
   590  				KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   591  				Sig:   "f4a2d468965d595b4d29615fb2083ef7ac22a948e1530925612d73ba580ce9765d93db7b7ed1b9755d96f13a6a1e858c64693c2f7adcb311afb28cb57fbadc0c",
   592  			}},
   593  		},
   594  		},
   595  		{[]string{"alice.pub"}, []string{"foo.tar.gz"}, validKey, []string{"sha256"}, true, &Envelope{
   596  			envelope: &dsse.Envelope{
   597  				PayloadType: PayloadType,
   598  				Payload:     "eyJfdHlwZSI6ImxpbmsiLCJieXByb2R1Y3RzIjp7fSwiY29tbWFuZCI6W10sImVudmlyb25tZW50Ijp7fSwibWF0ZXJpYWxzIjp7ImFsaWNlLnB1YiI6eyJzaGEyNTYiOiJmMDUxZThiNTYxODM1YjdiMmFhNzc5MWRiN2JjNzJmMjYxMzQxMWIwYjdkNDI4YTBhYzMzZDQ1YjhjNTE4MDM5In19LCJuYW1lIjoiTmFtZSIsInByb2R1Y3RzIjp7fX0=",
   599  				Signatures: []dsse.Signature{{
   600  					KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   601  					Sig:   "1u46q3nVmmvqKz/exUviEBPyfRndXwxouG+Jk1GadqKvhyfZv8to//xLPQWC+zy4bPQTicOp1yIBHqFO0bNeBw==",
   602  				}},
   603  			},
   604  		}, &Envelope{
   605  			envelope: &dsse.Envelope{
   606  				PayloadType: PayloadType,
   607  				Payload:     "eyJfdHlwZSI6ImxpbmsiLCJieXByb2R1Y3RzIjp7fSwiY29tbWFuZCI6W10sImVudmlyb25tZW50Ijp7fSwibWF0ZXJpYWxzIjp7ImFsaWNlLnB1YiI6eyJzaGEyNTYiOiJmMDUxZThiNTYxODM1YjdiMmFhNzc5MWRiN2JjNzJmMjYxMzQxMWIwYjdkNDI4YTBhYzMzZDQ1YjhjNTE4MDM5In19LCJuYW1lIjoiTmFtZSIsInByb2R1Y3RzIjp7ImZvby50YXIuZ3oiOnsic2hhMjU2IjoiNTI5NDdjYjc4YjkxYWQwMWZlODFjZDZhZWY0MmQxZjY4MTdlOTJiOWU2OTM2YzFlNWFhYmI3Yzk4NTE0ZjM1NSJ9fX0=",
   608  				Signatures: []dsse.Signature{{
   609  					KeyID: "be6371bc627318218191ce0780fd3183cce6c36da02938a477d2e4dfae1804a6",
   610  					Sig:   "XgNp1Q5N/ivFxNyuUNHcjOarMIj3WXZpb00/ZVy2pxdiAeOZYKpJkXPa7wRAM5auuwrVph9TrwoJQuDpJrZaCw==",
   611  				}},
   612  			},
   613  		},
   614  		},
   615  	}
   616  
   617  	for _, table := range tablesCorrect {
   618  		result, err := InTotoRecordStart(linkName, table.materialPaths, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, table.useDSSE)
   619  		assert.Nil(t, err, "unexpected error while running record start")
   620  		if table.useDSSE {
   621  			assert.Equal(t, table.startResult.(*Envelope).envelope, result.(*Envelope).envelope, "result from record start did not match expected result")
   622  		} else {
   623  			assert.Equal(t, table.startResult.(*Metablock), result.(*Metablock), "result from record start did not match expected result")
   624  		}
   625  		stopResult, err := InTotoRecordStop(result, table.productPaths, table.key, table.hashAlgorithms, nil, nil, testOSisWindows(), false, table.useDSSE)
   626  		assert.Nil(t, err, "unexpected error while running record stop")
   627  		if table.useDSSE {
   628  			assert.Equal(t, table.stopResult.(*Envelope).envelope, stopResult.(*Envelope).envelope, "result from record stop did not match expected result")
   629  		} else {
   630  			assert.Equal(t, table.stopResult.(*Metablock), stopResult.(*Metablock), "result from record stop did not match expected result")
   631  		}
   632  	}
   633  }
   634  
   635  // TestRecordArtifactWithBlobs ensures that we calculate the same hash for blobs
   636  func TestRecordArtifactWithBlobs(t *testing.T) {
   637  	type args struct {
   638  		path           string
   639  		hashAlgorithms []string
   640  	}
   641  	tests := []struct {
   642  		name    string
   643  		args    args
   644  		want    map[string]interface{}
   645  		wantErr error
   646  	}{
   647  		{
   648  			name: "test binary blob without line normalization segments",
   649  			args: args{
   650  				path:           "foo.tar.gz",
   651  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   652  			},
   653  			want: map[string]interface{}{"sha256": "52947cb78b91ad01fe81cd6aef42d1f6817e92b9e6936c1e5aabb7c98514f355",
   654  				"sha384": "ce17464027a7d7c15b15032b404fc76fdbadfa1fa566d7f7747020df2542a293b3098873a98dbbda6e461f7767b8ff6c",
   655  				"sha512": "bb040966a5a6aefb646909f636f7f99c9e16b684a1f0e83a87dc30c3ab4d9dec2f9b0091d8be74bbc78ba29cb0c2dd027c223579028cf9822b0bccc49d493a6d"},
   656  			wantErr: nil,
   657  		},
   658  		{
   659  			name: "test binary blob with windows-like line breaks as byte segments",
   660  			args: args{
   661  				path:           "helloworld",
   662  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   663  			},
   664  			want: map[string]interface{}{"sha256": "fd895747460401ca62d81f310538110734ff5401f8ef86c3ab27168598225db8",
   665  				"sha384": "ddc3ac40ca8d04929e13c42d555a5a6774d35bfac9e2f4cde5847ab3f12f36831faa3baf1b33922b53d288b352ae4b9a",
   666  				"sha512": "46f0e37e72879843f95ddecc4d511c9ba90241c34b471c2f2caca2784abe185da50ddc5252562b2a911b7cfedafa3e878f0e6b7aa843c136915da5306061e501"},
   667  			wantErr: nil,
   668  		},
   669  	}
   670  	for _, tt := range tests {
   671  		t.Run(tt.name, func(t *testing.T) {
   672  			got, err := RecordArtifact(tt.args.path, tt.args.hashAlgorithms, false)
   673  			if err != tt.wantErr {
   674  				t.Errorf("RecordArtifact() error = %v, wantErr %v", err, tt.wantErr)
   675  				return
   676  			}
   677  			if !reflect.DeepEqual(got, tt.want) {
   678  				t.Errorf("RecordArtifact() got = %v, want %v", got, tt.want)
   679  			}
   680  		})
   681  	}
   682  }
   683  
   684  // Copy of TestRecordArtifact and TestRecordArtifactWithBlobs with lineNormalization parameter set as true.
   685  // Need to be changed when line normalization is properly implemented.
   686  func TestLineNormalizationFlag(t *testing.T) {
   687  	type args struct {
   688  		path           string
   689  		hashAlgorithms []string
   690  	}
   691  	tests := []struct {
   692  		name    string
   693  		args    args
   694  		wantErr error
   695  	}{
   696  		{
   697  			name: "test line normalization with only new line character",
   698  			args: args{
   699  				path:           "line-ending-linux",
   700  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   701  			},
   702  			wantErr: nil,
   703  		},
   704  		{
   705  			name: "test line normalization with carriage return and new line characters",
   706  			args: args{
   707  				path:           "line-ending-windows",
   708  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   709  			},
   710  			wantErr: nil,
   711  		},
   712  		{
   713  			name: "test line normalization with only carriage return character",
   714  			args: args{
   715  				path:           "line-ending-macos",
   716  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   717  			},
   718  			wantErr: nil,
   719  		},
   720  		{
   721  			name: "test line normalization with combination of all of the above",
   722  			args: args{
   723  				path:           "line-ending-mixed",
   724  				hashAlgorithms: []string{"sha256", "sha384", "sha512"},
   725  			},
   726  			wantErr: nil,
   727  		},
   728  	}
   729  	expected := map[string]interface{}{
   730  		"sha256": "efb929dfabd55c93796fc61cbf1fe6157445f093167dbee82e8b069842a4fceb",
   731  		"sha384": "936e88775dfd17c24ed41e3a896dfdf3395707acee1b6f16a52ae144bdcd8611fd17e817f5b75e5a3cf7a1dacf187bae",
   732  		"sha512": "1d7a485cb2c3cf22c11b4be9afbf1745e053e21a40301d3e8143350d6d2873117c12acef49d4b3650b5262e8a26ffe809b177f968845bd268f26ffd978d314bd",
   733  	}
   734  	for _, tt := range tests {
   735  		t.Run(tt.name, func(t *testing.T) {
   736  			got, err := RecordArtifact(tt.args.path, tt.args.hashAlgorithms, true)
   737  			if err != tt.wantErr {
   738  				t.Errorf("RecordArtifact() error = %v, wantErr %v", err, tt.wantErr)
   739  				return
   740  			}
   741  			if !reflect.DeepEqual(got, expected) {
   742  				t.Errorf("RecordArtifact() got = %v, want %v", got, expected)
   743  			}
   744  		})
   745  	}
   746  }
   747  

View as plain text