github.com/in-toto/in-toto-golang@v0.9.1-0.20240517212500-990269f763cf/in_toto/attestations_test.go (about)

     1  package in_toto
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
     9  	slsa01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1"
    10  	slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestDecodeProvenanceStatementSLSA02(t *testing.T) {
    15  	// Data from example in specification for generalized link format,
    16  	// subject and materials trimmed.
    17  	var data = `
    18  {
    19    "_type": "https://in-toto.io/Statement/v0.1",
    20    "subject": [
    21      { "name": "curl-7.72.0.tar.bz2",
    22        "digest": { "sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef" }},
    23      { "name": "curl-7.72.0.tar.gz",
    24        "digest": { "sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2" }}
    25    ],
    26    "predicateType": "https://slsa.dev/provenance/v0.2",
    27    "predicate": {
    28      "builder": { "id": "https://github.com/Attestations/GitHubHostedActions@v1" },
    29      "buildType": "https://github.com/Attestations/GitHubActionsWorkflow@v1",
    30      "invocation": {
    31  	  "configSource": {
    32  		"uri": "git+https://github.com/curl/curl-docker@master",
    33  		"digest": { "sha1": "d6525c840a62b398424a78d792f457477135d0cf" },
    34  		"entryPoint": "build.yaml:maketgz"
    35  	  }
    36      },
    37      "metadata": {
    38        "buildStartedOn": "2020-08-19T08:38:00Z",
    39        "completeness": {
    40            "environment": true
    41        }
    42      },
    43      "materials": [
    44        {
    45          "uri": "git+https://github.com/curl/curl-docker@master",
    46          "digest": { "sha1": "d6525c840a62b398424a78d792f457477135d0cf" }
    47        }, {
    48          "uri": "github_hosted_vm:ubuntu-18.04:20210123.1"
    49        }
    50      ]
    51    }
    52  }
    53  `
    54  
    55  	var testTime = time.Unix(1597826280, 0)
    56  	var want = ProvenanceStatement{
    57  		StatementHeader: StatementHeader{
    58  			Type:          StatementInTotoV01,
    59  			PredicateType: slsa02.PredicateSLSAProvenance,
    60  			Subject: []Subject{
    61  				{
    62  					Name: "curl-7.72.0.tar.bz2",
    63  					Digest: common.DigestSet{
    64  						"sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef",
    65  					},
    66  				},
    67  				{
    68  					Name: "curl-7.72.0.tar.gz",
    69  					Digest: common.DigestSet{
    70  						"sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2",
    71  					},
    72  				},
    73  			},
    74  		},
    75  		Predicate: slsa02.ProvenancePredicate{
    76  			Builder: common.ProvenanceBuilder{
    77  				ID: "https://github.com/Attestations/GitHubHostedActions@v1",
    78  			},
    79  			BuildType: "https://github.com/Attestations/GitHubActionsWorkflow@v1",
    80  			Invocation: slsa02.ProvenanceInvocation{
    81  				ConfigSource: slsa02.ConfigSource{
    82  					EntryPoint: "build.yaml:maketgz",
    83  					URI:        "git+https://github.com/curl/curl-docker@master",
    84  					Digest: common.DigestSet{
    85  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
    86  					},
    87  				},
    88  			},
    89  			Metadata: &slsa02.ProvenanceMetadata{
    90  				BuildStartedOn: &testTime,
    91  				Completeness: slsa02.ProvenanceComplete{
    92  					Environment: true,
    93  				},
    94  			},
    95  			Materials: []common.ProvenanceMaterial{
    96  				{
    97  					URI: "git+https://github.com/curl/curl-docker@master",
    98  					Digest: common.DigestSet{
    99  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
   100  					},
   101  				},
   102  				{
   103  					URI: "github_hosted_vm:ubuntu-18.04:20210123.1",
   104  				},
   105  			},
   106  		},
   107  	}
   108  	var got ProvenanceStatement
   109  
   110  	if err := json.Unmarshal([]byte(data), &got); err != nil {
   111  		t.Errorf("failed to unmarshal json: %s\n", err)
   112  		return
   113  	}
   114  
   115  	// Make sure parsed time have same location set, location is only used
   116  	// for display purposes.
   117  	loc := want.Predicate.Metadata.BuildStartedOn.Location()
   118  	tmp := got.Predicate.Metadata.BuildStartedOn.In(loc)
   119  	got.Predicate.Metadata.BuildStartedOn = &tmp
   120  
   121  	assert.Equal(t, want, got, "Unexpexted object after decoding")
   122  }
   123  
   124  func TestEncodeProvenanceStatementSLSA02(t *testing.T) {
   125  	var testTime = time.Unix(1597826280, 0)
   126  	var p = ProvenanceStatement{
   127  		StatementHeader: StatementHeader{
   128  			Type:          StatementInTotoV01,
   129  			PredicateType: slsa02.PredicateSLSAProvenance,
   130  			Subject: []Subject{
   131  				{
   132  					Name: "curl-7.72.0.tar.bz2",
   133  					Digest: common.DigestSet{
   134  						"sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef",
   135  					},
   136  				},
   137  				{
   138  					Name: "curl-7.72.0.tar.gz",
   139  					Digest: common.DigestSet{
   140  						"sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2",
   141  					},
   142  				},
   143  			},
   144  		},
   145  		Predicate: slsa02.ProvenancePredicate{
   146  			Builder: common.ProvenanceBuilder{
   147  				ID: "https://github.com/Attestations/GitHubHostedActions@v1",
   148  			},
   149  			BuildType: "https://github.com/Attestations/GitHubActionsWorkflow@v1",
   150  			Invocation: slsa02.ProvenanceInvocation{
   151  				ConfigSource: slsa02.ConfigSource{
   152  					URI: "git+https://github.com/curl/curl-docker@master",
   153  					Digest: common.DigestSet{
   154  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
   155  					},
   156  					EntryPoint: "build.yaml:maketgz",
   157  				},
   158  			},
   159  			Metadata: &slsa02.ProvenanceMetadata{
   160  				BuildStartedOn:  &testTime,
   161  				BuildFinishedOn: &testTime,
   162  				Completeness: slsa02.ProvenanceComplete{
   163  					Parameters:  true,
   164  					Environment: false,
   165  					Materials:   true,
   166  				},
   167  			},
   168  			Materials: []common.ProvenanceMaterial{
   169  				{
   170  					URI: "git+https://github.com/curl/curl-docker@master",
   171  					Digest: common.DigestSet{
   172  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
   173  					},
   174  				},
   175  				{
   176  					URI: "github_hosted_vm:ubuntu-18.04:20210123.1",
   177  				},
   178  				{
   179  					URI: "git+https://github.com/curl/",
   180  				},
   181  			},
   182  		},
   183  	}
   184  	var want = `{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"curl-7.72.0.tar.bz2","digest":{"sha256":"ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef"}},{"name":"curl-7.72.0.tar.gz","digest":{"sha256":"d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2"}}],"predicate":{"builder":{"id":"https://github.com/Attestations/GitHubHostedActions@v1"},"buildType":"https://github.com/Attestations/GitHubActionsWorkflow@v1","invocation":{"configSource":{"uri":"git+https://github.com/curl/curl-docker@master","digest":{"sha1":"d6525c840a62b398424a78d792f457477135d0cf"},"entryPoint":"build.yaml:maketgz"}},"metadata":{"buildStartedOn":"2020-08-19T08:38:00Z","buildFinishedOn":"2020-08-19T08:38:00Z","completeness":{"parameters":true,"environment":false,"materials":true},"reproducible":false},"materials":[{"uri":"git+https://github.com/curl/curl-docker@master","digest":{"sha1":"d6525c840a62b398424a78d792f457477135d0cf"}},{"uri":"github_hosted_vm:ubuntu-18.04:20210123.1"},{"uri":"git+https://github.com/curl/"}]}}`
   185  
   186  	b, err := json.Marshal(&p)
   187  	assert.Nil(t, err, "Error during JSON marshal")
   188  	assert.Equal(t, want, string(b), "Wrong JSON produced")
   189  }
   190  
   191  func TestDecodeProvenanceStatementSLSA01(t *testing.T) {
   192  	// Data from example in specification for generalized link format,
   193  	// subject and materials trimmed.
   194  	var data = `
   195  {
   196    "_type": "https://in-toto.io/Statement/v0.1",
   197    "subject": [
   198      { "name": "curl-7.72.0.tar.bz2",
   199        "digest": { "sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef" }},
   200      { "name": "curl-7.72.0.tar.gz",
   201        "digest": { "sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2" }}
   202    ],
   203    "predicateType": "https://slsa.dev/provenance/v0.1",
   204    "predicate": {
   205      "builder": { "id": "https://github.com/Attestations/GitHubHostedActions@v1" },
   206      "recipe": {
   207        "type": "https://github.com/Attestations/GitHubActionsWorkflow@v1",
   208        "definedInMaterial": 0,
   209        "entryPoint": "build.yaml:maketgz"
   210      },
   211      "metadata": {
   212        "buildStartedOn": "2020-08-19T08:38:00Z",
   213        "completeness": {
   214            "environment": true
   215        }
   216      },
   217      "materials": [
   218        {
   219          "uri": "git+https://github.com/curl/curl-docker@master",
   220          "digest": { "sha1": "d6525c840a62b398424a78d792f457477135d0cf" }
   221        }, {
   222          "uri": "github_hosted_vm:ubuntu-18.04:20210123.1"
   223        }
   224      ]
   225    }
   226  }
   227  `
   228  
   229  	var testTime = time.Unix(1597826280, 0)
   230  	var want = ProvenanceStatementSLSA01{
   231  		StatementHeader: StatementHeader{
   232  			Type:          StatementInTotoV01,
   233  			PredicateType: slsa01.PredicateSLSAProvenance,
   234  			Subject: []Subject{
   235  				{
   236  					Name: "curl-7.72.0.tar.bz2",
   237  					Digest: common.DigestSet{
   238  						"sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef",
   239  					},
   240  				},
   241  				{
   242  					Name: "curl-7.72.0.tar.gz",
   243  					Digest: common.DigestSet{
   244  						"sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2",
   245  					},
   246  				},
   247  			},
   248  		},
   249  		Predicate: slsa01.ProvenancePredicate{
   250  			Builder: common.ProvenanceBuilder{
   251  				ID: "https://github.com/Attestations/GitHubHostedActions@v1",
   252  			},
   253  			Recipe: slsa01.ProvenanceRecipe{
   254  				Type:              "https://github.com/Attestations/GitHubActionsWorkflow@v1",
   255  				DefinedInMaterial: new(int),
   256  				EntryPoint:        "build.yaml:maketgz",
   257  			},
   258  			Metadata: &slsa01.ProvenanceMetadata{
   259  				BuildStartedOn: &testTime,
   260  				Completeness: slsa01.ProvenanceComplete{
   261  					Environment: true,
   262  				},
   263  			},
   264  			Materials: []common.ProvenanceMaterial{
   265  				{
   266  					URI: "git+https://github.com/curl/curl-docker@master",
   267  					Digest: common.DigestSet{
   268  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
   269  					},
   270  				},
   271  				{
   272  					URI: "github_hosted_vm:ubuntu-18.04:20210123.1",
   273  				},
   274  			},
   275  		},
   276  	}
   277  	var got ProvenanceStatementSLSA01
   278  
   279  	if err := json.Unmarshal([]byte(data), &got); err != nil {
   280  		t.Errorf("failed to unmarshal json: %s\n", err)
   281  		return
   282  	}
   283  
   284  	// Make sure parsed time have same location set, location is only used
   285  	// for display purposes.
   286  	loc := want.Predicate.Metadata.BuildStartedOn.Location()
   287  	tmp := got.Predicate.Metadata.BuildStartedOn.In(loc)
   288  	got.Predicate.Metadata.BuildStartedOn = &tmp
   289  
   290  	assert.Equal(t, want, got, "Unexpexted object after decoding")
   291  }
   292  
   293  func TestEncodeProvenanceStatementSLSA01(t *testing.T) {
   294  	var testTime = time.Unix(1597826280, 0)
   295  	var p = ProvenanceStatementSLSA01{
   296  		StatementHeader: StatementHeader{
   297  			Type:          StatementInTotoV01,
   298  			PredicateType: slsa01.PredicateSLSAProvenance,
   299  			Subject: []Subject{
   300  				{
   301  					Name: "curl-7.72.0.tar.bz2",
   302  					Digest: common.DigestSet{
   303  						"sha256": "ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef",
   304  					},
   305  				},
   306  				{
   307  					Name: "curl-7.72.0.tar.gz",
   308  					Digest: common.DigestSet{
   309  						"sha256": "d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2",
   310  					},
   311  				},
   312  			},
   313  		},
   314  		Predicate: slsa01.ProvenancePredicate{
   315  			Builder: common.ProvenanceBuilder{
   316  				ID: "https://github.com/Attestations/GitHubHostedActions@v1",
   317  			},
   318  			Recipe: slsa01.ProvenanceRecipe{
   319  				Type:              "https://github.com/Attestations/GitHubActionsWorkflow@v1",
   320  				DefinedInMaterial: new(int),
   321  				EntryPoint:        "build.yaml:maketgz",
   322  			},
   323  			Metadata: &slsa01.ProvenanceMetadata{
   324  				BuildStartedOn:  &testTime,
   325  				BuildFinishedOn: &testTime,
   326  				Completeness: slsa01.ProvenanceComplete{
   327  					Arguments:   true,
   328  					Environment: false,
   329  					Materials:   true,
   330  				},
   331  			},
   332  			Materials: []common.ProvenanceMaterial{
   333  				{
   334  					URI: "git+https://github.com/curl/curl-docker@master",
   335  					Digest: common.DigestSet{
   336  						"sha1": "d6525c840a62b398424a78d792f457477135d0cf",
   337  					},
   338  				},
   339  				{
   340  					URI: "github_hosted_vm:ubuntu-18.04:20210123.1",
   341  				},
   342  				{
   343  					URI: "git+https://github.com/curl/",
   344  				},
   345  			},
   346  		},
   347  	}
   348  	var want = `{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.1","subject":[{"name":"curl-7.72.0.tar.bz2","digest":{"sha256":"ad91970864102a59765e20ce16216efc9d6ad381471f7accceceab7d905703ef"}},{"name":"curl-7.72.0.tar.gz","digest":{"sha256":"d4d5899a3868fbb6ae1856c3e55a32ce35913de3956d1973caccd37bd0174fa2"}}],"predicate":{"builder":{"id":"https://github.com/Attestations/GitHubHostedActions@v1"},"recipe":{"type":"https://github.com/Attestations/GitHubActionsWorkflow@v1","definedInMaterial":0,"entryPoint":"build.yaml:maketgz"},"metadata":{"buildStartedOn":"2020-08-19T08:38:00Z","buildFinishedOn":"2020-08-19T08:38:00Z","completeness":{"arguments":true,"environment":false,"materials":true},"reproducible":false},"materials":[{"uri":"git+https://github.com/curl/curl-docker@master","digest":{"sha1":"d6525c840a62b398424a78d792f457477135d0cf"}},{"uri":"github_hosted_vm:ubuntu-18.04:20210123.1"},{"uri":"git+https://github.com/curl/"}]}}`
   349  
   350  	b, err := json.Marshal(&p)
   351  	assert.Nil(t, err, "Error during JSON marshal")
   352  	assert.Equal(t, want, string(b), "Wrong JSON produced")
   353  }
   354  
   355  // Test that the default date (January 1, year 1, 00:00:00 UTC) is
   356  // not marshalled
   357  func TestMetadataNoTime(t *testing.T) {
   358  	var md = slsa02.ProvenanceMetadata{
   359  		Completeness: slsa02.ProvenanceComplete{
   360  			Parameters: true,
   361  		},
   362  		Reproducible: true,
   363  	}
   364  	var want = `{"completeness":{"parameters":true,"environment":false,"materials":false},"reproducible":true}`
   365  	var got slsa02.ProvenanceMetadata
   366  	b, err := json.Marshal(&md)
   367  
   368  	t.Run("Marshal", func(t *testing.T) {
   369  		assert.Nil(t, err, "Error during JSON marshal")
   370  		assert.Equal(t, want, string(b), "Wrong JSON produced")
   371  	})
   372  
   373  	t.Run("Unmashal", func(t *testing.T) {
   374  		err := json.Unmarshal(b, &got)
   375  		assert.Nil(t, err, "Error during JSON unmarshal")
   376  		assert.Equal(t, md, got, "Wrong struct after JSON unmarshal")
   377  	})
   378  }
   379  
   380  // Verify that the behaviour of definedInMaterial can be controlled,
   381  // as there is a semantic difference in value present or 0.
   382  func TestRecipe(t *testing.T) {
   383  	var r = slsa01.ProvenanceRecipe{
   384  		Type:       "testType",
   385  		EntryPoint: "testEntry",
   386  	}
   387  	var want = `{"type":"testType","entryPoint":"testEntry"}`
   388  	var got slsa01.ProvenanceRecipe
   389  	b, err := json.Marshal(&r)
   390  
   391  	t.Run("No time/marshal", func(t *testing.T) {
   392  		assert.Nil(t, err, "Error during JSON marshal")
   393  		assert.Equal(t, want, string(b), "Wrong JSON produced")
   394  	})
   395  
   396  	t.Run("No time/unmarshal", func(t *testing.T) {
   397  		err = json.Unmarshal(b, &got)
   398  		assert.Nil(t, err, "Error during JSON unmarshal")
   399  		assert.Equal(t, r, got, "Wrong struct after JSON unmarshal")
   400  	})
   401  
   402  	// Set time to zero and run test again
   403  	r.DefinedInMaterial = new(int)
   404  	want = `{"type":"testType","definedInMaterial":0,"entryPoint":"testEntry"}`
   405  	b, err = json.Marshal(&r)
   406  
   407  	t.Run("With time/marshal", func(t *testing.T) {
   408  		assert.Nil(t, err, "Error during JSON marshal")
   409  		assert.Equal(t, want, string(b), "Wrong JSON produced")
   410  	})
   411  
   412  	t.Run("With time/unmarshal", func(t *testing.T) {
   413  		err = json.Unmarshal(b, &got)
   414  		assert.Nil(t, err, "Error during JSON unmarshal")
   415  		assert.Equal(t, r, got, "Wrong struct after JSON unmarshal")
   416  	})
   417  }
   418  
   419  func TestLinkStatement(t *testing.T) {
   420  	var data = `
   421  {
   422    "subject": [
   423       {"name": "baz",
   424        "digest": { "sha256": "hash1" }}
   425    ],
   426    "predicateType": "https://in-toto.io/Link/v1",
   427    "predicate": {
   428      "_type": "link",
   429      "name": "name",
   430      "command": ["cc", "-o", "baz", "baz.z"],
   431      "materials": {
   432         "kv": {"alg": "vv"}
   433      },
   434      "products": {
   435         "kp": {"alg": "vp"}
   436      },
   437      "byproducts": {
   438         "kb": "vb"
   439      },
   440      "environment": {
   441         "FOO": "BAR"
   442      }
   443    }
   444  }
   445  `
   446  
   447  	var want = LinkStatement{
   448  		StatementHeader: StatementHeader{
   449  			PredicateType: PredicateLinkV1,
   450  			Subject: []Subject{
   451  				{
   452  					Name: "baz",
   453  					Digest: common.DigestSet{
   454  						"sha256": "hash1",
   455  					},
   456  				},
   457  			},
   458  		},
   459  		Predicate: Link{
   460  			Type: "link",
   461  			Name: "name",
   462  			Materials: map[string]HashObj{
   463  				"kv": {"alg": "vv"},
   464  			},
   465  			Products: map[string]HashObj{
   466  				"kp": {"alg": "vp"},
   467  			},
   468  			ByProducts: map[string]interface{}{
   469  				"kb": "vb",
   470  			},
   471  			Environment: map[string]interface{}{
   472  				"FOO": "BAR",
   473  			},
   474  			Command: []string{"cc", "-o", "baz", "baz.z"},
   475  		},
   476  	}
   477  	var got LinkStatement
   478  
   479  	if err := json.Unmarshal([]byte(data), &got); err != nil {
   480  		t.Errorf("failed to unmarshal json: %s\n", err)
   481  		return
   482  	}
   483  
   484  	assert.Equal(t, want, got, "Unexpexted object after decoding")
   485  }