Skip to content
21 changes: 16 additions & 5 deletions cycode/cli/files_collector/sca/base_restore_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ def build_dep_tree_path(path: str, generated_file_name: str) -> str:
return join_paths(get_file_dir(path), generated_file_name)


def execute_command(command: List[str], file_name: str, command_timeout: int) -> Optional[str]:
def execute_command(
command: List[str], file_name: str, command_timeout: int, dependencies_file_name: Optional[str] = None
) -> Optional[str]:
try:
dependencies = shell(command, command_timeout)
dependencies = shell(command=command, timeout=command_timeout)
# Write stdout output to the file if output_file_path is provided
if dependencies_file_name:
with open(dependencies_file_name, 'w') as output_file:
output_file.write(dependencies)
except Exception as e:
logger.debug('Failed to restore dependencies via shell command, %s', {'filename': file_name}, exc_info=e)
return None
Expand All @@ -24,10 +30,13 @@ def execute_command(command: List[str], file_name: str, command_timeout: int) ->


class BaseRestoreDependencies(ABC):
def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: int) -> None:
def __init__(
self, context: click.Context, is_git_diff: bool, command_timeout: int, create_output_file_manually: bool = False
) -> None:
self.context = context
self.is_git_diff = is_git_diff
self.command_timeout = command_timeout
self.create_output_file_manually = create_output_file_manually

def restore(self, document: Document) -> Optional[Document]:
return self.try_restore_dependencies(document)
Expand All @@ -46,9 +55,11 @@ def try_restore_dependencies(self, document: Document) -> Optional[Document]:
if self.verify_restore_file_already_exist(restore_file_path):
restore_file_content = get_file_content(restore_file_path)
else:
restore_file_content = execute_command(
self.get_command(manifest_file_path), manifest_file_path, self.command_timeout
output_file_path = restore_file_path if self.create_output_file_manually else None
execute_command(
self.get_command(manifest_file_path), manifest_file_path, self.command_timeout, output_file_path
)
restore_file_content = get_file_content(restore_file_path)

return Document(restore_file_path, restore_file_content, self.is_git_diff)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class RestoreGradleDependencies(BaseRestoreDependencies):
def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: int) -> None:
super().__init__(context, is_git_diff, command_timeout)
super().__init__(context, is_git_diff, command_timeout, create_output_file_manually=True)

def is_project(self, document: Document) -> bool:
return document.path.endswith(BUILD_GRADLE_FILE_NAME) or document.path.endswith(BUILD_GRADLE_KTS_FILE_NAME)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from os import path
from typing import List, Optional

Expand Down Expand Up @@ -30,7 +31,7 @@ def get_lock_file_name(self) -> str:
return join_paths('target', MAVEN_CYCLONE_DEP_TREE_FILE_NAME)

def verify_restore_file_already_exist(self, restore_file_path: str) -> bool:
return False
return os.path.isfile(restore_file_path)

def try_restore_dependencies(self, document: Document) -> Optional[Document]:
restore_dependencies_document = super().try_restore_dependencies(document)
Expand Down
Empty file.
39 changes: 39 additions & 0 deletions cycode/cli/files_collector/sca/npm/restore_npm_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
from typing import List

import click

from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
from cycode.cli.models import Document

NPM_PROJECT_FILE_EXTENSIONS = ['.json']
NPM_LOCK_FILE_NAME = 'package-lock.json'
NPM_MANIFEST_FILE_NAME = 'package.json'


class RestoreNpmDependencies(BaseRestoreDependencies):
def __init__(self, context: click.Context, is_git_diff: bool, command_timeout: int) -> None:
super().__init__(context, is_git_diff, command_timeout)

def is_project(self, document: Document) -> bool:
return any(document.path.endswith(ext) for ext in NPM_PROJECT_FILE_EXTENSIONS)

def get_command(self, manifest_file_path: str) -> List[str]:
return [
'npm',
'install',
'--prefix',
self.prepare_manifest_file_path_for_command(manifest_file_path),
'--package-lock-only',
'--ignore-scripts',
'--no-audit',
]

def get_lock_file_name(self) -> str:
return NPM_LOCK_FILE_NAME

def verify_restore_file_already_exist(self, restore_file_path: str) -> bool:
return os.path.isfile(restore_file_path)

def prepare_manifest_file_path_for_command(self, manifest_file_path: str) -> str:
return manifest_file_path.replace(os.sep + NPM_MANIFEST_FILE_NAME, '')
3 changes: 3 additions & 0 deletions cycode/cli/files_collector/sca/sca_code_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from cycode.cli.files_collector.sca.base_restore_dependencies import BaseRestoreDependencies
from cycode.cli.files_collector.sca.maven.restore_gradle_dependencies import RestoreGradleDependencies
from cycode.cli.files_collector.sca.maven.restore_maven_dependencies import RestoreMavenDependencies
from cycode.cli.files_collector.sca.npm.restore_npm_dependencies import RestoreNpmDependencies
from cycode.cli.files_collector.sca.nuget.restore_nuget_dependencies import RestoreNugetDependencies
from cycode.cli.models import Document
from cycode.cli.utils.git_proxy import git_proxy
Expand All @@ -18,6 +19,7 @@

BUILD_GRADLE_DEP_TREE_TIMEOUT = 180
BUILD_NUGET_DEP_TREE_TIMEOUT = 180
BUILD_NPM_DEP_TREE_TIMEOUT = 180


def perform_pre_commit_range_scan_actions(
Expand Down Expand Up @@ -132,6 +134,7 @@ def restore_handlers(context: click.Context, is_git_diff: bool) -> List[BaseRest
RestoreGradleDependencies(context, is_git_diff, BUILD_GRADLE_DEP_TREE_TIMEOUT),
RestoreMavenDependencies(context, is_git_diff, BUILD_GRADLE_DEP_TREE_TIMEOUT),
RestoreNugetDependencies(context, is_git_diff, BUILD_NUGET_DEP_TREE_TIMEOUT),
RestoreNpmDependencies(context, is_git_diff, BUILD_NPM_DEP_TREE_TIMEOUT),
]


Expand Down
10 changes: 2 additions & 8 deletions cycode/cli/utils/shell_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
_SUBPROCESS_DEFAULT_TIMEOUT_SEC = 60


def shell(
command: Union[str, List[str]], timeout: int = _SUBPROCESS_DEFAULT_TIMEOUT_SEC, execute_in_shell: bool = False
) -> Optional[str]:
def shell(command: Union[str, List[str]], timeout: int = _SUBPROCESS_DEFAULT_TIMEOUT_SEC) -> Optional[str]:
logger.debug('Executing shell command: %s', command)

try:
result = subprocess.run( # noqa: S603
command,
timeout=timeout,
shell=execute_in_shell,
check=True,
capture_output=True,
command, timeout=timeout, check=True, capture_output=True
)

return result.stdout.decode('UTF-8').strip()
Expand Down
Loading