// saveFields appends cayley's Save method to a path for each field in // selectedFields, except the ones that appears also in exceptFields func saveFields(p *path.Path, selectedFields []string, exceptFields []string) { for _, selectedField := range selectedFields { if utils.Contains(selectedField, exceptFields) { continue } p = p.Save(selectedField, selectedField) } }
// toLayers converts a path leading to one or multiple layers to Layer structs, // selecting the specified fields func toLayers(path *path.Path, selectedFields []string) ([]*Layer, error) { var layers []*Layer saveFields(path, selectedFields, []string{FieldLayerSuccessors, FieldLayerPackages, FieldLayerInstalledPackages, FieldLayerRemovedPackages}) it, _ := path.BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) layer := Layer{Node: store.NameOf(it.Result())} for _, selectedField := range selectedFields { switch selectedField { case FieldLayerID: layer.ID = store.NameOf(tags[FieldLayerID]) case FieldLayerParent: layer.ParentNode = store.NameOf(tags[FieldLayerParent]) case FieldLayerSuccessors: var err error layer.SuccessorsNodes, err = toValues(cayley.StartPath(store, layer.Node).In(FieldLayerParent)) if err != nil { log.Errorf("could not get successors of layer %s: %s.", layer.Node, err.Error()) return nil, err } case FieldLayerOS: layer.OS = store.NameOf(tags[FieldLayerOS]) case FieldLayerPackages: var err error it, _ := cayley.StartPath(store, layer.Node).OutWithTags([]string{"predicate"}, FieldLayerInstalledPackages, FieldLayerRemovedPackages).BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) predicate := store.NameOf(tags["predicate"]) if predicate == FieldLayerInstalledPackages { layer.InstalledPackagesNodes = append(layer.InstalledPackagesNodes, store.NameOf(it.Result())) } else if predicate == FieldLayerRemovedPackages { layer.RemovedPackagesNodes = append(layer.RemovedPackagesNodes, store.NameOf(it.Result())) } } if it.Err() != nil { log.Errorf("could not get installed/removed packages of layer %s: %s.", layer.Node, it.Err()) return nil, err } case FieldLayerEngineVersion: layer.EngineVersion, _ = strconv.Atoi(store.NameOf(tags[FieldLayerEngineVersion])) default: panic("unknown selectedField") } } layers = append(layers, &layer) } if it.Err() != nil { log.Errorf("failed query in toLayers: %s", it.Err()) return []*Layer{}, ErrBackendException } return layers, nil }
// toValues returns multiple values from a path // If the path does not lead to any value, an empty array is returned // If a database error occurs, an empty array and an error are returned func toValues(p *path.Path) ([]string, error) { var values []string it, _ := p.BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { if it.Result() != nil { values = append(values, store.NameOf(it.Result())) } } if it.Err() != nil { log.Errorf("failed query in toValues: %s", it.Err()) return []string{}, ErrBackendException } return values, nil }
// toValue returns a single value from a path // If the path does not lead to a value, an empty string is returned // If the path leads to multiple values or if a database error occurs, an empty string and an error are returned func toValue(p *path.Path) (string, error) { var value string it, _ := p.BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { if value != "" { log.Error("failed query in toValue: used on an iterator containing multiple values") return "", ErrInconsistent } if it.Result() != nil { value = store.NameOf(it.Result()) } } if it.Err() != nil { log.Errorf("failed query in toValue: %s", it.Err()) return "", ErrBackendException } return value, nil }
// toPackages converts a path leading to one or multiple packages to Package structs, selecting the specified fields func toPackages(path *path.Path, selectedFields []string) ([]*Package, error) { var packages []*Package var err error saveFields(path, selectedFields, []string{FieldPackagePreviousVersion}) it, _ := path.BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) pkg := Package{Node: store.NameOf(it.Result())} for _, selectedField := range selectedFields { switch selectedField { case FieldPackageOS: pkg.OS = store.NameOf(tags[FieldPackageOS]) case FieldPackageName: pkg.Name = store.NameOf(tags[FieldPackageName]) case FieldPackageVersion: pkg.Version, err = types.NewVersion(store.NameOf(tags[FieldPackageVersion])) if err != nil { log.Warningf("could not parse version of package %s: %s", pkg.Node, err.Error()) } case FieldPackageNextVersion: pkg.NextVersionNode = store.NameOf(tags[FieldPackageNextVersion]) case FieldPackagePreviousVersion: pkg.PreviousVersionNode, err = toValue(cayley.StartPath(store, pkg.Node).In(FieldPackageNextVersion)) if err != nil { log.Warningf("could not get previousVersion on package %s: %s.", pkg.Node, err.Error()) return []*Package{}, ErrInconsistent } default: panic("unknown selectedField") } } packages = append(packages, &pkg) } if it.Err() != nil { log.Errorf("failed query in toPackages: %s", it.Err()) return []*Package{}, ErrBackendException } return packages, nil }
// toVulnerabilities converts a path leading to one or multiple vulnerabilities to Vulnerability structs, selecting the specified fields func toVulnerabilities(path *path.Path, selectedFields []string) ([]*Vulnerability, error) { var vulnerabilities []*Vulnerability saveFields(path, selectedFields, []string{FieldVulnerabilityFixedIn, FieldVulnerabilityCausedByPackage}) it, _ := path.BuildIterator().Optimize() defer it.Close() for cayley.RawNext(it) { tags := make(map[string]graph.Value) it.TagResults(tags) vulnerability := Vulnerability{Node: store.NameOf(it.Result())} for _, selectedField := range selectedFields { switch selectedField { case FieldVulnerabilityID: vulnerability.ID = store.NameOf(tags[FieldVulnerabilityID]) case FieldVulnerabilityLink: vulnerability.Link = store.NameOf(tags[FieldVulnerabilityLink]) case FieldVulnerabilityPriority: vulnerability.Priority = types.Priority(store.NameOf(tags[FieldVulnerabilityPriority])) case FieldVulnerabilityDescription: vulnerability.Description = store.NameOf(tags[FieldVulnerabilityDescription]) case FieldVulnerabilityFixedIn: var err error vulnerability.FixedInNodes, err = toValues(cayley.StartPath(store, vulnerability.Node).Out(FieldVulnerabilityFixedIn)) if err != nil { log.Errorf("could not get fixedIn on vulnerability %s: %s.", vulnerability.Node, err.Error()) return []*Vulnerability{}, err } case FieldVulnerabilityCausedByPackage: vulnerability.CausedByPackage = store.NameOf(tags[FieldVulnerabilityCausedByPackage]) default: panic("unknown selectedField") } } vulnerabilities = append(vulnerabilities, &vulnerability) } if it.Err() != nil { log.Errorf("failed query in toVulnerabilities: %s", it.Err()) return []*Vulnerability{}, ErrBackendException } return vulnerabilities, nil }
func buildPathFromObject(obj *otto.Object) *path.Path { var p *path.Path val, _ := obj.Get("_gremlin_type") stringArgs := propertiesOf(obj, "string_args") gremlinType := val.String() if prev, _ := obj.Get("_gremlin_prev"); !prev.IsObject() { switch gremlinType { case "vertex": return path.StartMorphism(stringArgs...) case "morphism": return path.StartMorphism() default: panic("No base gremlin path other than 'vertex' or 'morphism'") } } else { p = buildPathFromObject(prev.Object()) } if p == nil { return nil } switch gremlinType { case "Is": return p.Is(stringArgs...) case "In": preds, tags, ok := getViaData(obj) if !ok { return nil } return p.InWithTags(tags, preds...) case "Out": preds, tags, ok := getViaData(obj) if !ok { return nil } return p.OutWithTags(tags, preds...) case "Both": preds, _, ok := getViaData(obj) if !ok { return nil } return p.Both(preds...) case "Follow": subobj := getFirstArgAsMorphismChain(obj) if subobj == nil { return nil } return p.Follow(buildPathFromObject(subobj)) case "FollowR": subobj := getFirstArgAsMorphismChain(obj) if subobj == nil { return nil } return p.FollowReverse(buildPathFromObject(subobj)) case "And", "Intersect": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.And(buildPathFromObject(subobj)) case "Union", "Or": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.Or(buildPathFromObject(subobj)) case "Back": if len(stringArgs) != 1 { return nil } return p.Back(stringArgs[0]) case "Tag", "As": return p.Tag(stringArgs...) case "Has": if len(stringArgs) < 2 { return nil } return p.Has(stringArgs[0], stringArgs[1:]...) case "Save", "SaveR": if len(stringArgs) > 2 || len(stringArgs) == 0 { return nil } tag := stringArgs[0] if len(stringArgs) == 2 { tag = stringArgs[1] } if gremlinType == "SaveR" { return p.SaveReverse(stringArgs[0], tag) } return p.Save(stringArgs[0], tag) case "Except", "Difference": subobj := getFirstArgAsVertexChain(obj) if subobj == nil { return nil } return p.Except(buildPathFromObject(subobj)) case "InPredicates": return p.InPredicates() case "OutPredicates": return p.OutPredicates() case "LabelContext": labels, tags, ok := getViaData(obj) if !ok { return nil } return p.LabelContextWithTags(tags, labels...) default: panic(fmt.Sprint("Unimplemented Gremlin function", gremlinType)) } }