Skip to content

Commit

Permalink
Merge pull request #1523 from frappe/add-tar-safety
Browse files Browse the repository at this point in the history
fix: add safety filter for untarring
  • Loading branch information
18alantom authored Jan 24, 2024
2 parents 36c3cf4 + 3502c77 commit 38822f7
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
8 changes: 7 additions & 1 deletion bench/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from bench.utils import (
UNSET_ARG,
fetch_details_from_tag,
get_app_cache_extract_filter,
get_available_folder_name,
get_bench_cache_path,
is_bench_directory,
Expand Down Expand Up @@ -343,7 +344,12 @@ def get_cached(self) -> bool:

click.secho(f"Getting {self.app_name} from cache", fg="yellow")
with tarfile.open(cache_path, mode) as tar:
tar.extractall(app_path.parent)
try:
tar.extractall(app_path.parent, filter=get_app_cache_extract_filter())
except Exception:
logger.exception(f"Cache extraction failed for {self.app_name}")
shutil.rmtree(app_path)
return False

return True

Expand Down
33 changes: 33 additions & 0 deletions bench/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from glob import glob
from pathlib import Path
from shlex import split
from tarfile import TarInfo
from typing import List, Optional, Tuple

# imports - third party imports
Expand Down Expand Up @@ -569,3 +570,35 @@ def get_cmd_from_sysargv():
break

return cmd_from_ctx


def get_app_cache_extract_filter(
count_threshold: int = 10_000,
size_threshold: int = 1_000_000_000,
): # -> Callable[[TarInfo, str], TarInfo | None]
state = dict(count=0, size=0)

AbsoluteLinkError = Exception
def data_filter(m: TarInfo, _:str) -> TarInfo:
return m

if (sys.version_info.major == 3 and sys.version_info.minor > 7) or sys.version_info.major > 3:
from tarfile import data_filter, AbsoluteLinkError

def filter_function(member: TarInfo, dest_path: str) -> Optional[TarInfo]:
state["count"] += 1
state["size"] += member.size

if state["count"] > count_threshold:
raise RuntimeError(f"Number of entries exceeds threshold ({state['count']})")

if state["size"] > size_threshold:
raise RuntimeError(f"Extracted size exceeds threshold ({state['size']})")

try:
return data_filter(member, dest_path)
except AbsoluteLinkError:
# Links created by `frappe` after extraction
return None

return filter_function

0 comments on commit 38822f7

Please sign in to comment.