Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Snyk scan suggestion when building #8247

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions compose/cli/scan_suggest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import json
import logging
import os
from distutils.util import strtobool

from docker.constants import IS_WINDOWS_PLATFORM
from docker.utils.config import find_config_file


SCAN_BINARY_NAME = "docker-scan" + (".exe" if IS_WINDOWS_PLATFORM else "")

log = logging.getLogger(__name__)


class ScanConfig:
def __init__(self, d):
self.optin = False
vars(self).update(d)


def display_scan_suggest_msg():
if environment_scan_avoid_suggest() or \
scan_available() is None or \
scan_already_invoked():
return
log.info("Use 'docker scan' to run Snyk tests against images to find vulnerabilities "
"and learn how to fix them")


def environment_scan_avoid_suggest():
return os.getenv('DOCKER_SCAN_SUGGEST', 'true').lower() == 'false'


def scan_already_invoked():
docker_folder = docker_config_folder()
if docker_folder is None:
return False

scan_config_file = os.path.join(docker_folder, 'scan', "config.json")
if not os.path.exists(scan_config_file):
return False

try:
data = ''
with open(scan_config_file) as f:
data = f.read()
scan_config = json.loads(data, object_hook=ScanConfig)
return scan_config.optin if isinstance(scan_config.optin, bool) else strtobool(scan_config.optin)
except Exception: # pylint:disable=broad-except
return True


def scan_available():
docker_folder = docker_config_folder()
if docker_folder:
home_scan_bin = os.path.join(docker_folder, 'cli-plugins', SCAN_BINARY_NAME)
if os.path.isfile(home_scan_bin) or os.path.islink(home_scan_bin):
return home_scan_bin

if IS_WINDOWS_PLATFORM:
program_data_scan_bin = os.path.join('C:\\', 'ProgramData', 'Docker', 'cli-plugins',
SCAN_BINARY_NAME)
if os.path.isfile(program_data_scan_bin) or os.path.islink(program_data_scan_bin):
return program_data_scan_bin
else:
lib_scan_bin = os.path.join('/usr', 'local', 'lib', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
if os.path.isfile(lib_scan_bin) or os.path.islink(lib_scan_bin):
return lib_scan_bin
lib_exec_scan_bin = os.path.join('/usr', 'local', 'libexec', 'docker', 'cli-plugins',
SCAN_BINARY_NAME)
if os.path.isfile(lib_exec_scan_bin) or os.path.islink(lib_exec_scan_bin):
return lib_exec_scan_bin
lib_scan_bin = os.path.join('/usr', 'lib', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
if os.path.isfile(lib_scan_bin) or os.path.islink(lib_scan_bin):
return lib_scan_bin
lib_exec_scan_bin = os.path.join('/usr', 'libexec', 'docker', 'cli-plugins', SCAN_BINARY_NAME)
if os.path.isfile(lib_exec_scan_bin) or os.path.islink(lib_exec_scan_bin):
return lib_exec_scan_bin
return None


def docker_config_folder():
docker_config_file = find_config_file()
return None if not docker_config_file \
else os.path.dirname(os.path.abspath(docker_config_file))
11 changes: 11 additions & 0 deletions compose/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from . import parallel
from .cli.errors import UserError
from .cli.scan_suggest import display_scan_suggest_msg
from .config import ConfigurationError
from .config.config import V1
from .config.sort_services import get_container_name_from_network_mode
Expand Down Expand Up @@ -518,6 +519,9 @@ def build_service(service):
for service in services:
build_service(service)

if services:
display_scan_suggest_msg()

def create(
self,
service_names=None,
Expand Down Expand Up @@ -660,8 +664,15 @@ def up(self,
service_names,
include_deps=start_deps)

must_build = False
for svc in services:
if svc.must_build(do_build=do_build):
must_build = True
svc.ensure_image_exists(do_build=do_build, silent=silent, cli=cli)

if must_build:
display_scan_suggest_msg()

plans = self._get_convergence_plans(
services,
strategy,
Expand Down
18 changes: 18 additions & 0 deletions compose/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,24 @@ def ensure_image_exists(self, do_build=BuildAction.none, silent=False, cli=False
"rebuild this image you must use `docker-compose build` or "
"`docker-compose up --build`.".format(self.name))

def must_build(self, do_build=BuildAction.none):
if self.can_be_built() and do_build == BuildAction.force:
return True

try:
self.image()
return False
except NoSuchImageError:
pass

if not self.can_be_built():
return False

if do_build == BuildAction.skip:
return False

return True

def get_image_registry_data(self):
try:
return self.client.inspect_distribution(self.image_name)
Expand Down