From 8c910c6261074c727ef89e2b82d37cf882373b5a Mon Sep 17 00:00:00 2001 From: AH7 Date: Sun, 12 Aug 2018 17:00:13 +0300 Subject: [PATCH 1/2] feat: wip add snyk test to maven --- config/components.yml | 1 + config/snyk_auto_patch.yml | 22 +++ .../framework/snyk_auto_patch.rb | 135 ++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 config/snyk_auto_patch.yml create mode 100644 lib/java_buildpack/framework/snyk_auto_patch.rb diff --git a/config/components.yml b/config/components.yml index fea4f5b43e..b539fd0e8e 100644 --- a/config/components.yml +++ b/config/components.yml @@ -66,6 +66,7 @@ frameworks: - "JavaBuildpack::Framework::ProtectAppSecurityProvider" - "JavaBuildpack::Framework::RiverbedAppinternalsAgent" - "JavaBuildpack::Framework::SpringAutoReconfiguration" + - "JavaBuildpack::Framework::SnykAutoPatch" - "JavaBuildpack::Framework::SpringInsight" - "JavaBuildpack::Framework::SkyWalkingAgent" - "JavaBuildpack::Framework::YourKitProfiler" diff --git a/config/snyk_auto_patch.yml b/config/snyk_auto_patch.yml new file mode 100644 index 0000000000..3c653a4984 --- /dev/null +++ b/config/snyk_auto_patch.yml @@ -0,0 +1,22 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2017 the original author or 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. + +# Configuration for the Spring Auto Reconfiguration framework. +# Note that the repository is shared with the Play Auto Reconfiguration framework and should be kept in step to +# avoid conflicts. +--- +version: 1.+ +repository_root: "https://s3.amazonaws.com/snyk-cloudfoundry-artifacts/snyk-auto-patch" +enabled: true diff --git a/lib/java_buildpack/framework/snyk_auto_patch.rb b/lib/java_buildpack/framework/snyk_auto_patch.rb new file mode 100644 index 0000000000..2b6bca5f0e --- /dev/null +++ b/lib/java_buildpack/framework/snyk_auto_patch.rb @@ -0,0 +1,135 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2017 the original author or 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. + +require 'fileutils' +require 'yaml' +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/logging/logger_factory' +require 'java_buildpack/framework' +require 'net/http' +require 'json' +require 'rubygems' + +SNYK_API_URL = "https://snyk.io/api/v1/test/maven" + +module JavaBuildpack + module Framework + + # Encapsulates the detect, compile, and release functionality for enabling cloud auto-reconfiguration in Spring + # applications. + class SnykAutoPatch < JavaBuildpack::Component::VersionedDependencyComponent + + # Creates an instance + # + # @param [Hash] context a collection of utilities used the component + def initialize(context) + super(context) + @logger = JavaBuildpack::Logging::LoggerFactory.instance.get_logger SnykAutoPatch + end + + # (see JavaBuildpack::Component::BaseComponent#compile) + # This is to change the FS + def compile + pom_path = Dir.glob("#{@droplet.root}/**/pom.xml")[0] + uri = URI.parse(SNYK_API_URL) + req = Net::HTTP::Post.new(uri.to_s) + https = Net::HTTP.new(uri.host,uri.port) + https.use_ssl = true + req['Content-Type'] = 'application/json' + req['Authorization'] = 'token ' + @application.environment["SNYK_TOKEN"] + data = File.read(pom_path) + test_request = { + 'encoding' => 'plain', + 'files' => { + 'target' => { + "contents": "" + }, + } + } + test_request['files']['target']['contents'] = data + + additional = [] + jars = Dir.glob("#{@droplet.root}/WEB-INF/**/*.jar") + jars.each do |jar| + jar_pom_path = `unzip -Z1 #{jar} | grep "pom.xml"` + if (jar_pom_path.length) > 0 then + poms = jar_pom_path.split("\n") + poms.each do |pom| + pom_content = `unzip -p #{jar} #{pom}` + additional.push({"contents" => pom_content}) + end + end + end + test_request['files']['additional'] = additional; + + req.body = test_request.to_json + response = https.request(req) + res = JSON.parse(response.body) + if res['ok'] then + puts "Tested #{res['dependencyCount']} 0 vulnerabilties were found!" + else + issues = res.key?('issues') ? res['issues'] : res['vulnerabilities'] + vulns = issues['vulnerabilities'] + severityMap = { + 'high' => 3, + 'medium' => 2, + 'low' => 1 + } + vulns.sort! do |vuln_a, vuln_b| + vulna_map = severityMap[vuln_a['severity']] + vulnb_map = severityMap[vuln_b['severity']] + if (vulna_map > vulnb_map) + 1 + elsif (vulna_map < vulnb_map) + -1 + else + 0 + end + end + puts "\nFounded #{vulns.length} vulnerabilities on #{res['dependencyCount']} dependencies\n" + vulns.each do |vuln| + severity = vuln['severity'] + if (severity == 'high') then + color = "\e[31m" + elsif (severity == 'medium') then + color = "\e[1;33m" + else + color = "\e[34m" + end + puts "\n#{color}✗ #{severity.capitalize} severity vulnerabiltity found in #{vuln['package']}\e[0m" + puts " Description: #{severity} severity vulnerabiltity found in #{vuln['package']}" + puts " Info: #{vuln['url']}" + puts " Introduce through: #{vuln['from'][0]}\n" + end + + raise "Terminating droplet compilation as Snyk detected vulnerabilties..." + end + end + + # (see JavaBuildpack::Component::BaseComponent#release) + # This is for runtime configuration (Env var and etc..) + def release + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @configuration['enabled'] + end + end + + end +end From 83a1a1722b184781e3a30795159a30795a0e36a5 Mon Sep 17 00:00:00 2001 From: Antoine Arlaud Date: Sun, 16 Sep 2018 17:08:01 -0400 Subject: [PATCH 2/2] fix: Handling pomless cases, using the first pom in first jar file as main pom file for API call. --- .../framework/snyk_auto_patch.rb | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/java_buildpack/framework/snyk_auto_patch.rb b/lib/java_buildpack/framework/snyk_auto_patch.rb index 2b6bca5f0e..2d53dceb8e 100644 --- a/lib/java_buildpack/framework/snyk_auto_patch.rb +++ b/lib/java_buildpack/framework/snyk_auto_patch.rb @@ -49,29 +49,38 @@ def compile https.use_ssl = true req['Content-Type'] = 'application/json' req['Authorization'] = 'token ' + @application.environment["SNYK_TOKEN"] - data = File.read(pom_path) + if (pom_path) then + data = File.read(pom_path) + else + data = "" + end test_request = { - 'encoding' => 'plain', + 'encoding' => 'plain', 'files' => { 'target' => { "contents": "" - }, + }, } } - test_request['files']['target']['contents'] = data additional = [] jars = Dir.glob("#{@droplet.root}/WEB-INF/**/*.jar") jars.each do |jar| jar_pom_path = `unzip -Z1 #{jar} | grep "pom.xml"` - if (jar_pom_path.length) > 0 then + if (jar_pom_path.length) > 0 then poms = jar_pom_path.split("\n") poms.each do |pom| pom_content = `unzip -p #{jar} #{pom}` - additional.push({"contents" => pom_content}) + # if no main pom files were found, using pom from first jar file as main pom for API call. + if data.empty? then + data = pom_content + else + additional.push({"contents" => pom_content}) + end end end end + test_request['files']['target']['contents'] = data test_request['files']['additional'] = additional; req.body = test_request.to_json @@ -90,19 +99,19 @@ def compile vulns.sort! do |vuln_a, vuln_b| vulna_map = severityMap[vuln_a['severity']] vulnb_map = severityMap[vuln_b['severity']] - if (vulna_map > vulnb_map) + if (vulna_map > vulnb_map) 1 - elsif (vulna_map < vulnb_map) + elsif (vulna_map < vulnb_map) -1 else 0 end end puts "\nFounded #{vulns.length} vulnerabilities on #{res['dependencyCount']} dependencies\n" - vulns.each do |vuln| + vulns.each do |vuln| severity = vuln['severity'] if (severity == 'high') then - color = "\e[31m" + color = "\e[31m" elsif (severity == 'medium') then color = "\e[1;33m" else @@ -112,8 +121,8 @@ def compile puts " Description: #{severity} severity vulnerabiltity found in #{vuln['package']}" puts " Info: #{vuln['url']}" puts " Introduce through: #{vuln['from'][0]}\n" - end - + end + raise "Terminating droplet compilation as Snyk detected vulnerabilties..." end end