From 6e69795805d92e84de84aca3dd22f2bd4ca8ead4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Garc=C3=ADa=20Veytia=20=28Puerco=29?= <adolfo.garcia@uservers.net> Date: Tue, 10 Jan 2023 00:58:42 -0600 Subject: [PATCH 1/2] Remove vex module packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are now splitting the vex module, this commit removes the vex functionality that is now living in openvex/vex Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net> --- .gitignore | 1 + .go-version | 1 - pkg/attestation/attestation.go | 130 ----------- pkg/attestation/attestation_test.go | 32 --- pkg/csaf/csaf.go | 113 --------- pkg/csaf/csaf_test.go | 46 ---- pkg/csaf/testdata/csaf.json | 100 -------- pkg/sarif/sarif.go | 48 ---- pkg/vex/justification.go | 78 ------- pkg/vex/statement.go | 166 -------------- pkg/vex/status.go | 66 ------ pkg/vex/testdata/csaf.json | 90 -------- pkg/vex/testdata/vex.yaml | 26 --- pkg/vex/vex.go | 343 ---------------------------- pkg/vex/vex_test.go | 163 ------------- 15 files changed, 1 insertion(+), 1402 deletions(-) delete mode 100644 .go-version delete mode 100644 pkg/attestation/attestation.go delete mode 100644 pkg/attestation/attestation_test.go delete mode 100644 pkg/csaf/csaf.go delete mode 100644 pkg/csaf/csaf_test.go delete mode 100644 pkg/csaf/testdata/csaf.json delete mode 100644 pkg/sarif/sarif.go delete mode 100644 pkg/vex/justification.go delete mode 100644 pkg/vex/statement.go delete mode 100644 pkg/vex/status.go delete mode 100644 pkg/vex/testdata/csaf.json delete mode 100644 pkg/vex/testdata/vex.yaml delete mode 100644 pkg/vex/vex.go delete mode 100644 pkg/vex/vex_test.go diff --git a/.gitignore b/.gitignore index bc84b04..c4909e3 100644 --- a/.gitignore +++ b/.gitignore @@ -139,3 +139,4 @@ qemu-*-static rootfs.tar vexctl dist/** +.go-version diff --git a/.go-version b/.go-version deleted file mode 100644 index 836ae4e..0000000 --- a/.go-version +++ /dev/null @@ -1 +0,0 @@ -1.19.2 diff --git a/pkg/attestation/attestation.go b/pkg/attestation/attestation.go deleted file mode 100644 index 7dae8fb..0000000 --- a/pkg/attestation/attestation.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package attestation - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "strings" - "time" - - "github.com/google/go-containerregistry/pkg/crane" - intoto "github.com/in-toto/in-toto-golang/in_toto" - "github.com/sigstore/cosign/cmd/cosign/cli/options" - "github.com/sigstore/cosign/cmd/cosign/cli/sign" - "github.com/sigstore/sigstore/pkg/signature/dsse" - signatureoptions "github.com/sigstore/sigstore/pkg/signature/options" - - "chainguard.dev/vex/pkg/vex" -) - -type Attestation struct { - intoto.StatementHeader - // Predicate contains type specific metadata. - Predicate vex.VEX `json:"predicate"` - Signed bool `json:"-"` - signedData []byte `json:"-"` -} - -func New() *Attestation { - return &Attestation{ - StatementHeader: intoto.StatementHeader{ - Type: intoto.StatementInTotoV01, - PredicateType: vex.TypeURI, - Subject: []intoto.Subject{}, - }, - Predicate: vex.New(), - } -} - -// ToJSON returns the attestation as a JSON byte array -func (att *Attestation) ToJSON(w io.Writer) error { - if att.Signed { - if _, err := w.Write(att.signedData); err != nil { - return fmt.Errorf("writing signed attestation") - } - return nil - } - - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.SetEscapeHTML(false) - - if err := enc.Encode(att); err != nil { - return fmt.Errorf("encoding attestation: %w", err) - } - - return nil -} - -func (att *Attestation) AddImageSubjects(imageRefs []string) error { - for _, refString := range imageRefs { - digest, err := crane.Digest(refString) - if err != nil { - return fmt.Errorf("getting image digest: %w", err) - } - s := intoto.Subject{ - Name: refString, - Digest: map[string]string{"sha256": strings.TrimPrefix(digest, "sha256:")}, - } - - att.Subject = append(att.Subject, s) - } - return nil -} - -// Sign the attestation -func (att *Attestation) Sign() error { - ctx := context.Background() - var timeout time.Duration /// TODO move to options - var certPath, certChainPath string - ko := options.KeyOpts{ - // KeyRef: s.options.PrivateKeyPath, - // IDToken: identityToken, - FulcioURL: options.DefaultFulcioURL, - RekorURL: options.DefaultRekorURL, - OIDCIssuer: options.DefaultOIDCIssuerURL, - OIDCClientID: "sigstore", - - InsecureSkipFulcioVerify: false, - SkipConfirmation: true, - // FulcioAuthFlow: "", - } - - if timeout != 0 { - var cancelFn context.CancelFunc - ctx, cancelFn = context.WithTimeout(ctx, timeout) - defer cancelFn() - } - - sv, err := sign.SignerFromKeyOpts(ctx, certPath, certChainPath, ko) - if err != nil { - return fmt.Errorf("getting signer: %w", err) - } - defer sv.Close() - - // Wrap the attestation in the DSSE envelope - wrapped := dsse.WrapSigner(sv, "application/vnd.in-toto+json") - - var b bytes.Buffer - if err := att.ToJSON(&b); err != nil { - return fmt.Errorf("serializing attestation to json: %w", err) - } - - signedPayload, err := wrapped.SignMessage( - bytes.NewReader(b.Bytes()), signatureoptions.WithContext(ctx), - ) - if err != nil { - return fmt.Errorf("signing attestation: %w", err) - } - - att.signedData = signedPayload - att.Signed = true - return nil -} diff --git a/pkg/attestation/attestation_test.go b/pkg/attestation/attestation_test.go deleted file mode 100644 index b855772..0000000 --- a/pkg/attestation/attestation_test.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package attestation - -import ( - "bytes" - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" - - "chainguard.dev/vex/pkg/vex" -) - -func TestSerialize(t *testing.T) { - att := New() - pred := vex.New() - pred.Author = "Chainguard" - att.Predicate = pred - - var b bytes.Buffer - err := att.ToJSON(&b) - require.NoError(t, err) - - att2 := New() - err = json.Unmarshal(b.Bytes(), &att2) - require.NoError(t, err) - require.Equal(t, att2.Predicate.Author, "Chainguard") -} diff --git a/pkg/csaf/csaf.go b/pkg/csaf/csaf.go deleted file mode 100644 index 86f8135..0000000 --- a/pkg/csaf/csaf.go +++ /dev/null @@ -1,113 +0,0 @@ -package csaf - -import ( - "encoding/json" - "fmt" - "os" - "time" -) - -type CSAF struct { - Document DocumentMetadata `json:"document"` - ProductTree ProductBranch `json:"product_tree"` - Vulnerabilities []Vulnerability `json:"vulnerabilities"` -} - -type DocumentMetadata struct { - Title string `json:"title"` - Tracking Tracking `json:"tracking"` -} - -type Tracking struct { - ID string `json:"id"` - CurrentReleaseDate time.Time `json:"current_release_date"` -} - -type Vulnerability struct { - CVE string `json:"cve"` - ProductStatus map[string][]string `json:"product_status"` - Threats []ThreatData `json:"threats"` -} - -type ThreatData struct { - Category string `json:"category"` - Details string `json:"details"` - ProductIDs []string `json:"product_ids"` -} - -type ProductBranch struct { - Category string `json:"category"` - Name string `json:"name"` - Branches []ProductBranch `json:"branches"` - Product Product `json:"product,omitempty"` -} - -type Product struct { - Name string `json:"name"` - ID string `json:"product_id"` - IdentificationHelper map[string]string `json:"product_identification_helper"` -} - -func Open(path string) (*CSAF, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("opening CSAF document: %w", err) - } - - csafDoc := &CSAF{} - if err := json.Unmarshal(data, csafDoc); err != nil { - return nil, fmt.Errorf("unmarshalling CSAF document: %w", err) - } - return csafDoc, nil -} - -func (csafDoc *CSAF) FirstProductName() string { - return csafDoc.ProductTree.FindFirstProduct() -} - -// FindFirstProduct recursively searches for the first product in the tree -func (branch *ProductBranch) FindFirstProduct() string { - if branch.Product.ID != "" { - return branch.Product.ID - } - - // No noested branches - if branch.Branches == nil { - return "" - } - - for _, b := range branch.Branches { - if p := b.FindFirstProduct(); p != "" { - return p - } - } - - return "" -} - -// FindFirstProduct recursively searches for the first product in the tree -func (branch *ProductBranch) FindProductIdentifier(helperType, helperValue string) *Product { - if len(branch.Product.IdentificationHelper) != 0 { - for k := range branch.Product.IdentificationHelper { - if k != helperType { - continue - } - if branch.Product.IdentificationHelper[k] == helperValue { - return &branch.Product - } - } - } - - // No noested branches - if branch.Branches == nil { - return nil - } - - for _, b := range branch.Branches { - if p := b.FindProductIdentifier(helperType, helperValue); p != nil { - return p - } - } - - return nil -} diff --git a/pkg/csaf/csaf_test.go b/pkg/csaf/csaf_test.go deleted file mode 100644 index f994f96..0000000 --- a/pkg/csaf/csaf_test.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package csaf - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestOpen(t *testing.T) { - doc, err := Open("testdata/csaf.json") - require.NoError(t, err) - require.NotNil(t, doc) - require.Equal(t, "Example VEX Document", doc.Document.Title) - require.Equal(t, "CSAFPID-0001", doc.FirstProductName()) - - // Vulnerabilities - require.Len(t, doc.Vulnerabilities, 1) - require.Equal(t, doc.Vulnerabilities[0].CVE, "CVE-2009-4487") - require.Len(t, doc.Vulnerabilities[0].ProductStatus, 1) - require.Len(t, doc.Vulnerabilities[0].ProductStatus["known_not_affected"], 1) - require.Equal(t, doc.Vulnerabilities[0].ProductStatus["known_not_affected"][0], "CSAFPID-0001") -} - -func TestFindFirstProduct(t *testing.T) { - doc, err := Open("testdata/csaf.json") - require.NoError(t, err) - require.NotNil(t, doc) - - prod := doc.ProductTree.FindFirstProduct() - require.Equal(t, prod, "CSAFPID-0001") -} - -func TestFindByHelper(t *testing.T) { - doc, err := Open("testdata/csaf.json") - require.NoError(t, err) - require.NotNil(t, doc) - - prod := doc.ProductTree.FindProductIdentifier("purl", "pkg:maven/@1.3.4") - require.NotNil(t, prod) - require.Equal(t, prod.ID, "CSAFPID-0001") -} diff --git a/pkg/csaf/testdata/csaf.json b/pkg/csaf/testdata/csaf.json deleted file mode 100644 index a9a424a..0000000 --- a/pkg/csaf/testdata/csaf.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "document": { - "category": "csaf_vex", - "csaf_version": "2.0", - "notes": [ - { - "category": "summary", - "text": "Example VEX document.", - "title": "Document Title" - } - ], - "publisher": { - "category": "vendor", - "name": "Example Company", - "namespace": "https://psirt.example.com" - }, - "title": "Example VEX Document", - "tracking": { - "current_release_date": "2022-03-03T11:00:00.000Z", - "generator": { - "date": "2022-03-03T11:00:00.000Z", - "engine": { - "name": "Secvisogram", - "version": "1.11.0" - } - }, - "id": "2022-EVD-UC-01-NA-001", - "initial_release_date": "2022-03-03T11:00:00.000Z", - "revision_history": [ - { - "date": "2022-03-03T11:00:00.000Z", - "number": "1", - "summary": "Initial version." - } - ], - "status": "final", - "version": "1" - } - }, - "product_tree": { - "branches": [ - { - "branches": [ - { - "product": { - "name": "Example Company ABC 4.2", - "product_id": "CSAFPID-0001", - "product_identification_helper": { - "purl": "pkg:maven/@1.3.4" - } - }, - "branches": [ - { - "category": "product_version", - "name": "4.2", - "product": { - "name": "Example Company ABC 4.2", - "product_id": "INTERNAL-0001", - "product_identification_helper": { - "purl": "pkg:golang/github.com/go-homedir@v1.1.0" - } - } - } - ], - "category": "product_name", - "name": "ABC" - } - ], - "category": "vendor", - "name": "Example Company" - } - ] - }, - "vulnerabilities": [ - { - "cve": "CVE-2009-4487", - "notes": [ - { - "category": "description", - "text": "nginx 0.7.64 writes data to a log file without sanitizing non-printable characters, which might allow remote attackers to modify a window's title, or possibly execute arbitrary commands or overwrite files, via an HTTP request containing an escape sequence for a terminal emulator.", - "title": "CVE description" - } - ], - "product_status": { - "known_not_affected": [ - "CSAFPID-0001" - ] - }, - "threats": [ - { - "category": "impact", - "details": "Class with vulnerable code was removed before shipping.", - "product_ids": [ - "CSAFPID-0001" - ] - } - ] - } - ] -} diff --git a/pkg/sarif/sarif.go b/pkg/sarif/sarif.go deleted file mode 100644 index 51a622f..0000000 --- a/pkg/sarif/sarif.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package sarif - -import ( - "encoding/json" - "fmt" - "io" - "os" - - gosarif "github.com/owenrumney/go-sarif/sarif" -) - -type Report struct { - gosarif.Report -} - -func Open(path string) (*Report, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("opening yaml file: %w", err) - } - report := New() - if err := json.Unmarshal(data, report); err != nil { - return nil, fmt.Errorf("unmarshalling vex data: %w", err) - } - return report, nil -} - -func New() *Report { - return &Report{ - Report: gosarif.Report{}, - } -} - -func (report *Report) ToJSON(w io.Writer) error { - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.SetEscapeHTML(false) - - if err := enc.Encode(report); err != nil { - return fmt.Errorf("encoding sarif report: %w", err) - } - return nil -} diff --git a/pkg/vex/justification.go b/pkg/vex/justification.go deleted file mode 100644 index 0b22535..0000000 --- a/pkg/vex/justification.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package vex - -// Justification describes why a given component is not affected by a -// vulnerability. -type Justification string - -const ( - // ComponentNotPresent means the vulnerable component is not included in the artifact. - // - // ComponentNotPresent is a strong justification that the artifact is not affected. - ComponentNotPresent Justification = "component_not_present" - - // VulnerableCodeNotPresent means the vulnerable component is included in - // artifact, but the vulnerable code is not present. Typically, this case occurs - // when source code is configured or built in a way that excluded the vulnerable - // code. - // - // VulnerableCodeNotPresent is a strong justification that the artifact is not affected. - VulnerableCodeNotPresent Justification = "vulnerable_code_not_present" - - // VulnerableCodeNotInExecutePath means the vulnerable code (likely in - // [subcomponent_id]) can not be executed as it is used by [product_id]. - // Typically, this case occurs when [product_id] includes the vulnerable - // [subcomponent_id] and the vulnerable code but does not call or use the - // vulnerable code. - VulnerableCodeNotInExecutePath Justification = "vulnerable_code_not_in_execute_path" - - // VulnerableCodeCannotBeControlledByAdversary means the vulnerable code cannot - // be controlled by an attacker to exploit the vulnerability. - // - // This justification could be difficult to prove conclusively. - VulnerableCodeCannotBeControlledByAdversary Justification = "vulnerable_code_cannot_be_controlled_by_adversary" - - // InlineMitigationsAlreadyExist means [product_id] includes built-in protections - // or features that prevent exploitation of the vulnerability. These built-in - // protections cannot be subverted by the attacker and cannot be configured or - // disabled by the user. These mitigations completely prevent exploitation based - // on known attack vectors. - // - // This justification could be difficult to prove conclusively. History is - // littered with examples of mitigation bypasses, typically involving minor - // modifications of existing exploit code. - InlineMitigationsAlreadyExist Justification = "inline_mitigations_already_exist" -) - -// Justifications returns a list of the valid Justification values. -func Justifications() []string { - return []string{ - string(ComponentNotPresent), - string(VulnerableCodeNotPresent), - string(VulnerableCodeNotInExecutePath), - string(VulnerableCodeCannotBeControlledByAdversary), - string(InlineMitigationsAlreadyExist), - } -} - -// Valid returns a bool indicating whether the Justification value is equal to -// one of the enumerated allowed values for Justification. -func (j Justification) Valid() bool { - switch j { - case ComponentNotPresent, - VulnerableCodeNotPresent, - VulnerableCodeNotInExecutePath, - VulnerableCodeCannotBeControlledByAdversary, - InlineMitigationsAlreadyExist: - - return true - - default: - - return false - } -} diff --git a/pkg/vex/statement.go b/pkg/vex/statement.go deleted file mode 100644 index 98248e2..0000000 --- a/pkg/vex/statement.go +++ /dev/null @@ -1,166 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package vex - -import ( - "fmt" - "sort" - "strings" - "time" -) - -// A Statement is a declaration conveying a single [status] for a single [vul_id] for one or more [product_id]s. A VEX Statement exists within a VEX Document. -type Statement struct { - // [vul_id] SHOULD use existing and well known identifiers, for example: - // CVE, the Global Security Database (GSD), or a supplier’s vulnerability - // tracking system. It is expected that vulnerability identification systems - // are external to and maintained separately from VEX. - // - // [vul_id] MAY be URIs or URLs. - // [vul_id] MAY be arbitrary and MAY be created by the VEX statement [author]. - Vulnerability string `json:"vulnerability,omitempty"` - VulnDescription string `json:"vuln_description,omitempty"` - - // Timestamp is the time at which the information expressed in the Statement - // was known to be true. - Timestamp *time.Time `json:"timestamp,omitempty"` - - // ProductIdentifiers - // Product details MUST specify what Status applies to. - // Product details MUST include [product_id] and MAY include [subcomponent_id]. - Products []string `json:"products,omitempty"` - Subcomponents []string `json:"subcomponents,omitempty"` - - // A VEX statement MUST provide Status of the vulnerabilities with respect to the - // products and components listed in the statement. Status MUST be one of the - // Status const values, some of which have further options and requirements. - Status Status `json:"status"` - - // [status_notes] MAY convey information about how [status] was determined - // and MAY reference other VEX information. - StatusNotes string `json:"status_notes,omitempty"` - - // For ”not_affected” status, a VEX statement MUST include a status Justification - // that further explains the status. - Justification Justification `json:"justification,omitempty"` - - // For ”not_affected” status, a VEX statement MAY include an ImpactStatement - // that contains a description why the vulnerability cannot be exploited. - ImpactStatement string `json:"impact_statement,omitempty"` - - // For "affected" status, a VEX statement MUST include an ActionStatement that - // SHOULD describe actions to remediate or mitigate [vul_id]. - ActionStatement string `json:"action_statement,omitempty"` - ActionStatementTimestamp *time.Time `json:"action_statement_timestamp,omitempty"` -} - -// Validate checks to see whether the given Statement is valid. If it's not, an -// error is returned explaining the reason the Statement is invalid. Otherwise, -// nil is returned. -func (stmt Statement) Validate() error { //nolint:gocritic // turning off for rule hugeParam - if s := stmt.Status; !s.Valid() { - return fmt.Errorf("invalid status value %q, must be one of [%s]", s, strings.Join(Statuses(), ", ")) - } - - switch s := stmt.Status; s { - case StatusNotAffected: - // require a justification - j := stmt.Justification - is := stmt.ImpactStatement - if j == "" && is == "" { - return fmt.Errorf("either justification or impact statement must be defined when using status %q", s) - } - - if j != "" && !j.Valid() { - return fmt.Errorf("invalid justification value %q, must be one of [%s]", j, strings.Join(Justifications(), ", ")) - } - - // irrelevant fields should not be set - if v := stmt.ActionStatement; v != "" { - return fmt.Errorf("action statement should not be set when using status %q (was set to %q)", s, v) - } - - case StatusAffected: - // irrelevant fields should not be set - if v := stmt.Justification; v != "" { - return fmt.Errorf("justification should not be set when using status %q (was set to %q)", s, v) - } - - if v := stmt.ImpactStatement; v != "" { - return fmt.Errorf("impact statement should not be set when using status %q (was set to %q)", s, v) - } - - // action statement is now required - if v := stmt.ActionStatement; v == "" { - return fmt.Errorf("action statement must be set when using status %q", s) - } - - case StatusUnderInvestigation: - // irrelevant fields should not be set - if v := stmt.Justification; v != "" { - return fmt.Errorf("justification should not be set when using status %q (was set to %q)", s, v) - } - - if v := stmt.ImpactStatement; v != "" { - return fmt.Errorf("impact statement should not be set when using status %q (was set to %q)", s, v) - } - - if v := stmt.ActionStatement; v != "" { - return fmt.Errorf("action statement should not be set when using status %q (was set to %q)", s, v) - } - - case StatusFixed: - // irrelevant fields should not be set - if v := stmt.Justification; v != "" { - return fmt.Errorf("justification should not be set when using status %q (was set to %q)", s, v) - } - - if v := stmt.ImpactStatement; v != "" { - return fmt.Errorf("impact statement should not be set when using status %q (was set to %q)", s, v) - } - - if v := stmt.ActionStatement; v != "" { - return fmt.Errorf("action statement should not be set when using status %q (was set to %q)", s, v) - } - } - - return nil -} - -// SortStatements does an "in-place" sort of the given slice of VEX statements. -// -// The documentTimestamp parameter is needed because statements without timestamps inherit the timestamp of the document. -func SortStatements(stmts []Statement, documentTimestamp time.Time) { - sort.SliceStable(stmts, func(i, j int) bool { - vulnComparison := strings.Compare(stmts[i].Vulnerability, stmts[j].Vulnerability) - if vulnComparison != 0 { - // i.e. different vulnerabilities; sort by string comparison - return vulnComparison < 0 - } - - // i.e. the same vulnerability; sort statements by timestamp - - iTime := stmts[i].Timestamp - if iTime == nil || iTime.IsZero() { - iTime = &documentTimestamp - } - - jTime := stmts[j].Timestamp - if jTime == nil || jTime.IsZero() { - jTime = &documentTimestamp - } - - if iTime == nil { - return false - } - - if jTime == nil { - return true - } - - return iTime.Before(*jTime) - }) -} diff --git a/pkg/vex/status.go b/pkg/vex/status.go deleted file mode 100644 index 84b91bb..0000000 --- a/pkg/vex/status.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package vex - -// Status describes the exploitability status of a component with respect to a -// vulnerability. -type Status string - -const ( - // StatusNotAffected means no remediation or mitigation is required. - StatusNotAffected Status = "not_affected" - - // StatusAffected means actions are recommended to remediate or mitigate. - StatusAffected Status = "affected" - - // StatusFixed means the listed products or components have been remediated (by including fixes). - StatusFixed Status = "fixed" - - // StatusUnderInvestigation means the author of the VEX statement is investigating. - StatusUnderInvestigation Status = "under_investigation" -) - -// Statuses returns a list of the valid Status values. -func Statuses() []string { - return []string{ - string(StatusNotAffected), - string(StatusAffected), - string(StatusFixed), - string(StatusUnderInvestigation), - } -} - -// Valid returns a bool indicating whether the Status value is equal to one of the enumerated allowed values for Status. -func (s Status) Valid() bool { - switch s { - case StatusNotAffected, - StatusAffected, - StatusFixed, - StatusUnderInvestigation: - - return true - - default: - - return false - } -} - -// StatusFromCSAF returns a vex status from the CSAF status -func StatusFromCSAF(csafStatus string) Status { - switch csafStatus { - case "known_not_affected": - return StatusNotAffected - case "fixed": - return StatusFixed - case "under_investigation": - return StatusUnderInvestigation - case "known_affected": - return StatusAffected - default: - return "" - } -} diff --git a/pkg/vex/testdata/csaf.json b/pkg/vex/testdata/csaf.json deleted file mode 100644 index be0019f..0000000 --- a/pkg/vex/testdata/csaf.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "document": { - "category": "csaf_vex", - "csaf_version": "2.0", - "notes": [ - { - "category": "summary", - "text": "Example VEX document.", - "title": "Document Title" - } - ], - "publisher": { - "category": "vendor", - "name": "Example Company", - "namespace": "https://psirt.example.com" - }, - "title": "Example VEX Document Use Case 1 - Not Affected", - "tracking": { - "current_release_date": "2022-03-03T11:00:00.000Z", - "generator": { - "date": "2022-03-03T11:00:00.000Z", - "engine": { - "name": "Secvisogram", - "version": "1.11.0" - } - }, - "id": "2022-EVD-UC-01-NA-001", - "initial_release_date": "2022-03-03T11:00:00.000Z", - "revision_history": [ - { - "date": "2022-03-03T11:00:00.000Z", - "number": "1", - "summary": "Initial version." - } - ], - "status": "final", - "version": "1" - } - }, - "product_tree": { - "branches": [ - { - "branches": [ - { - "branches": [ - { - "category": "product_version", - "name": "4.2", - "product": { - "name": "Example Company ABC 4.2", - "product_id": "CSAFPID-0001" - } - } - ], - "category": "product_name", - "name": "ABC" - } - ], - "category": "vendor", - "name": "Example Company" - } - ] - }, - "vulnerabilities": [ - { - "cve": "CVE-2009-4487", - "notes": [ - { - "category": "description", - "text": "nginx 0.7.64 writes data to a log file without sanitizing non-printable characters, which might allow remote attackers to modify a window's title, or possibly execute arbitrary commands or overwrite files, via an HTTP request containing an escape sequence for a terminal emulator.", - "title": "CVE description" - } - ], - "product_status": { - "known_not_affected": [ - "CSAFPID-0001" - ] - }, - "threats": [ - { - "category": "impact", - "details": "Class with vulnerable code was removed before shipping.", - "product_ids": [ - "CSAFPID-0001" - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/pkg/vex/testdata/vex.yaml b/pkg/vex/testdata/vex.yaml deleted file mode 100644 index eb068fd..0000000 --- a/pkg/vex/testdata/vex.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -format: vex_attestation -id: "" -author: "Chainguard" -role: "author" -timestamp: "2022-08-29T17:48:53.697543267-05:00" -statements: - - vulnerability: "CVE-2022-31030" - status: "not_affected" - justification: "vulnerable_code_not_in_execute_path" - action_statement: "Affected library function not called" - references: - - type: FEDORA - ref: "FEDORA-2022-1da581ac6d" - - type: "DEBIAN" - ref: "DSA-5162" - - vulnerability: "CVE-2021-44228" # Log4j - status: "affected" - action_statement: "Customers are advised to upgrade" - references: - - type: FEDORA - ref: "FEDORA-2021-66d6c484f3" - - type: "CERT-VN" - ref: "VU#930724" - - type: "CISCO" - ref: "cisco-sa-apache-log4j-qRuKNEbd" diff --git a/pkg/vex/vex.go b/pkg/vex/vex.go deleted file mode 100644 index d3fc4a9..0000000 --- a/pkg/vex/vex.go +++ /dev/null @@ -1,343 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package vex - -import ( - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "sort" - "strconv" - "strings" - "time" - - "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" - - "chainguard.dev/vex/pkg/csaf" -) - -const ( - // TypeURI is the type used to describe VEX documents, e.g. within [in-toto - // statements]. - // - // [in-toto statements]: https://github.com/in-toto/attestation/blob/main/spec/README.md#statement - TypeURI = "text/vex" - - // DefaultAuthor is the default value for a document's Author field. - DefaultAuthor = "Unknown Author" - - // DefaultRole is the default value for a document's AuthorRole field. - DefaultRole = "Document Creator" - - // Context is the URL of the json-ld context definition - Context = "https://openvex.dev/ns" - - // PublicNamespace is the public openvex namespace for common @ids - PublicNamespace = "https://openvex.dev/docs" - - // NoActionStatementMsg is the action statement that informs that there is no action statement :/ - NoActionStatementMsg = "No action statement provided" -) - -// The VEX type represents a VEX document and all of its contained information. -type VEX struct { - Metadata - Statements []Statement `json:"statements"` -} - -// The Metadata type represents the metadata associated with a VEX document. -type Metadata struct { - // Context is the URL pointing to the jsonld context definition - Context string `json:"@context"` - - // ID is the identifying string for the VEX document. This should be unique per - // document. - ID string `json:"@id"` - - // Author is the identifier for the author of the VEX statement, ideally a common - // name, may be a URI. [author] is an individual or organization. [author] - // identity SHOULD be cryptographically associated with the signature of the VEX - // statement or document or transport. - Author string `json:"author"` - - // AuthorRole describes the role of the document Author. - AuthorRole string `json:"role"` - - // Timestamp defines the time at which the document was issued. - Timestamp *time.Time `json:"timestamp"` - - // Version is the document version. It must be incremented when any content - // within the VEX document changes, including any VEX statements included within - // the VEX document. - Version string `json:"version"` - - // Tooling expresses how the VEX document and contained VEX statements were - // generated. It's optional. It may specify tools or automated processes used in - // the document or statement generation. - Tooling string `json:"tooling,omitempty"` - - // Supplier is an optional field. - Supplier string `json:"supplier,omitempty"` -} - -// New returns a new, initialized VEX document. -func New() VEX { - now := time.Now() - t, err := DateFromEnv() - if err != nil { - logrus.Warn(err) - } - if t != nil { - now = *t - } - return VEX{ - Metadata: Metadata{ - Context: Context, - Author: DefaultAuthor, - AuthorRole: DefaultRole, - Version: "1", - Timestamp: &now, - }, - Statements: []Statement{}, - } -} - -// Load reads the VEX document file at the given path and returns a decoded VEX -// object. If Load is unable to read the file or decode the document, it returns -// an error. -func Load(path string) (*VEX, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("loading VEX file: %w", err) - } - - vexDoc := &VEX{} - if err := json.Unmarshal(data, vexDoc); err != nil { - return nil, fmt.Errorf("unmarshaling VEX document: %w", err) - } - return vexDoc, nil -} - -// OpenYAML opens a VEX file in YAML format. -func OpenYAML(path string) (*VEX, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("opening YAML file: %w", err) - } - vexDoc := New() - if err := yaml.Unmarshal(data, &vexDoc); err != nil { - return nil, fmt.Errorf("unmarshalling VEX data: %w", err) - } - return &vexDoc, nil -} - -// OpenJSON opens a VEX file in JSON format. -func OpenJSON(path string) (*VEX, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("opening JSON file: %w", err) - } - vexDoc := New() - if err := json.Unmarshal(data, &vexDoc); err != nil { - return nil, fmt.Errorf("unmarshalling VEX data: %w", err) - } - return &vexDoc, nil -} - -// ToJSON serializes the VEX document to JSON and writes it to the passed writer. -func (vexDoc *VEX) ToJSON(w io.Writer) error { - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - enc.SetEscapeHTML(false) - - if err := enc.Encode(vexDoc); err != nil { - return fmt.Errorf("encoding vex document: %w", err) - } - return nil -} - -// StatementFromID returns a statement for a given vulnerability if there is one. -func (vexDoc *VEX) StatementFromID(id string) *Statement { - for _, statement := range vexDoc.Statements { //nolint:gocritic // turning off for rule rangeValCopy - if statement.Vulnerability == id { - logrus.Infof("VEX doc contains statement for CVE %s", id) - return &statement - } - } - return nil -} - -// SortDocuments sorts and returns a slice of documents based on their date. -// VEXes should be applied sequentially in chronological order as they capture -// knowledge about an artifact as it changes over time. -func SortDocuments(docs []*VEX) []*VEX { - sort.Slice(docs, func(i, j int) bool { - if docs[j].Timestamp == nil { - return true - } - if docs[i].Timestamp == nil { - return false - } - return docs[i].Timestamp.Before(*(docs[j].Timestamp)) - }) - return docs -} - -// OpenCSAF opens a CSAF document and builds a VEX object from it. -func OpenCSAF(path string, products []string) (*VEX, error) { - csafDoc, err := csaf.Open(path) - if err != nil { - return nil, fmt.Errorf("opening csaf doc: %w", err) - } - - productDict := map[string]string{} - for _, pid := range products { - productDict[pid] = pid - } - - // If no products were specified, we use the first one - if len(products) == 0 { - p := csafDoc.FirstProductName() - if p == "" { - // Error? I think so. - return nil, errors.New("unable to find a product ID in CSAF document") - } - productDict[p] = p - } - - // Create the vex doc - v := &VEX{ - Metadata: Metadata{ - ID: csafDoc.Document.Tracking.ID, - Author: "", - AuthorRole: "", - Timestamp: &time.Time{}, - }, - Statements: []Statement{}, - } - - // Cycle the CSAF vulns list and get those that apply - for _, c := range csafDoc.Vulnerabilities { - for status, docProducts := range c.ProductStatus { - for _, productID := range docProducts { - if _, ok := productDict[productID]; ok { - // Check we have a valid status - if StatusFromCSAF(status) == "" { - return nil, fmt.Errorf("invalid status for product %s", productID) - } - - // TODO search the threats struct for justification, etc - just := "" - for _, t := range c.Threats { - // Search the threats for a justification - for _, p := range t.ProductIDs { - if p == productID { - just = t.Details - } - } - } - - v.Statements = append(v.Statements, Statement{ - Vulnerability: c.CVE, - Status: StatusFromCSAF(status), - Justification: "", // Justifications are not machine readable in csaf, it seems - ActionStatement: just, - Products: products, - }) - } - } - } - } - - return v, nil -} - -// CanonicalHash returns a hash representing the state of impact statements -// expressed in it. This hash should be constant as long as the impact -// statements are not modified. Changes in extra information and metadata -// will not alter the hash. -func (vexDoc *VEX) CanonicalHash() (string, error) { - // Here's the algo: - - // 1. Start with the document date. In unixtime to avoid format variance. - cString := fmt.Sprintf("%d", vexDoc.Timestamp.Unix()) - - // 2. Document version - cString += fmt.Sprintf(":%s", vexDoc.Version) - - // 3. Sort the statements - stmts := vexDoc.Statements - SortStatements(stmts, *vexDoc.Timestamp) - - // 4. Now add the data from each statement - //nolint:gocritic - for _, s := range stmts { - // 4a. Vulnerability - cString += fmt.Sprintf(":%s", s.Vulnerability) - // 4b. Status + Justification - cString += fmt.Sprintf(":%s:%s", s.Status, s.Justification) - // 4c. Statement time, in unixtime. If it exists, if not the doc's - if s.Timestamp != nil { - cString += fmt.Sprintf(":%d", s.Timestamp.Unix()) - } else { - cString += fmt.Sprintf(":%d", vexDoc.Timestamp.Unix()) - } - // 4d. Sorted products - prods := s.Products - sort.Strings(prods) - cString += fmt.Sprintf(":%s", strings.Join(prods, ":")) - } - - // 5. Hash the string in sha256 and return - h := sha256.New() - if _, err := h.Write([]byte(cString)); err != nil { - return "", fmt.Errorf("hashing canonicalization string: %w", err) - } - return fmt.Sprintf("%x", h.Sum(nil)), nil -} - -// GenerateCanonicalID generates an ID for the document. The ID will be -// based on the canonicalization hash. This means that documents -// with the same impact statements will always get the same ID. -// Trying to generate the id of a doc with an existing ID will -// not do anything. -func (vexDoc *VEX) GenerateCanonicalID() (string, error) { - if vexDoc.ID != "" { - return vexDoc.ID, nil - } - cHash, err := vexDoc.CanonicalHash() - if err != nil { - return "", fmt.Errorf("getting canonical hash: %w", err) - } - - // For common namespaced documents we namespace them into /public - vexDoc.ID = fmt.Sprintf("%s/public/vex-%s", PublicNamespace, cHash) - return vexDoc.ID, nil -} - -func DateFromEnv() (*time.Time, error) { - // Support envvar for reproducible vexing - d := os.Getenv("SOURCE_DATE_EPOCH") - if d == "" { - return nil, nil - } - - var t time.Time - sec, err := strconv.ParseInt(d, 10, 64) - if err == nil { - t = time.Unix(sec, 0) - } else { - t, err = time.Parse(time.RFC3339, d) - if err != nil { - return nil, fmt.Errorf("failed to parse envvar SOURCE_DATE_EPOCH: %w", err) - } - } - return &t, nil -} diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go deleted file mode 100644 index cda67c3..0000000 --- a/pkg/vex/vex_test.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright 2022 Chainguard, Inc. -SPDX-License-Identifier: Apache-2.0 -*/ - -package vex - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestLoadYAML(t *testing.T) { - vexDoc, err := OpenYAML("testdata/vex.yaml") - require.NoError(t, err) - - require.Len(t, vexDoc.Statements, 2) -} - -func TestLoadCSAF(t *testing.T) { - vexDoc, err := OpenCSAF("testdata/csaf.json", []string{}) - require.NoError(t, err) - require.Len(t, vexDoc.Statements, 1) - require.Equal(t, vexDoc.Statements[0].Vulnerability, "CVE-2009-4487") - require.Equal(t, vexDoc.Statements[0].Status, StatusNotAffected) - require.Equal(t, vexDoc.Metadata.ID, "2022-EVD-UC-01-NA-001") -} - -func genTestDoc(t *testing.T) VEX { - ts, err := time.Parse(time.RFC3339, "2022-12-22T16:36:43-05:00") - require.NoError(t, err) - return VEX{ - Metadata: Metadata{ - Author: "John Doe", - AuthorRole: "VEX Writer Extraordinaire", - Timestamp: &ts, - Version: "1", - Tooling: "OpenVEX", - Supplier: "Chainguard Inc", - }, - Statements: []Statement{ - { - Vulnerability: "CVE-1234-5678", - VulnDescription: "", - Products: []string{"pkg:apk/wolfi/bash@1.0.0"}, - Status: "under_investigation", - }, - }, - } -} - -func TestCanonicalHash(t *testing.T) { - goldenHash := `461bb1de8d85c7a6af96edf24d0e0672726d248500e63c5413f89db0c6710fa0` - - otherTS, err := time.Parse(time.RFC3339, "2019-01-22T16:36:43-05:00") - require.NoError(t, err) - - for i, tc := range []struct { - prepare func(*VEX) - expected string - shouldErr bool - }{ - // Default Expected - {func(v *VEX) {}, goldenHash, false}, - // Adding a statement changes the hash - { - func(v *VEX) { - v.Statements = append(v.Statements, Statement{ - Vulnerability: "CVE-2010-543231", - Products: []string{"pkg:apk/wolfi/git@2.0.0"}, - Status: "affected", - }) - }, - "cf392111c8dfee8f6a115780de1eabf292fcd36aafb6eca75952ea7e2d648e21", - false, - }, - // Changing metadata should not change hash - { - func(v *VEX) { - v.Author = "123" - v.AuthorRole = "abc" - v.ID = "298347" // Mmhh... - v.Supplier = "Mr Supplier" - v.Tooling = "Fake Tool 1.0" - }, - goldenHash, - false, - }, - // Changing other statement metadata should not change the hash - { - func(v *VEX) { - v.Statements[0].ActionStatement = "Action!" - v.Statements[0].VulnDescription = "It is very bad" - v.Statements[0].StatusNotes = "Let's note somthn here" - v.Statements[0].ImpactStatement = "We evaded this CVE by a hair" - v.Statements[0].ActionStatementTimestamp = &otherTS - }, - goldenHash, - false, - }, - // Changing products changes the hash - { - func(v *VEX) { - v.Statements[0].Products[0] = "cool router, bro" - }, - "3ba778366d70b4fc656f9c1338a6be26fab55a7d011db4ceddf2f4840080ab3b", - false, - }, - // Changing document time changes the hash - { - func(v *VEX) { - v.Timestamp = &otherTS - }, - "c69a58b923d83f2c0952a508572aec6529801950e9dcac520dfbcbb953fffe52", - false, - }, - // Same timestamp in statement as doc should not change the hash - { - func(v *VEX) { - v.Statements[0].Timestamp = v.Timestamp - }, - goldenHash, - false, - }, - } { - doc := genTestDoc(t) - tc.prepare(&doc) - hashString, err := doc.CanonicalHash() - if tc.shouldErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - require.Equal(t, tc.expected, hashString, fmt.Sprintf("Testcase #%d %s", i, doc.Statements[0].Products[0])) - } -} - -func TestGenerateCanonicalID(t *testing.T) { - for _, tc := range []struct { - prepare func(*VEX) - expectedID string - }{ - { - // Normal generation - prepare: func(v *VEX) {}, - expectedID: "https://openvex.dev/docs/public/vex-461bb1de8d85c7a6af96edf24d0e0672726d248500e63c5413f89db0c6710fa0", - }, - { - // Existing IDs should not be changed - prepare: func(v *VEX) { v.ID = "VEX-ID-THAT-ALREADY-EXISTED" }, - expectedID: "VEX-ID-THAT-ALREADY-EXISTED", - }, - } { - doc := genTestDoc(t) - tc.prepare(&doc) - id, err := doc.GenerateCanonicalID() - require.NoError(t, err) - require.Equal(t, tc.expectedID, id) - } -} From 48f541bc125bf2f1961dec43f4fc7dc4a619f3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20Garc=C3=ADa=20Veytia=20=28Puerco=29?= <adolfo.garcia@uservers.net> Date: Tue, 10 Jan 2023 01:11:58 -0600 Subject: [PATCH 2/2] Rename module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vexctl is now split out from vex and now named github.com/openvex/vexctl Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net> --- go.mod | 10 +++++----- go.sum | 3 ++- main.go | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index a405752..ae170f1 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,15 @@ -module chainguard.dev/vex +module github.com/openvex/vexctl go 1.19 require ( + chainguard.dev/vex v0.1.0 github.com/google/go-containerregistry v0.12.1 - github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add github.com/owenrumney/go-sarif v1.1.1 github.com/secure-systems-lab/go-securesystemslib v0.4.0 github.com/sigstore/cosign v1.13.1 - github.com/sigstore/sigstore v1.5.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.6.1 - gopkg.in/yaml.v3 v3.0.1 sigs.k8s.io/release-utils v0.7.3 ) @@ -131,6 +129,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add // indirect github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect github.com/jhump/protoreflect v1.14.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -164,12 +163,12 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect github.com/segmentio/ksuid v1.0.4 // indirect github.com/sigstore/fulcio v0.6.0 // indirect github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2 // indirect + github.com/sigstore/sigstore v1.5.0 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/spf13/afero v1.8.2 // indirect @@ -238,6 +237,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.23.5 // indirect k8s.io/apimachinery v0.23.5 // indirect k8s.io/client-go v0.23.5 // indirect diff --git a/go.sum b/go.sum index 677dc21..4b1b79f 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxo bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk= bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= +chainguard.dev/vex v0.1.0 h1:nxOUH65+OjBQ2Vph+8u5qpf7YRyT2XUtLnp27Q43XcM= +chainguard.dev/vex v0.1.0/go.mod h1:uNzgmAtDI3UkKkzJrVetp5bq6bpJ5vvYU4JybJxaF6I= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -1140,7 +1142,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= diff --git a/main.go b/main.go index 8092d2f..858368f 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 package main import ( - "chainguard.dev/vex/internal/cmd" + "github.com/openvex/vexctl/internal/cmd" ) func main() {