Skip to content

Commit

Permalink
feat(api, ui): add vulnerability trend (#3130)
Browse files Browse the repository at this point in the history
  • Loading branch information
sguiheux authored and fsamin committed Aug 3, 2018
1 parent 1dc3aa3 commit 4861e20
Show file tree
Hide file tree
Showing 32 changed files with 727 additions and 111 deletions.
2 changes: 1 addition & 1 deletion contrib/plugins/plugin-clair/clair.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (d ClairPlugin) Run(a plugin.IJob) plugin.Result {
}
}

report := sdk.VulnerabilityReport{
report := sdk.VulnerabilityWorkerReport{
Vulnerabilities: vulnerabilities,
Summary: summary,
}
Expand Down
3 changes: 2 additions & 1 deletion contrib/plugins/plugin-npm-audit-parser/npm-audit-parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (d NpmAuditParserPlugin) Run(j plugin.IJob) plugin.Result {
return plugin.Fail
}

var report sdk.VulnerabilityReport
var report sdk.VulnerabilityWorkerReport
summary := make(map[string]int64)
for _, a := range npmAudit.Advisories {
for _, f := range a.Findings {
Expand Down Expand Up @@ -94,6 +94,7 @@ func (d NpmAuditParserPlugin) Run(j plugin.IJob) plugin.Result {

}
}
report.Summary = summary
if err := plugin.SendVulnerabilityReport(j, report); err != nil {
_ = plugin.SendLog(j, "Unable to send report: %s", err)
return plugin.Fail
Expand Down
1 change: 1 addition & 0 deletions engine/api/api_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func (api *API) InitRouter() {
r.Handle("/project/{key}/application/{permApplicationName}/variable/audit", r.GET(api.getVariablesAuditInApplicationHandler))
r.Handle("/project/{key}/application/{permApplicationName}/variable/{name}", r.GET(api.getVariableInApplicationHandler), r.POST(api.addVariableInApplicationHandler), r.PUT(api.updateVariableInApplicationHandler), r.DELETE(api.deleteVariableFromApplicationHandler))
r.Handle("/project/{key}/application/{permApplicationName}/variable/{name}/audit", r.GET(api.getVariableAuditInApplicationHandler))
r.Handle("/project/{key}/application/{permApplicationName}/vulnerability/{id}", r.POST(api.postVulnerabilityHandler))
// Application deployment
r.Handle("/project/{key}/application/{permApplicationName}/deployment/config/{platform}", r.POST(api.postApplicationDeploymentStrategyConfigHandler, AllowProvider(true)), r.GET(api.getApplicationDeploymentStrategyConfigHandler), r.DELETE(api.deleteApplicationDeploymentStrategyConfigHandler))
r.Handle("/project/{key}/application/{permApplicationName}/deployment/config", r.GET(api.getApplicationDeploymentStrategiesConfigHandler))
Expand Down
4 changes: 4 additions & 0 deletions engine/api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ func (api *API) getApplicationHandler() Handler {
withKeys := FormBool(r, "withKeys")
withUsage := FormBool(r, "withUsage")
withDeploymentStrategies := FormBool(r, "withDeploymentStrategies")
withVulnerabilities := FormBool(r, "withVulnerabilities")
branchName := r.FormValue("branchName")
remote := r.FormValue("remote")
versionString := r.FormValue("version")
Expand All @@ -267,6 +268,9 @@ func (api *API) getApplicationHandler() Handler {
if withDeploymentStrategies {
loadOptions = append(loadOptions, application.LoadOptions.WithDeploymentStrategies)
}
if withVulnerabilities {
loadOptions = append(loadOptions, application.LoadOptions.WithVulnerabilities)
}

app, errApp := application.LoadByName(api.mustDB(), api.Cache, projectKey, applicationName, getUser(ctx), loadOptions...)
if errApp != nil {
Expand Down
53 changes: 42 additions & 11 deletions engine/api/application/application_vunerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,30 @@ import (
"github.com/ovh/cds/sdk"
)

// InsertVulnerability Insert a new vulnerability
func InsertVulnerability(db gorp.SqlExecutor, v sdk.Vulnerability) error {
dbVuln := dbApplicationVulnerability(v)
if err := db.Insert(&dbVuln); err != nil {
return sdk.WrapError(err, "InsertVulnerability> Unable to insert vulnerabilities")
// InsertVulnerabilities Insert vulnerabilities
func InsertVulnerabilities(db gorp.SqlExecutor, vs []sdk.Vulnerability, appID int64) error {
if _, err := db.Exec("DELETE FROM application_vulnerability WHERE application_id = $1", appID); err != nil {
return sdk.WrapError(err, "InsertVulnerability> Unable to remove old vulnerabilities")
}
for _, v := range vs {
v.ApplicationID = appID
dbVuln := dbApplicationVulnerability(v)
if err := db.Insert(&dbVuln); err != nil {
return sdk.WrapError(err, "InsertVulnerability> Unable to insert vulnerabilities")
}
}
return nil
}

// LoadVulnerabilitiesByRun loads vulnerabilities for the given run
func LoadVulnerabilitiesByRun(db gorp.SqlExecutor, nodeRunID int64) ([]sdk.Vulnerability, error) {
// LoadVulnerabilities load vulnerabilities for the given application
func LoadVulnerabilities(db gorp.SqlExecutor, appID int64) ([]sdk.Vulnerability, error) {
results := make([]dbApplicationVulnerability, 0)
query := `SELECT * FROM application_vulnerability
WHERE workflow_node_run_id=$1`
if _, err := db.Select(&results, query, nodeRunID); err != nil {
query := `SELECT *
FROM application_vulnerability
WHERE application_id = $1`
if _, err := db.Select(&results, query, appID); err != nil {
if err != sql.ErrNoRows {
return nil, sdk.WrapError(err, "LoadVulnerabilitiesByRun> unable to load vulnerabilities for run %d", nodeRunID)
return nil, sdk.WrapError(err, "LoadVulnerabilities> unable to load latest vulnerabilities for application %d", appID)
}
return nil, sdk.ErrNotFound
}
Expand All @@ -34,3 +41,27 @@ func LoadVulnerabilitiesByRun(db gorp.SqlExecutor, nodeRunID int64) ([]sdk.Vulne
}
return vulnerabilities, nil
}

// LoadVulnerability load the given vulnerability
func LoadVulnerability(db gorp.SqlExecutor, appID int64, vulnID int64) (sdk.Vulnerability, error) {
var dbVuln dbApplicationVulnerability
query := `SELECT *
FROM application_vulnerability
WHERE application_id = $1 AND id = $2`
if err := db.SelectOne(&dbVuln, query, appID, vulnID); err != nil {
if err != sql.ErrNoRows {
return sdk.Vulnerability{}, sdk.WrapError(err, "LoadVulnerability> unable to load vulnerability %d for application %d", vulnID, appID)
}
return sdk.Vulnerability{}, sdk.ErrNotFound
}
return sdk.Vulnerability(dbVuln), nil
}

// UpdateVulnerability updates a vulnerability
func UpdateVulnerability(db gorp.SqlExecutor, v sdk.Vulnerability) error {
dbVuln := dbApplicationVulnerability(v)
if _, err := db.Update(&dbVuln); err != nil {
return sdk.WrapError(err, "UpdateVulnerability> Unable to update vulnerability")
}
return nil
}
2 changes: 2 additions & 0 deletions engine/api/application/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var LoadOptions = struct {
WithClearKeys LoadOptionFunc
WithDeploymentStrategies LoadOptionFunc
WithClearDeploymentStrategies LoadOptionFunc
WithVulnerabilities LoadOptionFunc
}{
Default: &loadDefaultDependencies,
WithVariables: &loadVariables,
Expand All @@ -45,6 +46,7 @@ var LoadOptions = struct {
WithClearKeys: &loadClearKeys,
WithDeploymentStrategies: &loadDeploymentStrategies,
WithClearDeploymentStrategies: &loadDeploymentStrategiesWithClearPassword,
WithVulnerabilities: &loadVulnerabilities,
}

// LoadOldApplicationWorkflowToClean load application to clean
Expand Down
9 changes: 9 additions & 0 deletions engine/api/application/dao_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ var (
return nil
}

loadVulnerabilities = func(db gorp.SqlExecutor, store cache.Store, app *sdk.Application, u *sdk.User) error {
var err error
app.Vulnerabilities, err = LoadVulnerabilities(db, app.ID)
if err != nil && err != sql.ErrNoRows {
return sdk.WrapError(err, "application.loadVulnerabilities> Unable to load vulnerabilities")
}
return nil
}

loadDeploymentStrategiesWithClearPassword = func(db gorp.SqlExecutor, store cache.Store, app *sdk.Application, u *sdk.User) error {
var err error
app.DeploymentStrategies, err = LoadDeploymentStrategies(db, app.ID, true)
Expand Down
47 changes: 47 additions & 0 deletions engine/api/application_vulnerability.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package api

import (
"context"
"net/http"

"github.com/gorilla/mux"

"github.com/ovh/cds/engine/api/application"
"github.com/ovh/cds/sdk"
)

func (api *API) postVulnerabilityHandler() Handler {
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
key := vars["key"]
appName := vars["permApplicationName"]

vulnID, errV := requestVarInt(r, "id")
if errV != nil {
return sdk.WrapError(errV, "postVulnerabilityHandler> Unable to read ID")
}

var v sdk.Vulnerability
if err := UnmarshalBody(r, &v); err != nil {
return sdk.WrapError(err, "postVulnerabilityHandler> Unable to read body")
}

app, errL := application.LoadByName(api.mustDB(), api.Cache, key, appName, getUser(ctx))
if errL != nil {
return sdk.WrapError(errL, "postVulnerabilityHandler> Unable to load application")
}

vulnDB, errV := application.LoadVulnerability(api.mustDB(), app.ID, vulnID)
if errV != nil {
return sdk.WrapError(errV, "postVulnerabilityHandler> Unable to load vulnerability")
}

vulnDB.Ignored = v.Ignored

if err := application.UpdateVulnerability(api.mustDB(), vulnDB); err != nil {
return sdk.WrapError(err, "postVulnerabilityHandler> Unable to update vulnerability")
}

return WriteJSON(w, vulnDB, http.StatusOK)
}
}
65 changes: 65 additions & 0 deletions engine/api/application_vulnerability_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package api

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"

"bytes"
"fmt"
"github.com/ovh/cds/engine/api/application"
"github.com/ovh/cds/engine/api/test"
"github.com/ovh/cds/engine/api/test/assets"
"github.com/ovh/cds/sdk"
)

func Test_postVulnerabilityHandler(t *testing.T) {
api, db, router := newTestAPI(t)

//Create admin user
u, pass := assets.InsertAdminUser(api.mustDB())

//Insert Project
pkey := sdk.RandomString(10)
proj := assets.InsertTestProject(t, db, api.Cache, pkey, pkey, u)

app := &sdk.Application{
Name: sdk.RandomString(10),
}
if err := application.Insert(api.mustDB(), api.Cache, proj, app, u); err != nil {
t.Fatal(err)
}

v := sdk.Vulnerability{}
v.ApplicationID = app.ID

assert.NoError(t, application.InsertVulnerabilities(db, []sdk.Vulnerability{v}, app.ID))

vulns, err := application.LoadVulnerabilities(db, app.ID)
assert.NoError(t, err)

vars := map[string]string{
"key": proj.Key,
"permApplicationName": app.Name,
"id": fmt.Sprintf("%d", vulns[0].ID),
}

uri := router.GetRoute("POST", api.postVulnerabilityHandler, vars)

vulns[0].Ignored = true
jsonBody, _ := json.Marshal(vulns[0])
body := bytes.NewBuffer(jsonBody)

req, err := http.NewRequest("POST", uri, body)
test.NoError(t, err)
assets.AuthentifyRequest(t, req, u, pass)

// Do the request
w := httptest.NewRecorder()
router.Mux.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)

}
9 changes: 4 additions & 5 deletions engine/api/workflow/dao_node_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/go-gorp/gorp"
"github.com/ovh/venom"

"github.com/ovh/cds/engine/api/application"
"github.com/ovh/cds/engine/api/cache"
"github.com/ovh/cds/engine/api/database/gorpmapping"
"github.com/ovh/cds/engine/api/repositoriesmanager"
Expand Down Expand Up @@ -94,11 +93,11 @@ func LoadNodeRun(db gorp.SqlExecutor, projectkey, workflowname string, number, i
r.Coverage = cov
}
if loadOpts.WithVulnerabilities {
vulns, errV := application.LoadVulnerabilitiesByRun(db, r.ID)
if errV != nil && errV != sdk.ErrNotFound {
return nil, sdk.WrapError(errV, "LoadNodeRun>Error loading vulnerabilities for run %d", r.ID)
vuln, errV := loadVulnerabilityReport(db, r.ID)
if errV != nil {
return nil, sdk.WrapError(errV, "LoadNodeRun>Error vulnerability report coverage for run %d", r.ID)
}
r.Vulnerabilities = vulns
r.VulnerabilitiesReport = vuln
}
return r, nil

Expand Down
Loading

0 comments on commit 4861e20

Please sign in to comment.