diff --git a/server/static/css/styles.css b/server/static/css/styles.css index 6f696e5e..a392ffa0 100644 --- a/server/static/css/styles.css +++ b/server/static/css/styles.css @@ -194,7 +194,7 @@ td { td:last-child, th:last-child { text-align: right; - padding-right: 0px; + padding-right: 5px; } td a { display: block; @@ -205,6 +205,38 @@ tr.parent a { .parent a:hover { color: #2a2a2a; } + +/*------------------------------------*\ + Loading Indicator +\*------------------------------------*/ +.signal { + border: 2px solid #333; + border-radius: 15px; + height: 15px; + left: 50%; + margin: -8px 0 0 -8px; + opacity: 0; + top: 50%; + width: 15px; + float: right; + animation: pulsate 1s ease-out; + animation-iteration-count: infinite; +} + +@keyframes pulsate { + 0% { + transform: scale(.1); + opacity: 0.0; + } + 50% { + opacity: 1; + } + 100% { + transform: scale(1.2); + opacity: 0; + } +} + /*------------------------------------*\ Footer \*------------------------------------*/ diff --git a/server/static/js/scripts.js b/server/static/js/scripts.js index bab6c4e0..49a68e0b 100644 --- a/server/static/js/scripts.js +++ b/server/static/js/scripts.js @@ -35,6 +35,24 @@ function search(search_val){ } } +function loadVulnerabilityCount(url){ + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onload = function() { + if (xhr.status === 200) { + var report = JSON.parse(xhr.responseText); + var id = report.Repo + ':' + report.Tag; + var element = document.getElementById(id); + + if (element) { + element.innerHTML = report.BadVulns; + } else { + console.log("element not found for given id ", id); + } + } + }; + xhr.send(); +} var el = document.querySelectorAll('tr:nth-child(2)')[0].querySelectorAll('td:nth-child(2)')[0]; if (el.textContent == 'Parent Directory'){ @@ -72,22 +90,26 @@ our_table.setAttribute('id', 'directory'); var search_input = document.querySelectorAll('input[name="filter"]')[0]; var clear_button = document.querySelectorAll('a.clear')[0]; -if (search_input.value !== ''){ - search(search_input.value); +if (search_input) { + if (search_input.value !== ''){ + search(search_input.value); + } + + search_input.addEventListener('keyup', function(e){ + e.preventDefault(); + search(search_input.value); + }); + + search_input.addEventListener('keypress', function(e){ + if ( e.which == 13 ) { + e.preventDefault(); + } + }); } -search_input.addEventListener('keyup', function(e){ - e.preventDefault(); - search(search_input.value); -}); - -search_input.addEventListener('keypress', function(e){ - if ( e.which == 13 ) { - e.preventDefault(); - } -}); - -clear_button.addEventListener('click', function(e){ - search_input.value = ''; - search(''); -}); +if (clear_button) { + clear_button.addEventListener('click', function(e){ + search_input.value = ''; + search(''); + }); +} diff --git a/server/templates/echo/tags.html b/server/templates/echo/tags.html index 90249737..255354a0 100644 --- a/server/templates/echo/tags.html +++ b/server/templates/echo/tags.html @@ -37,9 +37,9 @@ {{ $value.Created.Format "02 Jan, 2006 15:04:05 UTC" }} - - - {{ $value.VulnerabilityReport.BadVulns }} + + +
@@ -51,6 +51,18 @@ @jessfraz + -{{end}} \ No newline at end of file +{{end}} diff --git a/server/web.go b/server/web.go index e9398f66..66f90b2e 100644 --- a/server/web.go +++ b/server/web.go @@ -103,6 +103,7 @@ func listenAndServe(port, keyfile, certfile string, r *registry.Registry, c *cla e.GET("/repo/:repo", rc.tags) e.GET("/repo/:repo/:tag", rc.tag) e.GET("/repo/:repo/:tag/vulns", rc.vulnerabilities) + e.GET("/repo/:repo/:tag/report", rc.report) srv := &http.Server{ Addr: ":" + port, @@ -260,6 +261,58 @@ func (rc *registryController) tags(c echo.Context) error { return err } +func (rc *registryController) report(c echo.Context) error { + repo, err := url.QueryUnescape(c.Param("repo")) + if err != nil { + return c.String(http.StatusNotFound, "Given repo can not be unescaped.") + } + if repo == "" { + return c.String(http.StatusNotFound, "No repo given") + } + tag := c.Param("tag") + if tag == "" { + return c.String(http.StatusNotFound, "No tag given") + } + + m1, err := r.ManifestV1(repo, tag) + if err != nil { + log.WithFields(log.Fields{ + "error": err, + "repo": repo, + "tag": tag, + }).Warn("getting v1 manifest failed") + } + + for _, h := range m1.History { + var comp v1Compatibility + if err := json.Unmarshal([]byte(h.V1Compatibility), &comp); err != nil { + msg := "unmarshal v1compatibility failed" + log.WithFields(log.Fields{ + "error": err, + "repo": repo, + "tag": tag, + }).Warn(msg) + return c.String(http.StatusInternalServerError, msg) + } + break + } + + result := clair.VulnerabilityReport{} + + if rc.cl != nil { + result, err = rc.cl.Vulnerabilities(rc.reg, repo, tag, m1) + if err != nil { + log.WithFields(log.Fields{ + "error": err, + "repo": repo, + "tag": tag, + }).Error("error during vulnerability scanning.") + } + } + + return c.JSON(http.StatusOK, result) +} + func (rc *registryController) vulnerabilities(c echo.Context) error { repo, err := url.QueryUnescape(c.Param("repo")) if err != nil {