Skip to content

Commit b0b652e

Browse files
committed
add missing handling
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
1 parent 857803e commit b0b652e

File tree

3 files changed

+122
-29
lines changed

3 files changed

+122
-29
lines changed

app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,44 @@ func validateIsDNS1123(name string) error {
212212

213213
return nil
214214
}
215+
216+
// ValidateUniqueMaterialName validates that only one material definition
217+
// with the same ID is present in the schema
218+
func (contract *CraftingSchemaV2) ValidateUniqueMaterialName() error {
219+
spec := contract.GetSpec()
220+
if spec == nil {
221+
return nil
222+
}
223+
224+
materialNames := make(map[string]bool)
225+
for _, m := range spec.GetMaterials() {
226+
if _, found := materialNames[m.Name]; found {
227+
return fmt.Errorf("material with name=%s is duplicated", m.Name)
228+
}
229+
materialNames[m.Name] = true
230+
}
231+
232+
return nil
233+
}
234+
235+
// ValidatePolicyAttachments validates policy references in the schema
236+
func (contract *CraftingSchemaV2) ValidatePolicyAttachments() error {
237+
spec := contract.GetSpec()
238+
if spec == nil || spec.GetPolicies() == nil {
239+
return nil
240+
}
241+
242+
policies := spec.GetPolicies()
243+
attachments := append(policies.GetAttestation(), policies.GetMaterials()...)
244+
245+
for _, att := range attachments {
246+
// Validate refs.
247+
if att.GetRef() != "" {
248+
if err := ValidatePolicyAttachmentRef(att.GetRef()); err != nil {
249+
return fmt.Errorf("invalid reference %q: %w", att.GetRef(), err)
250+
}
251+
}
252+
}
253+
254+
return nil
255+
}

app/controlplane/pkg/biz/workflowcontract.go

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,20 @@ type Contract struct {
6868
Raw []byte
6969
// Detected format as provided by the user
7070
Format unmarshal.RawFormat
71-
// marhalled proto contract
71+
// marshalled proto v1 contract
7272
Schema *schemav1.CraftingSchema
73+
// marshalled proto v2 contract
74+
Schemav2 *schemav1.CraftingSchemaV2
75+
}
76+
77+
// isV2Schema returns true if the contract uses the v2 CraftingSchema format
78+
func (c *Contract) isV2Schema() bool {
79+
return c.Schemav2 != nil
80+
}
81+
82+
// isV1Schema returns true if the contract uses the v1 CraftingSchema format
83+
func (c *Contract) isV1Schema() bool {
84+
return c.Schema != nil
7385
}
7486

7587
type WorkflowContractWithVersion struct {
@@ -388,20 +400,47 @@ func (uc *WorkflowContractUseCase) ValidateContractPolicies(rawSchema []byte, to
388400
return NewErrValidation(err)
389401
}
390402

391-
for _, att := range c.Schema.GetPolicies().GetAttestation() {
392-
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
393-
return NewErrValidation(err)
403+
switch {
404+
case c.isV1Schema():
405+
// Handle v1 schema
406+
schema := c.Schema
407+
for _, att := range schema.GetPolicies().GetAttestation() {
408+
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
409+
return NewErrValidation(err)
410+
}
394411
}
395-
}
396-
for _, att := range c.Schema.GetPolicies().GetMaterials() {
397-
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
398-
return NewErrValidation(err)
412+
for _, att := range schema.GetPolicies().GetMaterials() {
413+
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
414+
return NewErrValidation(err)
415+
}
399416
}
400-
}
401-
for _, gatt := range c.Schema.GetPolicyGroups() {
402-
if _, err := uc.findPolicyGroup(gatt, token); err != nil {
403-
return NewErrValidation(err)
417+
for _, gatt := range schema.GetPolicyGroups() {
418+
if _, err := uc.findPolicyGroup(gatt, token); err != nil {
419+
return NewErrValidation(err)
420+
}
421+
}
422+
case c.isV2Schema():
423+
// Handle v2 schema
424+
spec := c.Schemav2.GetSpec()
425+
if spec.GetPolicies() != nil {
426+
for _, att := range spec.GetPolicies().GetAttestation() {
427+
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
428+
return NewErrValidation(err)
429+
}
430+
}
431+
for _, att := range spec.GetPolicies().GetMaterials() {
432+
if _, err := uc.findAndValidatePolicy(att, token); err != nil {
433+
return NewErrValidation(err)
434+
}
435+
}
404436
}
437+
for _, gatt := range spec.GetPolicyGroups() {
438+
if _, err := uc.findPolicyGroup(gatt, token); err != nil {
439+
return NewErrValidation(err)
440+
}
441+
}
442+
default:
443+
return NewErrValidation(fmt.Errorf("invalid schema format"))
405444
}
406445

407446
return nil
@@ -601,22 +640,36 @@ func (uc *WorkflowContractUseCase) findProvider(providerName string) (*policies.
601640

602641
// UnmarshalAndValidateRawContract Takes the raw contract + format and will unmarshal the contract and validate it
603642
func UnmarshalAndValidateRawContract(raw []byte, format unmarshal.RawFormat) (*Contract, error) {
604-
contract := &schemav1.CraftingSchema{}
605-
err := unmarshal.FromRaw(raw, format, contract, true)
606-
if err != nil {
607-
return nil, NewErrValidation(err)
608-
}
609-
610-
// Custom Validations
611-
if err := contract.ValidateUniqueMaterialName(); err != nil {
612-
return nil, NewErrValidation(err)
643+
// Try parsing as v2 Contract format first
644+
v2Contract := &schemav1.CraftingSchemaV2{}
645+
v2Err := unmarshal.FromRaw(raw, format, v2Contract, true)
646+
if v2Err == nil {
647+
// Custom Validations
648+
if err := v2Contract.ValidateUniqueMaterialName(); err != nil {
649+
return nil, NewErrValidation(fmt.Errorf("unique material name validation failed: %w", err))
650+
}
651+
if err := v2Contract.ValidatePolicyAttachments(); err != nil {
652+
return nil, NewErrValidation(fmt.Errorf("policy attachment validation failed: %w", err))
653+
}
654+
return &Contract{Raw: raw, Format: format, Schemav2: v2Contract}, nil
613655
}
614656

615-
if err := contract.ValidatePolicyAttachments(); err != nil {
616-
return nil, NewErrValidation(err)
657+
// Fallback to v1 CraftingSchema format
658+
v1Contract := &schemav1.CraftingSchema{}
659+
v1Err := unmarshal.FromRaw(raw, format, v1Contract, true)
660+
if v1Err == nil {
661+
// Custom Validations
662+
if err := v1Contract.ValidateUniqueMaterialName(); err != nil {
663+
return nil, NewErrValidation(fmt.Errorf("unique material name validation failed: %w", err))
664+
}
665+
if err := v1Contract.ValidatePolicyAttachments(); err != nil {
666+
return nil, NewErrValidation(fmt.Errorf("policy attachment validation failed: %w", err))
667+
}
668+
return &Contract{Raw: raw, Format: format, Schema: v1Contract}, nil
617669
}
618-
619-
return &Contract{Raw: raw, Format: format, Schema: contract}, nil
670+
// Both parsing attempts failed
671+
// Best effort: provide errors for both schemas
672+
return nil, NewErrValidation(fmt.Errorf("contract validation failed:\n v2 Contract format error: %w\n v1 CraftingSchema format error: %w", v2Err, v1Err))
620673
}
621674

622675
// Will try to figure out the format of the raw contract and validate it

app/controlplane/pkg/data/workflowcontract.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflow"
3030
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowcontract"
3131
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowcontractversion"
32-
"github.com/chainloop-dev/chainloop/app/controlplane/pkg/unmarshal"
3332

3433
"github.com/go-kratos/kratos/v2/log"
3534
"github.com/google/uuid"
@@ -358,12 +357,12 @@ func entContractVersionToBizContractVersion(w *ent.WorkflowContractVersion) (*bi
358357
// Scenario 2: contracts that have been updated after the introduction of the raw_body field will have the raw_body field populated
359358
// but we also want to keep the Body field populated for backward compatibility
360359
} else if len(w.Body) == 0 {
361-
schema := &schemav1.CraftingSchema{}
362-
err := unmarshal.FromRaw(w.RawBody, w.RawBodyFormat, schema, false)
360+
parsedContract, err := biz.UnmarshalAndValidateRawContract(w.RawBody, w.RawBodyFormat)
363361
if err != nil {
364362
return nil, fmt.Errorf("failed to unmarshal raw body: %w", err)
365363
}
366-
contract.Schema = schema
364+
contract.Schema = parsedContract.Schema
365+
contract.Schemav2 = parsedContract.Schemav2
367366
}
368367

369368
return &biz.WorkflowContractVersion{

0 commit comments

Comments
 (0)