Skip to content

Commit

Permalink
Ingest and Query vulnerabilities for arango backend (#1052)
Browse files Browse the repository at this point in the history
* add builk ingestion for cve, ghsa, and osv

Signed-off-by: pxp928 <parth.psu@gmail.com>

* update arangoDB with collections and indexes for vuln nodes

Signed-off-by: pxp928 <parth.psu@gmail.com>

* enforce lowercase for vulnerabilities

Signed-off-by: pxp928 <parth.psu@gmail.com>

---------

Signed-off-by: pxp928 <parth.psu@gmail.com>
  • Loading branch information
pxp928 authored Jul 18, 2023
1 parent 74cc02e commit 3a24c08
Show file tree
Hide file tree
Showing 26 changed files with 1,016 additions and 31 deletions.
1 change: 0 additions & 1 deletion pkg/assembler/backends/arangodb/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ RETURN NEW`
defer cursor.Close()

return getArtifacts(ctx, cursor)

}

func (c *arangoClient) IngestArtifact(ctx context.Context, artifact *model.ArtifactInputSpec) (*model.Artifact, error) {
Expand Down
75 changes: 45 additions & 30 deletions pkg/assembler/backends/arangodb/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ const (
srcHasNameStr string = "srcHasName"
srcNamesStr string = "srcNames"

// builder collections

buildersStr string = "builders"

// artifact collection

artifactsStr string = "artifacts"

// cve collection

cvesStr string = "cves"

// ghsa collection

ghsasStr string = "ghsas"

// osv collection

osvsStr string = "osvs"

// isDependency collections

isDependencyEdgesStr string = "isDependencyEdges"
Expand All @@ -76,26 +96,20 @@ const (
hasSLSAEdgesStr string = "hasSLSAEdges"
hasSLSAsStr string = "hasSLSAs"

// builder collections

buildersStr string = "builders"

// hashEquals collections

hashEqualsEdgesStr string = "hashEqualsEdges"

// artifact collection

artifactsStr string = "artifacts"
hashEqualsStr string = "hashEquals"

// hasSBOM collection

hasSBOMEdgesStr string = "hasSBOMEdges"
hasSBOMsStr string = "hasSBOMs"

// hashEqual collection
// certifyVuln collection

hashEqualsStr string = "hashEquals"
certifyVulnEdgesStr string = "certifyVulnEdges"
certifyVulnsStr string = "certifyVulns"
)

type ArangoConfig struct {
Expand Down Expand Up @@ -303,10 +317,18 @@ func GetBackend(ctx context.Context, args backends.BackendArgs) (backends.Backen
// repeat this for the collections where an edge is going into
hasSBOMEdges.To = []string{hasSBOMsStr}

var certifyVulnEdges driver.EdgeDefinition
certifyVulnEdges.Collection = certifyVulnEdgesStr
// define a set of collections where an edge is going out...
certifyVulnEdges.From = []string{pkgVersionsStr, certifyVulnsStr}

// repeat this for the collections where an edge is going into
certifyVulnEdges.To = []string{certifyVulnsStr, cvesStr, ghsasStr, osvsStr}

// A graph can contain additional vertex collections, defined in the set of orphan collections
var options driver.CreateGraphOptions
options.EdgeDefinitions = []driver.EdgeDefinition{hashEqualsEdges, pkgHasType, pkgHasNamespace, pkgHasName,
pkgHasVersion, srcHasType, srcHasNamespace, srcHasName, isDependencyEdges, isOccurrencesEdges, hasSBOMEdges, hasSLSAEdges}
pkgHasVersion, srcHasType, srcHasNamespace, srcHasName, isDependencyEdges, isOccurrencesEdges, hasSBOMEdges, hasSLSAEdges, certifyVulnEdges}

// create a graph
graph, err = db.CreateGraphV2(ctx, "guac", &options)
Expand All @@ -327,6 +349,18 @@ func GetBackend(ctx context.Context, args backends.BackendArgs) (backends.Backen
return nil, fmt.Errorf("failed to generate index for builders: %w", err)
}

if err := createIndexPerCollection(ctx, db, cvesStr, []string{"cveID"}, false, "byCveID"); err != nil {
return nil, fmt.Errorf("failed to generate index for cves: %w", err)
}

if err := createIndexPerCollection(ctx, db, ghsasStr, []string{"ghsaID"}, false, "byGhsaID"); err != nil {
return nil, fmt.Errorf("failed to generate index for ghsas: %w", err)
}

if err := createIndexPerCollection(ctx, db, osvsStr, []string{"osvID"}, false, "byOsvID"); err != nil {
return nil, fmt.Errorf("failed to generate index for osvs: %w", err)
}

if err := createIndexPerCollection(ctx, db, hashEqualsStr, []string{"artifactID", "equalArtifactID"}, true, "byArtIDEqualArtID"); err != nil {
return nil, fmt.Errorf("failed to generate index for hashEquals: %w", err)
}
Expand Down Expand Up @@ -695,16 +729,6 @@ func getPreloadString(prefix, name string) string {
return name
}

func (c *arangoClient) Cve(ctx context.Context, cveSpec *model.CVESpec) ([]*model.Cve, error) {
panic(fmt.Errorf("not implemented: Cve - Cve"))
}
func (c *arangoClient) Ghsa(ctx context.Context, ghsaSpec *model.GHSASpec) ([]*model.Ghsa, error) {
panic(fmt.Errorf("not implemented: Ghsa - Ghsa"))
}
func (c *arangoClient) Osv(ctx context.Context, osvSpec *model.OSVSpec) ([]*model.Osv, error) {
panic(fmt.Errorf("not implemented: Osv - Osv"))
}

// Retrieval read-only queries for evidence trees
func (c *arangoClient) CertifyBad(ctx context.Context, certifyBadSpec *model.CertifyBadSpec) ([]*model.CertifyBad, error) {
panic(fmt.Errorf("not implemented: CertifyBad - CertifyBad"))
Expand Down Expand Up @@ -738,18 +762,9 @@ func (c *arangoClient) Scorecards(ctx context.Context, certifyScorecardSpec *mod

// Mutations for software trees (read-write queries)

func (c *arangoClient) IngestCve(ctx context.Context, cve *model.CVEInputSpec) (*model.Cve, error) {
panic(fmt.Errorf("not implemented: IngestCve - IngestCve"))
}
func (c *arangoClient) IngestGhsa(ctx context.Context, ghsa *model.GHSAInputSpec) (*model.Ghsa, error) {
panic(fmt.Errorf("not implemented: IngestGhsa - IngestGhsa"))
}
func (c *arangoClient) IngestMaterials(ctx context.Context, materials []*model.ArtifactInputSpec) ([]*model.Artifact, error) {
return nil, nil
}
func (c *arangoClient) IngestOsv(ctx context.Context, osv *model.OSVInputSpec) (*model.Osv, error) {
panic(fmt.Errorf("not implemented: IngestOsv - IngestOsv"))
}

// Mutations for evidence trees (read-write queries, assume software trees ingested)
func (c *arangoClient) CertifyScorecard(ctx context.Context, source model.SourceInputSpec, scorecard model.ScorecardInputSpec) (*model.CertifyScorecard, error) {
Expand Down
148 changes: 148 additions & 0 deletions pkg/assembler/backends/arangodb/cve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//
// 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.

package arangodb

import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/arangodb/go-driver"
"github.com/guacsec/guac/pkg/assembler/graphql/model"
)

func (c *arangoClient) Cve(ctx context.Context, cveSpec *model.CVESpec) ([]*model.Cve, error) {
values := map[string]any{}
arangoQueryBuilder := newForQuery(cvesStr, "cve")
if cveSpec.Year != nil {
arangoQueryBuilder.filter("cve", "year", "==", "@year")
values["year"] = *cveSpec.Year
}
if cveSpec.CveID != nil {
arangoQueryBuilder.filter("cve", "cveId", "==", "@cveId")
values["cveId"] = strings.ToLower(*cveSpec.CveID)
}
arangoQueryBuilder.query.WriteString("\n")
arangoQueryBuilder.query.WriteString(`RETURN {
"id": cve._id,
"year": cve.year,
"cveID": cve.cveId
}`)

fmt.Println(arangoQueryBuilder.string())
cursor, err := executeQueryWithRetry(ctx, c.db, arangoQueryBuilder.string(), values, "Cve")
if err != nil {
return nil, fmt.Errorf("failed to query for cve: %w", err)
}
defer cursor.Close()

return getCVEs(ctx, cursor)
}

func getCVEQueryValues(cve *model.CVEInputSpec) map[string]any {
values := map[string]any{}
values["year"] = cve.Year
values["cveId"] = strings.ToLower(cve.CveID)
return values
}

func (c *arangoClient) IngestCVEs(ctx context.Context, cves []*model.CVEInputSpec) ([]*model.Cve, error) {
listOfValues := []map[string]any{}
for i := range cves {
listOfValues = append(listOfValues, getCVEQueryValues(cves[i]))
}

var documents []string
for _, val := range listOfValues {
bs, _ := json.Marshal(val)
documents = append(documents, string(bs))
}

queryValues := map[string]any{}
queryValues["documents"] = fmt.Sprint(strings.Join(documents, ","))

var sb strings.Builder

sb.WriteString("for doc in [")
for i, val := range listOfValues {
bs, _ := json.Marshal(val)
if i == len(listOfValues)-1 {
sb.WriteString(string(bs))
} else {
sb.WriteString(string(bs) + ",")
}
}
sb.WriteString("]")

query := `
UPSERT { year:doc.year, cveId:doc.cveId }
INSERT { year:doc.year, cveId:doc.cveId }
UPDATE {} IN cves OPTIONS { indexHint: "byCveID" }
RETURN NEW`

sb.WriteString(query)

cursor, err := executeQueryWithRetry(ctx, c.db, sb.String(), nil, "IngestCVEs")
if err != nil {
return nil, fmt.Errorf("failed to ingest cve: %w", err)
}
defer cursor.Close()

return getCVEs(ctx, cursor)
}

func (c *arangoClient) IngestCve(ctx context.Context, cve *model.CVEInputSpec) (*model.Cve, error) {
query := `
UPSERT { year:@year, cveId:@cveId }
INSERT { year:@year, cveId:@cveId }
UPDATE {} IN cves OPTIONS { indexHint: "byCveID" }
RETURN NEW`

cursor, err := executeQueryWithRetry(ctx, c.db, query, getCVEQueryValues(cve), "IngestCve")
if err != nil {
return nil, fmt.Errorf("failed to ingest cve: %w", err)
}
defer cursor.Close()

createdCVEs, err := getCVEs(ctx, cursor)
if err != nil {
return nil, fmt.Errorf("failed to get cves from arango cursor: %w", err)
}
if len(createdCVEs) == 1 {
return createdCVEs[0], nil
} else {
return nil, fmt.Errorf("number of cves ingested is greater than one")
}
}

func getCVEs(ctx context.Context, cursor driver.Cursor) ([]*model.Cve, error) {
var createdCVEs []*model.Cve
for {
var doc *model.Cve
_, err := cursor.ReadDocument(ctx, &doc)
if err != nil {
if driver.IsNoMoreDocuments(err) {
break
} else {
return nil, fmt.Errorf("failed to get cve from cursor: %w", err)
}
} else {
createdCVEs = append(createdCVEs, doc)
}
}
return createdCVEs, nil
}
Loading

0 comments on commit 3a24c08

Please sign in to comment.