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 }