diff --git a/.github/scripts/excluded_from_copyright b/.github/scripts/excluded_from_copyright index adf035736b..aaae575e04 100644 --- a/.github/scripts/excluded_from_copyright +++ b/.github/scripts/excluded_from_copyright @@ -51,5 +51,7 @@ ./pkg/assembler/graphql/generated/path.generated.go ./pkg/assembler/graphql/generated/metadata.generated.go ./pkg/assembler/graphql/resolvers/metadata.resolvers.go +./pkg/assembler/graphql/resolvers/contact.resolvers.go +./pkg/assembler/graphql/generated/contact.generated.go ./internal/testing/mocks/scorecard.go ./internal/testing/mocks/documentparser.go diff --git a/pkg/assembler/graphql/generated/artifact.generated.go b/pkg/assembler/graphql/generated/artifact.generated.go index f4eb4b7a2e..ae77a65e4c 100644 --- a/pkg/assembler/graphql/generated/artifact.generated.go +++ b/pkg/assembler/graphql/generated/artifact.generated.go @@ -27,6 +27,7 @@ type MutationResolver interface { CertifyScorecard(ctx context.Context, source model.SourceInputSpec, scorecard model.ScorecardInputSpec) (*model.CertifyScorecard, error) IngestVEXStatement(ctx context.Context, subject model.PackageOrArtifactInput, vulnerability model.VulnerabilityInput, vexStatement model.VexStatementInputSpec) (*model.CertifyVEXStatement, error) IngestVulnerability(ctx context.Context, pkg model.PkgInputSpec, vulnerability model.VulnerabilityInput, certifyVuln model.VulnerabilityMetaDataInput) (*model.CertifyVuln, error) + IngestPointOfContact(ctx context.Context, subject model.PackageSourceOrArtifactInput, pkgMatchType *model.MatchFlags, pointOfContact model.PointOfContactInputSpec) (*model.PointOfContact, error) IngestCve(ctx context.Context, cve *model.CVEInputSpec) (*model.Cve, error) IngestGhsa(ctx context.Context, ghsa *model.GHSAInputSpec) (*model.Ghsa, error) IngestHasSbom(ctx context.Context, subject model.PackageOrArtifactInput, hasSbom model.HasSBOMInputSpec) (*model.HasSbom, error) @@ -54,6 +55,7 @@ type QueryResolver interface { Scorecards(ctx context.Context, scorecardSpec *model.CertifyScorecardSpec) ([]*model.CertifyScorecard, error) CertifyVEXStatement(ctx context.Context, certifyVEXStatementSpec *model.CertifyVEXStatementSpec) ([]*model.CertifyVEXStatement, error) CertifyVuln(ctx context.Context, certifyVulnSpec *model.CertifyVulnSpec) ([]*model.CertifyVuln, error) + PointOfContact(ctx context.Context, pointOfContactSpec *model.PointOfContactSpec) ([]*model.PointOfContact, error) Cve(ctx context.Context, cveSpec *model.CVESpec) ([]*model.Cve, error) Ghsa(ctx context.Context, ghsaSpec *model.GHSASpec) ([]*model.Ghsa, error) HasSbom(ctx context.Context, hasSBOMSpec *model.HasSBOMSpec) ([]*model.HasSbom, error) @@ -634,6 +636,39 @@ func (ec *executionContext) field_Mutation_ingestPkgEqual_args(ctx context.Conte return args, nil } +func (ec *executionContext) field_Mutation_ingestPointOfContact_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.PackageSourceOrArtifactInput + if tmp, ok := rawArgs["subject"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("subject")) + arg0, err = ec.unmarshalNPackageSourceOrArtifactInput2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageSourceOrArtifactInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["subject"] = arg0 + var arg1 *model.MatchFlags + if tmp, ok := rawArgs["pkgMatchType"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pkgMatchType")) + arg1, err = ec.unmarshalOMatchFlags2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐMatchFlags(ctx, tmp) + if err != nil { + return nil, err + } + } + args["pkgMatchType"] = arg1 + var arg2 model.PointOfContactInputSpec + if tmp, ok := rawArgs["pointOfContact"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pointOfContact")) + arg2, err = ec.unmarshalNPointOfContactInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactInputSpec(ctx, tmp) + if err != nil { + return nil, err + } + } + args["pointOfContact"] = arg2 + return args, nil +} + func (ec *executionContext) field_Mutation_ingestSLSA_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -952,6 +987,21 @@ func (ec *executionContext) field_Query_PkgEqual_args(ctx context.Context, rawAr return args, nil } +func (ec *executionContext) field_Query_PointOfContact_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *model.PointOfContactSpec + if tmp, ok := rawArgs["pointOfContactSpec"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("pointOfContactSpec")) + arg0, err = ec.unmarshalOPointOfContactSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactSpec(ctx, tmp) + if err != nil { + return nil, err + } + } + args["pointOfContactSpec"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1864,6 +1914,79 @@ func (ec *executionContext) fieldContext_Mutation_ingestVulnerability(ctx contex return fc, nil } +func (ec *executionContext) _Mutation_ingestPointOfContact(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_ingestPointOfContact(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().IngestPointOfContact(rctx, fc.Args["subject"].(model.PackageSourceOrArtifactInput), fc.Args["pkgMatchType"].(*model.MatchFlags), fc.Args["pointOfContact"].(model.PointOfContactInputSpec)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.PointOfContact) + fc.Result = res + return ec.marshalNPointOfContact2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContact(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_ingestPointOfContact(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_PointOfContact_id(ctx, field) + case "subject": + return ec.fieldContext_PointOfContact_subject(ctx, field) + case "email": + return ec.fieldContext_PointOfContact_email(ctx, field) + case "info": + return ec.fieldContext_PointOfContact_info(ctx, field) + case "since": + return ec.fieldContext_PointOfContact_since(ctx, field) + case "justification": + return ec.fieldContext_PointOfContact_justification(ctx, field) + case "origin": + return ec.fieldContext_PointOfContact_origin(ctx, field) + case "collector": + return ec.fieldContext_PointOfContact_collector(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PointOfContact", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_ingestPointOfContact_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_ingestCVE(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_ingestCVE(ctx, field) if err != nil { @@ -3531,6 +3654,79 @@ func (ec *executionContext) fieldContext_Query_CertifyVuln(ctx context.Context, return fc, nil } +func (ec *executionContext) _Query_PointOfContact(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_PointOfContact(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().PointOfContact(rctx, fc.Args["pointOfContactSpec"].(*model.PointOfContactSpec)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*model.PointOfContact) + fc.Result = res + return ec.marshalNPointOfContact2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_PointOfContact(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_PointOfContact_id(ctx, field) + case "subject": + return ec.fieldContext_PointOfContact_subject(ctx, field) + case "email": + return ec.fieldContext_PointOfContact_email(ctx, field) + case "info": + return ec.fieldContext_PointOfContact_info(ctx, field) + case "since": + return ec.fieldContext_PointOfContact_since(ctx, field) + case "justification": + return ec.fieldContext_PointOfContact_justification(ctx, field) + case "origin": + return ec.fieldContext_PointOfContact_origin(ctx, field) + case "collector": + return ec.fieldContext_PointOfContact_collector(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PointOfContact", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_PointOfContact_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_cve(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_cve(ctx, field) if err != nil { @@ -5092,6 +5288,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "ingestPointOfContact": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_ingestPointOfContact(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "ingestCVE": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_ingestCVE(ctx, field) @@ -5413,6 +5616,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "PointOfContact": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_PointOfContact(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "cve": field := field diff --git a/pkg/assembler/graphql/generated/contact.generated.go b/pkg/assembler/graphql/generated/contact.generated.go new file mode 100644 index 0000000000..9ac3af7a3d --- /dev/null +++ b/pkg/assembler/graphql/generated/contact.generated.go @@ -0,0 +1,711 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package generated + +import ( + "context" + "errors" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/99designs/gqlgen/graphql" + "github.com/guacsec/guac/pkg/assembler/graphql/model" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ************************** generated!.gotpl ************************** + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +// endregion ***************************** args.gotpl ***************************** + +// region ************************** directives.gotpl ************************** + +// endregion ************************** directives.gotpl ************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _PointOfContact_id(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_id(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_subject(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_subject(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Subject, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(model.PackageSourceOrArtifact) + fc.Result = res + return ec.marshalNPackageSourceOrArtifact2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageSourceOrArtifact(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_subject(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type PackageSourceOrArtifact does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_email(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_email(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Email, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_email(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_info(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_info(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Info, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_info(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_since(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_since(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Since, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_since(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_justification(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_justification(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Justification, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_justification(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_origin(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_origin(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Origin, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_origin(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _PointOfContact_collector(ctx context.Context, field graphql.CollectedField, obj *model.PointOfContact) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_PointOfContact_collector(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Collector, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_PointOfContact_collector(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PointOfContact", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputPointOfContactInputSpec(ctx context.Context, obj interface{}) (model.PointOfContactInputSpec, error) { + var it model.PointOfContactInputSpec + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"email", "info", "since", "justification", "origin", "collector"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "email": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Email = data + case "info": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("info")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Info = data + case "since": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("since")) + data, err := ec.unmarshalNTime2timeᚐTime(ctx, v) + if err != nil { + return it, err + } + it.Since = data + case "justification": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("justification")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Justification = data + case "origin": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("origin")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Origin = data + case "collector": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("collector")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Collector = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputPointOfContactSpec(ctx context.Context, obj interface{}) (model.PointOfContactSpec, error) { + var it model.PointOfContactSpec + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"id", "subject", "email", "info", "since", "justification", "origin", "collector"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "id": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + data, err := ec.unmarshalOID2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.ID = data + case "subject": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("subject")) + data, err := ec.unmarshalOPackageSourceOrArtifactSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPackageSourceOrArtifactSpec(ctx, v) + if err != nil { + return it, err + } + it.Subject = data + case "email": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Email = data + case "info": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("info")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Info = data + case "since": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("since")) + data, err := ec.unmarshalOTime2ᚖtimeᚐTime(ctx, v) + if err != nil { + return it, err + } + it.Since = data + case "justification": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("justification")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Justification = data + case "origin": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("origin")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Origin = data + case "collector": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("collector")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Collector = data + } + } + + return it, nil +} + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var pointOfContactImplementors = []string{"PointOfContact"} + +func (ec *executionContext) _PointOfContact(ctx context.Context, sel ast.SelectionSet, obj *model.PointOfContact) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, pointOfContactImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PointOfContact") + case "id": + out.Values[i] = ec._PointOfContact_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "subject": + out.Values[i] = ec._PointOfContact_subject(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "email": + out.Values[i] = ec._PointOfContact_email(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "info": + out.Values[i] = ec._PointOfContact_info(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "since": + out.Values[i] = ec._PointOfContact_since(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "justification": + out.Values[i] = ec._PointOfContact_justification(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "origin": + out.Values[i] = ec._PointOfContact_origin(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "collector": + out.Values[i] = ec._PointOfContact_collector(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) marshalNPointOfContact2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContact(ctx context.Context, sel ast.SelectionSet, v model.PointOfContact) graphql.Marshaler { + return ec._PointOfContact(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPointOfContact2ᚕᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PointOfContact) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNPointOfContact2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContact(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPointOfContact2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContact(ctx context.Context, sel ast.SelectionSet, v *model.PointOfContact) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PointOfContact(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNPointOfContactInputSpec2githubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactInputSpec(ctx context.Context, v interface{}) (model.PointOfContactInputSpec, error) { + res, err := ec.unmarshalInputPointOfContactInputSpec(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalOPointOfContactSpec2ᚖgithubᚗcomᚋguacsecᚋguacᚋpkgᚋassemblerᚋgraphqlᚋmodelᚐPointOfContactSpec(ctx context.Context, v interface{}) (*model.PointOfContactSpec, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputPointOfContactSpec(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/pkg/assembler/graphql/generated/root_.generated.go b/pkg/assembler/graphql/generated/root_.generated.go index c11b9980b4..10489a2a9a 100644 --- a/pkg/assembler/graphql/generated/root_.generated.go +++ b/pkg/assembler/graphql/generated/root_.generated.go @@ -201,6 +201,7 @@ type ComplexityRoot struct { IngestPackage func(childComplexity int, pkg model.PkgInputSpec) int IngestPackages func(childComplexity int, pkgs []*model.PkgInputSpec) int IngestPkgEqual func(childComplexity int, pkg model.PkgInputSpec, otherPackage model.PkgInputSpec, pkgEqual model.PkgEqualInputSpec) int + IngestPointOfContact func(childComplexity int, subject model.PackageSourceOrArtifactInput, pkgMatchType *model.MatchFlags, pointOfContact model.PointOfContactInputSpec) int IngestSlsa func(childComplexity int, subject model.ArtifactInputSpec, builtFrom []*model.ArtifactInputSpec, builtBy model.BuilderInputSpec, slsa model.SLSAInputSpec) int IngestSource func(childComplexity int, source model.SourceInputSpec) int IngestVEXStatement func(childComplexity int, subject model.PackageOrArtifactInput, vulnerability model.VulnerabilityInput, vexStatement model.VexStatementInputSpec) int @@ -254,6 +255,17 @@ type ComplexityRoot struct { Packages func(childComplexity int) int } + PointOfContact struct { + Collector func(childComplexity int) int + Email func(childComplexity int) int + ID func(childComplexity int) int + Info func(childComplexity int) int + Justification func(childComplexity int) int + Origin func(childComplexity int) int + Since func(childComplexity int) int + Subject func(childComplexity int) int + } + Query struct { Artifacts func(childComplexity int, artifactSpec *model.ArtifactSpec) int Builders func(childComplexity int, builderSpec *model.BuilderSpec) int @@ -279,6 +291,7 @@ type ComplexityRoot struct { Packages func(childComplexity int, pkgSpec *model.PkgSpec) int Path func(childComplexity int, subject string, target string, maxPathLength int, usingOnly []model.Edge) int PkgEqual func(childComplexity int, pkgEqualSpec *model.PkgEqualSpec) int + PointOfContact func(childComplexity int, pointOfContactSpec *model.PointOfContactSpec) int Scorecards func(childComplexity int, scorecardSpec *model.CertifyScorecardSpec) int Sources func(childComplexity int, sourceSpec *model.SourceSpec) int } @@ -1240,6 +1253,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.IngestPkgEqual(childComplexity, args["pkg"].(model.PkgInputSpec), args["otherPackage"].(model.PkgInputSpec), args["pkgEqual"].(model.PkgEqualInputSpec)), true + case "Mutation.ingestPointOfContact": + if e.complexity.Mutation.IngestPointOfContact == nil { + break + } + + args, err := ec.field_Mutation_ingestPointOfContact_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.IngestPointOfContact(childComplexity, args["subject"].(model.PackageSourceOrArtifactInput), args["pkgMatchType"].(*model.MatchFlags), args["pointOfContact"].(model.PointOfContactInputSpec)), true + case "Mutation.ingestSLSA": if e.complexity.Mutation.IngestSlsa == nil { break @@ -1449,6 +1474,62 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.PkgEqual.Packages(childComplexity), true + case "PointOfContact.collector": + if e.complexity.PointOfContact.Collector == nil { + break + } + + return e.complexity.PointOfContact.Collector(childComplexity), true + + case "PointOfContact.email": + if e.complexity.PointOfContact.Email == nil { + break + } + + return e.complexity.PointOfContact.Email(childComplexity), true + + case "PointOfContact.id": + if e.complexity.PointOfContact.ID == nil { + break + } + + return e.complexity.PointOfContact.ID(childComplexity), true + + case "PointOfContact.info": + if e.complexity.PointOfContact.Info == nil { + break + } + + return e.complexity.PointOfContact.Info(childComplexity), true + + case "PointOfContact.justification": + if e.complexity.PointOfContact.Justification == nil { + break + } + + return e.complexity.PointOfContact.Justification(childComplexity), true + + case "PointOfContact.origin": + if e.complexity.PointOfContact.Origin == nil { + break + } + + return e.complexity.PointOfContact.Origin(childComplexity), true + + case "PointOfContact.since": + if e.complexity.PointOfContact.Since == nil { + break + } + + return e.complexity.PointOfContact.Since(childComplexity), true + + case "PointOfContact.subject": + if e.complexity.PointOfContact.Subject == nil { + break + } + + return e.complexity.PointOfContact.Subject(childComplexity), true + case "Query.artifacts": if e.complexity.Query.Artifacts == nil { break @@ -1737,6 +1818,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.PkgEqual(childComplexity, args["pkgEqualSpec"].(*model.PkgEqualSpec)), true + case "Query.PointOfContact": + if e.complexity.Query.PointOfContact == nil { + break + } + + args, err := ec.field_Query_PointOfContact_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.PointOfContact(childComplexity, args["pointOfContactSpec"].(*model.PointOfContactSpec)), true + case "Query.scorecards": if e.complexity.Query.Scorecards == nil { break @@ -2077,6 +2170,8 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputPkgInputSpec, ec.unmarshalInputPkgNameSpec, ec.unmarshalInputPkgSpec, + ec.unmarshalInputPointOfContactInputSpec, + ec.unmarshalInputPointOfContactSpec, ec.unmarshalInputSLSAInputSpec, ec.unmarshalInputSLSAPredicateInputSpec, ec.unmarshalInputSLSAPredicateSpec, @@ -2912,6 +3007,103 @@ extend type Mutation { "Adds a certification that a package has been scanned for vulnerabilities." ingestVulnerability(pkg: PkgInputSpec!, vulnerability: VulnerabilityInput!, certifyVuln: VulnerabilityMetaDataInput!): CertifyVuln! } +`, BuiltIn: false}, + {Name: "../schema/contact.graphql", Input: `# +# Copyright 2023 The GUAC Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE: This is experimental and might change in the future! + +""" +PointOfContact is an attestation of how to get in touch with the person(s) responsible +for a package, source, or artifact. + +All evidence trees record a justification for the property they represent as +well as the document that contains the attestation (origin) and the collector +that collected the document (collector). + +The attestation applies to a subject which is a package, source, or artifact. +If the attestation targets a package, it must target a PackageName or a +PackageVersion. If the attestation targets a source, it must target a +SourceName. + +email is the email address (singular) of the point of contact. + +info is additional contact information other than email address. This is free +form. + +NOTE: the identifiers for point of contact should be part of software trees. +This will benefit from identifier look up and traversal as well as organization +hierarchy. However, until the use case arises, PointOfContact will be a flat +reference to the contact details. +""" +type PointOfContact { + id: ID! + subject: PackageSourceOrArtifact! + email: String! + info: String! + since: Time! + justification: String! + origin: String! + collector: String! +} + +""" +PointOfContactSpec allows filtering the list of PointOfContact evidence to return in a +query. + +If a package is specified in the subject filter, then it must be specified up +to PackageName or PackageVersion. That is, user must specify package name, or +name and one of version, qualifiers, or subpath. + +If a source is specified in the subject filter, then it must specify a name, +and optionally a tag and a commit. + +since filters attestations with a value of since later or equal to the provided filter. +""" +input PointOfContactSpec { + id: ID + subject: PackageSourceOrArtifactSpec + email: String + info: String + since: Time + justification: String + origin: String + collector: String +} + +""" +PointOfContactInputSpec represents the mutation input to ingest a PointOfContact evidence. +""" +input PointOfContactInputSpec { + email: String! + info: String! + since: Time! + justification: String! + origin: String! + collector: String! +} + +extend type Query { + "Returns all PointOfContact attestations matching a filter." + PointOfContact (pointOfContactSpec: PointOfContactSpec): [PointOfContact!]! +} + +extend type Mutation { + "Adds a PointOfContact attestation to a package, source or artifact." + ingestPointOfContact(subject: PackageSourceOrArtifactInput!, pkgMatchType: MatchFlags, pointOfContact: PointOfContactInputSpec!): PointOfContact! +} `, BuiltIn: false}, {Name: "../schema/cve.graphql", Input: `# # Copyright 2023 The GUAC Authors. diff --git a/pkg/assembler/graphql/model/nodes.go b/pkg/assembler/graphql/model/nodes.go index f05d81394f..56850cab9c 100644 --- a/pkg/assembler/graphql/model/nodes.go +++ b/pkg/assembler/graphql/model/nodes.go @@ -1002,6 +1002,70 @@ type PkgSpec struct { Subpath *string `json:"subpath,omitempty"` } +// PointOfContact is an attestation of how to get in touch with the person(s) responsible +// for a package, source, or artifact. +// +// All evidence trees record a justification for the property they represent as +// well as the document that contains the attestation (origin) and the collector +// that collected the document (collector). +// +// The attestation applies to a subject which is a package, source, or artifact. +// If the attestation targets a package, it must target a PackageName or a +// PackageVersion. If the attestation targets a source, it must target a +// SourceName. +// +// email is the email address (singular) of the point of contact. +// +// info is additional contact information other than email address. This is free +// form. +// +// NOTE: the identifiers for point of contact should be part of software trees. +// This will benefit from identifier look up and traversal as well as organization +// hierarchy. However, until the use case arises, PointOfContact will be a flat +// reference to the contact details. +type PointOfContact struct { + ID string `json:"id"` + Subject PackageSourceOrArtifact `json:"subject"` + Email string `json:"email"` + Info string `json:"info"` + Since time.Time `json:"since"` + Justification string `json:"justification"` + Origin string `json:"origin"` + Collector string `json:"collector"` +} + +// PointOfContactInputSpec represents the mutation input to ingest a PointOfContact evidence. +type PointOfContactInputSpec struct { + Email string `json:"email"` + Info string `json:"info"` + Since time.Time `json:"since"` + Justification string `json:"justification"` + Origin string `json:"origin"` + Collector string `json:"collector"` +} + +// PointOfContactSpec allows filtering the list of PointOfContact evidence to return in a +// query. +// +// If a package is specified in the subject filter, then it must be specified up +// to PackageName or PackageVersion. That is, user must specify package name, or +// name and one of version, qualifiers, or subpath. +// +// If a source is specified in the subject filter, then it must specify a name, +// and optionally a tag and a commit. +// +// since filters attestations with a value of since later or equal to the provided filter. +type PointOfContactSpec struct { + ID *string `json:"id,omitempty"` + Subject *PackageSourceOrArtifactSpec `json:"subject,omitempty"` + Email *string `json:"email,omitempty"` + Info *string `json:"info,omitempty"` + Since *time.Time `json:"since,omitempty"` + Justification *string `json:"justification,omitempty"` + Origin *string `json:"origin,omitempty"` + Collector *string `json:"collector,omitempty"` +} + // SLSA contains all of the fields present in a SLSA attestation. // // The materials and builders are objects of the HasSLSA predicate, everything diff --git a/pkg/assembler/graphql/resolvers/contact.resolvers.go b/pkg/assembler/graphql/resolvers/contact.resolvers.go new file mode 100644 index 0000000000..3d277b4ca5 --- /dev/null +++ b/pkg/assembler/graphql/resolvers/contact.resolvers.go @@ -0,0 +1,22 @@ +package resolvers + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.34 + +import ( + "context" + "fmt" + + "github.com/guacsec/guac/pkg/assembler/graphql/model" +) + +// IngestPointOfContact is the resolver for the ingestPointOfContact field. +func (r *mutationResolver) IngestPointOfContact(ctx context.Context, subject model.PackageSourceOrArtifactInput, pkgMatchType *model.MatchFlags, pointOfContact model.PointOfContactInputSpec) (*model.PointOfContact, error) { + panic(fmt.Errorf("not implemented: IngestPointOfContact - ingestPointOfContact")) +} + +// PointOfContact is the resolver for the PointOfContact field. +func (r *queryResolver) PointOfContact(ctx context.Context, pointOfContactSpec *model.PointOfContactSpec) ([]*model.PointOfContact, error) { + panic(fmt.Errorf("not implemented: PointOfContact - PointOfContact")) +} diff --git a/pkg/assembler/graphql/schema/contact.graphql b/pkg/assembler/graphql/schema/contact.graphql new file mode 100644 index 0000000000..218ccad893 --- /dev/null +++ b/pkg/assembler/graphql/schema/contact.graphql @@ -0,0 +1,96 @@ +# +# Copyright 2023 The GUAC Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE: This is experimental and might change in the future! + +""" +PointOfContact is an attestation of how to get in touch with the person(s) responsible +for a package, source, or artifact. + +All evidence trees record a justification for the property they represent as +well as the document that contains the attestation (origin) and the collector +that collected the document (collector). + +The attestation applies to a subject which is a package, source, or artifact. +If the attestation targets a package, it must target a PackageName or a +PackageVersion. If the attestation targets a source, it must target a +SourceName. + +email is the email address (singular) of the point of contact. + +info is additional contact information other than email address. This is free +form. + +NOTE: the identifiers for point of contact should be part of software trees. +This will benefit from identifier look up and traversal as well as organization +hierarchy. However, until the use case arises, PointOfContact will be a flat +reference to the contact details. +""" +type PointOfContact { + id: ID! + subject: PackageSourceOrArtifact! + email: String! + info: String! + since: Time! + justification: String! + origin: String! + collector: String! +} + +""" +PointOfContactSpec allows filtering the list of PointOfContact evidence to return in a +query. + +If a package is specified in the subject filter, then it must be specified up +to PackageName or PackageVersion. That is, user must specify package name, or +name and one of version, qualifiers, or subpath. + +If a source is specified in the subject filter, then it must specify a name, +and optionally a tag and a commit. + +since filters attestations with a value of since later or equal to the provided filter. +""" +input PointOfContactSpec { + id: ID + subject: PackageSourceOrArtifactSpec + email: String + info: String + since: Time + justification: String + origin: String + collector: String +} + +""" +PointOfContactInputSpec represents the mutation input to ingest a PointOfContact evidence. +""" +input PointOfContactInputSpec { + email: String! + info: String! + since: Time! + justification: String! + origin: String! + collector: String! +} + +extend type Query { + "Returns all PointOfContact attestations matching a filter." + PointOfContact (pointOfContactSpec: PointOfContactSpec): [PointOfContact!]! +} + +extend type Mutation { + "Adds a PointOfContact attestation to a package, source or artifact." + ingestPointOfContact(subject: PackageSourceOrArtifactInput!, pkgMatchType: MatchFlags, pointOfContact: PointOfContactInputSpec!): PointOfContact! +}