-
Notifications
You must be signed in to change notification settings - Fork 7
Generate composer.lock files #471
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
base: main
Are you sure you want to change the base?
Changes from 20 commits
0314d9f
df03719
627c5e8
e96fe48
deeaae7
8dcd3fe
18ac459
47ec973
8fafd6c
0416d29
7dc3078
8d62552
bf65480
ac6c8f8
bce7432
f165e10
dfe927a
acb39fb
880247c
32ab4e2
b849677
a60c99d
22eb123
4ebbe82
83b314c
14eac8c
8cab6cf
2107bcf
d8e3de7
83de2b2
9d9750a
d21e59c
1e73976
0d26b9b
3b53779
c077fba
76be831
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import subprocess | ||
| import os | ||
| from glob import glob | ||
| from engine.plugins.lib import utils | ||
| import docker | ||
| import docker.errors | ||
|
|
||
| logger = utils.setup_logging("trivy_sca") | ||
|
|
||
| cmd = [ | ||
| "composer", | ||
| "install", | ||
| "--no-scripts", # Skip execution of scripts | ||
| "--no-audit", # Don't run an audit | ||
| "&&", | ||
| "ls", | ||
| "-l", | ||
| "composer.lock" | ||
| ] | ||
|
|
||
| docker_client = docker.from_env() | ||
|
|
||
|
|
||
| def install_package_files(include_dev: bool, path: str, root_path: str): | ||
| # Create a composer.lock file if it doesn't already exist | ||
| logger.info( | ||
| f"Generating composer.lock for {path.replace(root_path, '')} (including dev dependencies: {include_dev}" | ||
|
||
| ) | ||
| if not include_dev: | ||
| cmd.append("--no-dev") | ||
|
|
||
| # Run Composer in a container | ||
| COMPOSER_IMG = "composer:latest" | ||
| container_name = "composer_runner" | ||
|
||
| host_working_dir = path | ||
| container_mount_path = "/app" | ||
|
|
||
| container = docker_client.containers.run( | ||
| COMPOSER_IMG, | ||
| name=container_name, | ||
| command=cmd, | ||
| volumes={ | ||
| host_working_dir: {"bind": container_mount_path, "mode": "rw"}, | ||
| }, | ||
| working_dir=container_mount_path, | ||
| auto_remove=False, | ||
| stdout=True, | ||
| stderr=True, | ||
| detach=True, | ||
| ) | ||
|
|
||
|
|
||
| exit_code = container.wait() | ||
| logs = container.logs().decode("utf-8") | ||
|
|
||
| logger.error(f'exit code: {exit_code}') | ||
| logger.error(f'logs: {logs}') | ||
|
|
||
|
|
||
| # container.remove() | ||
|
|
||
|
|
||
| return | ||
|
|
||
|
|
||
| def check_composer_package_files(path: str, include_dev: bool) -> tuple: | ||
| """ | ||
| Main Function | ||
| Find all of the composer.json files in the repo and build lock files for them if they dont have one already. | ||
| Parses the results and returns them with the errors. | ||
| """ | ||
|
|
||
| errors = [] | ||
| alerts = [] | ||
|
|
||
| # Find and loop through all the composer.json files in the path | ||
| files = glob(f"{path}/**/composer.json", recursive=True) | ||
|
|
||
| logger.info("Found %d composer.json files", len(files)) | ||
|
|
||
| # If there are no composer.json files, exit function | ||
| if len(files) == 0: | ||
| return errors, alerts | ||
|
|
||
| # Build a set of all directories containing package files | ||
| paths = set() | ||
| for filename in files: | ||
| paths.add(os.path.dirname(filename)) | ||
|
|
||
| # Loop through paths that have a package file | ||
| for sub_path in paths: | ||
| lockfile = os.path.join(sub_path, "composer.lock") | ||
| lockfile_missing = not os.path.exists(lockfile) | ||
| # Generate a lock file if does not exist in path that has a composer.json | ||
| if lockfile_missing: | ||
| msg = ( | ||
| f"No composer.lock file was found in path {sub_path.replace(path, '')}." | ||
| " Please consider creating a composer.lock file for this project." | ||
| ) | ||
| logger.warning(msg) | ||
| alerts.append(msg) | ||
| r = install_package_files(include_dev, sub_path, path) | ||
| # if r.returncode != 0: | ||
| # error = r.stderr.decode("utf-8") | ||
| # logger.error(error) | ||
| # errors.append(error) | ||
| # return errors, alerts | ||
| # Return the results | ||
| return errors, alerts | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| import json | ||
| import subprocess | ||
| from engine.plugins.lib.trivy_common.generate_locks import check_package_files | ||
| from engine.plugins.lib.trivy_common.generate_composer_locks import check_composer_package_files | ||
| from engine.plugins.lib.utils import convert_string_to_json | ||
| from engine.plugins.lib.trivy_common.parsing_util import parse_output | ||
| from engine.plugins.lib.utils import setup_logging | ||
|
|
@@ -38,9 +39,18 @@ def main(): | |
| args = parse_args() | ||
| include_dev = args.engine_vars.get("include_dev", False) | ||
| results = [] | ||
| alerts = [] | ||
| errors = [] | ||
|
|
||
| # Generate Lock files (without installing npm packages) | ||
| lock_file_errors, lock_file_alerts = check_package_files(args.path, include_dev, False) | ||
| # Generate Lock files (and install npm packages for license info) | ||
| lock_file_errors, lock_file_alerts = check_package_files(args.path, include_dev, True) | ||
| alerts.extend(lock_file_alerts) | ||
| errors.extend(lock_file_errors) | ||
|
|
||
| # Run Composer Install for exact version numbers | ||
| compose_lock_errors, compose_lock_alerts = check_composer_package_files(args.path, include_dev) | ||
| alerts.extend(compose_lock_alerts) | ||
| errors.extend(compose_lock_errors) | ||
|
|
||
| # Scan local lock files | ||
| output = execute_trivy_lock_scan(args.path, include_dev) | ||
|
|
@@ -54,11 +64,7 @@ def main(): | |
| results.extend(result) | ||
|
|
||
| # Return results | ||
| print( | ||
| json.dumps( | ||
| {"success": not bool(results), "details": results, "errors": lock_file_errors, "alerts": lock_file_alerts} | ||
| ) | ||
| ) | ||
| print(json.dumps({"success": not bool(results), "details": results, "errors": errors, "alerts": alerts})) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (minor) Since our JSON results can be quite large, we should switch from using json.dump({ ... }, sys.stdout) |
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this function doesn't report errors up, we should document the fact that this is "best effort", i.e. if the
composer.lockcouldn't be generated then we will continue anyway.