// Copyright 2015 clair 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 logic import ( "errors" "net/http" "github.com/coreos/clair/api/jsonhttp" "github.com/coreos/clair/database" cerrors "github.com/coreos/clair/utils/errors" "github.com/julienschmidt/httprouter" ) // GETVulnerabilities returns a vulnerability identified by an ID if it exists. func GETVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params) { // Find vulnerability. vulnerability, err := database.FindOneVulnerability(p.ByName("id"), []string{database.FieldVulnerabilityID, database.FieldVulnerabilityLink, database.FieldVulnerabilityPriority, database.FieldVulnerabilityDescription, database.FieldVulnerabilityFixedIn}) if err != nil { jsonhttp.RenderError(w, 0, err) return } abstractVulnerability, err := vulnerability.ToAbstractVulnerability() if err != nil { jsonhttp.RenderError(w, 0, err) return } jsonhttp.Render(w, http.StatusOK, abstractVulnerability) } // POSTVulnerabilities manually inserts a vulnerability into the database if it // does not exist yet. func POSTVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params) { var parameters *database.AbstractVulnerability if s, err := jsonhttp.ParseBody(r, ¶meters); err != nil { jsonhttp.RenderError(w, s, err) return } // Ensure that the vulnerability does not exist. vulnerability, err := database.FindOneVulnerability(parameters.ID, []string{}) if err != nil && err != cerrors.ErrNotFound { jsonhttp.RenderError(w, 0, err) return } if vulnerability != nil { jsonhttp.RenderError(w, 0, cerrors.NewBadRequestError("vulnerability already exists")) return } // Insert packages. packages := database.AbstractPackagesToPackages(parameters.AffectedPackages) err = database.InsertPackages(packages) if err != nil { jsonhttp.RenderError(w, 0, err) return } var pkgNodes []string for _, p := range packages { pkgNodes = append(pkgNodes, p.Node) } // Insert vulnerability. notifications, err := database.InsertVulnerabilities([]*database.Vulnerability{parameters.ToVulnerability(pkgNodes)}) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Insert notifications. err = database.InsertNotifications(notifications, database.GetDefaultNotificationWrapper()) if err != nil { jsonhttp.RenderError(w, 0, err) return } jsonhttp.Render(w, http.StatusCreated, nil) } // PUTVulnerabilities updates a vulnerability if it exists. func PUTVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params) { var parameters *database.AbstractVulnerability if s, err := jsonhttp.ParseBody(r, ¶meters); err != nil { jsonhttp.RenderError(w, s, err) return } parameters.ID = p.ByName("id") // Ensure that the vulnerability exists. _, err := database.FindOneVulnerability(parameters.ID, []string{}) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Insert packages. packages := database.AbstractPackagesToPackages(parameters.AffectedPackages) err = database.InsertPackages(packages) if err != nil { jsonhttp.RenderError(w, 0, err) return } var pkgNodes []string for _, p := range packages { pkgNodes = append(pkgNodes, p.Node) } // Insert vulnerability. notifications, err := database.InsertVulnerabilities([]*database.Vulnerability{parameters.ToVulnerability(pkgNodes)}) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Insert notifications. err = database.InsertNotifications(notifications, database.GetDefaultNotificationWrapper()) if err != nil { jsonhttp.RenderError(w, 0, err) return } jsonhttp.Render(w, http.StatusCreated, nil) } // DELVulnerabilities deletes a vulnerability if it exists. func DELVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params) { err := database.DeleteVulnerability(p.ByName("id")) if err != nil { jsonhttp.RenderError(w, 0, err) return } jsonhttp.Render(w, http.StatusNoContent, nil) } // GETVulnerabilitiesIntroducingLayers returns the list of layers that // introduces a given vulnerability, if it exists. // To clarify, it does not return the list of every layers that have // the vulnerability. func GETVulnerabilitiesIntroducingLayers(w http.ResponseWriter, r *http.Request, p httprouter.Params) { // Find vulnerability to verify that it exists. _, err := database.FindOneVulnerability(p.ByName("id"), []string{}) if err != nil { jsonhttp.RenderError(w, 0, err) return } layers, err := database.FindAllLayersIntroducingVulnerability(p.ByName("id"), []string{database.FieldLayerID}) if err != nil { jsonhttp.RenderError(w, 0, err) return } layersIDs := []string{} for _, l := range layers { layersIDs = append(layersIDs, l.ID) } jsonhttp.Render(w, http.StatusOK, struct{ IntroducingLayersIDs []string }{IntroducingLayersIDs: layersIDs}) } // POSTVulnerabilitiesAffectedLayersParameters represents the expected // parameters for POSTVulnerabilitiesAffectedLayers. type POSTVulnerabilitiesAffectedLayersParameters struct { LayersIDs []string } // POSTVulnerabilitiesAffectedLayers returns whether the specified layers // (by their IDs) are vulnerable to the given Vulnerability or not. func POSTVulnerabilitiesAffectedLayers(w http.ResponseWriter, r *http.Request, p httprouter.Params) { // Parse body. var parameters POSTBatchLayersVulnerabilitiesParameters if s, err := jsonhttp.ParseBody(r, ¶meters); err != nil { jsonhttp.RenderError(w, s, err) return } if len(parameters.LayersIDs) == 0 { jsonhttp.RenderError(w, http.StatusBadRequest, errors.New("getting the entire list of affected layers is not supported yet: at least one LayerID query parameter must be provided")) return } // Find vulnerability. vulnerability, err := database.FindOneVulnerability(p.ByName("id"), []string{database.FieldVulnerabilityFixedIn}) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Save the fixed in nodes into a map for fast check. fixedInPackagesMap := make(map[string]struct{}) for _, fixedInNode := range vulnerability.FixedInNodes { fixedInPackagesMap[fixedInNode] = struct{}{} } response := make(map[string]interface{}) // For each LayerID parameter. for _, layerID := range parameters.LayersIDs { // Find layer layer, err := database.FindOneLayerByID(layerID, []string{database.FieldLayerParent, database.FieldLayerPackages, database.FieldLayerPackages}) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Find layer's packages. packagesNodes, err := layer.AllPackages() if err != nil { jsonhttp.RenderError(w, 0, err) return } // Get successors packages of layer' packages. successors, err := getSuccessorsFromPackagesNodes(packagesNodes) if err != nil { jsonhttp.RenderError(w, 0, err) return } // Determine if the layer is vulnerable by verifying if one of the successors // of its packages are fixed by the vulnerability. vulnerable := false for _, p := range successors { if _, fixed := fixedInPackagesMap[p]; fixed { vulnerable = true break } } response[layerID] = struct{ Vulnerable bool }{Vulnerable: vulnerable} } jsonhttp.Render(w, http.StatusOK, response) }