diff --git a/atomic_reactor/plugins/cachi2_init.py b/atomic_reactor/plugins/cachi2_init.py index bf6d6b4c3..e713e6943 100644 --- a/atomic_reactor/plugins/cachi2_init.py +++ b/atomic_reactor/plugins/cachi2_init.py @@ -135,7 +135,15 @@ def process_remote_sources(self) -> List[Dict[str, Any]]: remote_source_data["ref"] ) - enforce_sandbox(source_path_app, remove_unsafe_symlinks=False) + remove_unsafe_symlinks = False + flags = remote_source_data.get("flags", []) + if "remove-unsafe-symlinks" in flags: + remove_unsafe_symlinks = True + + enforce_sandbox( + source_path_app, + remove_unsafe_symlinks, + ) validate_paths(source_path_app, remote_source_data.get("packages", {})) if clone_only(remote_source_data): diff --git a/atomic_reactor/utils/cachi2.py b/atomic_reactor/utils/cachi2.py index 5fdc732f2..7ce9a820b 100644 --- a/atomic_reactor/utils/cachi2.py +++ b/atomic_reactor/utils/cachi2.py @@ -23,11 +23,12 @@ class SymlinkSandboxError(Exception): """Found symlink(s) pointing outside the sandbox.""" -def enforce_sandbox(repo_root: Path, remove_unsafe_symlinks: bool) -> None: +def enforce_sandbox(repo_root: Path, remove_unsafe_symlinks: bool = False) -> None: """ Check that there are no symlinks that try to leave the cloned repository. :param (str | Path) repo_root: absolute path to root of cloned repository + :param bool remove_unsafe_symlinks: remove unsafe symlinks if any are found :raises OsbsValidationException: if any symlink points outside of cloned repository """ for path_to_dir, subdirs, files in os.walk(repo_root): diff --git a/tests/plugins/test_cachi2_init.py b/tests/plugins/test_cachi2_init.py index 0a70b1978..f885925bd 100644 --- a/tests/plugins/test_cachi2_init.py +++ b/tests/plugins/test_cachi2_init.py @@ -6,6 +6,7 @@ of the BSD license. See the LICENSE file for details. """ +from pathlib import Path from textwrap import dedent import os.path import json @@ -35,6 +36,7 @@ from tests.mock_env import MockEnv from tests.stubs import StubSource +from tests.utils.test_cachi2 import Symlink, write_file_tree REMOTE_SOURCE_REPO = 'https://git.example.com/team/repo.git' @@ -370,6 +372,35 @@ def test_dependency_replacements(workflow): expect_error="Dependency replacements are not supported by Cachi2") +def test_enforce_sandbox(workflow: DockerBuildWorkflow) -> None: + """Should remove symlink pointing outside of repository""" + container_yaml_config = dedent(f"""\ + remote_source: + flags: + - remove-unsafe-symlinks + repo: {REMOTE_SOURCE_REPO} + ref: {REMOTE_SOURCE_REF} + pkg_managers: [] + """) + + mock_repo_config(workflow, data=container_yaml_config) + + def clone_f(repo, target_dir, ref): + bad_symlink = Path(target_dir / 'symlink_to_root') + with open(target_dir / "clone.txt", "w") as f: + f.write(f"{repo}:{ref}") + f.flush() + write_file_tree({bad_symlink: Symlink("/")}, target_dir) + assert Path(target_dir / bad_symlink).exists() + + mocked = flexmock(Cachi2InitPlugin) + mocked.should_receive('clone_remote_source').replace_with(clone_f) + Cachi2InitPlugin(workflow).run() + + assert not Path(workflow.build_dir.path / CACHI2_BUILD_DIR / "remote-source" + / CACHI2_BUILD_APP_DIR / "symlink_to_root").exists() + + def run_plugin_with_args(workflow, dependency_replacements=None, expect_error=None, expect_result=True, expected_plugin_results=None): runner = (MockEnv(workflow) diff --git a/tests/utils/test_cachi2.py b/tests/utils/test_cachi2.py index e11885f36..c00c09269 100644 --- a/tests/utils/test_cachi2.py +++ b/tests/utils/test_cachi2.py @@ -180,7 +180,7 @@ def test_remote_source_invalid_paths(tmpdir, remote_source_packages, expected_er def test_remote_source_path_to_symlink_out_of_repo(tmpdir): - """If cloned repo contains symlink that points out fo repo, + """If cloned repo contains symlink that points out of repo, path in packages shouldn't be allowed""" tmpdir_path = Path(tmpdir)