This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new a848fcbb Use exarch to extract archives with custom validation
a848fcbb is described below
commit a848fcbbd0f1cd558a65157933bf0dddb20be4cc
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Mar 4 17:22:46 2026 +0000
Use exarch to extract archives with custom validation
---
atr/tasks/quarantine.py | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/atr/tasks/quarantine.py b/atr/tasks/quarantine.py
index 083780b5..37905842 100644
--- a/atr/tasks/quarantine.py
+++ b/atr/tasks/quarantine.py
@@ -23,6 +23,7 @@ import pathlib
import aiofiles.os
import aioshutil
+import exarch
import atr.attestable as attestable
import atr.config as config
@@ -35,6 +36,7 @@ import atr.models.schema as schema
import atr.models.sql as sql
import atr.paths as paths
import atr.storage.writers.revision as revision
+import atr.tarzip as tarzip
import atr.tasks.checks as checks
import atr.util as util
@@ -95,13 +97,26 @@ async def _extract_archives_to_cache(
project_name: str,
version_name: str,
) -> None:
- # Cannot import as archives because that shadows the parameter name
- import atr.archives
-
conf = config.get()
cache_base = paths.get_cache_archives_dir() / project_name / version_name
await aiofiles.os.makedirs(cache_base, exist_ok=True)
+ extraction_config = (
+ exarch.SecurityConfig()
+ .max_file_size(conf.MAX_EXTRACT_SIZE)
+ .max_total_size(conf.MAX_EXTRACT_SIZE)
+ .max_file_count(tarzip.MAX_ARCHIVE_MEMBERS)
+ .max_compression_ratio(100.0)
+ .max_path_depth(32)
+ # Escaping the root is still disallowed by exarch even when symlinks
are allowed
+ .allow_symlinks(True)
+ .allow_hardlinks(False)
+ .allow_absolute_paths(False)
+ # Too many archives use this for us to disallow it
+ # We could set to 0o444 after extraction anyway
+ .allow_world_writable(True)
+ )
+
for archive in archives:
cache_dir = cache_base /
hashes.filesystem_cache_archives_key(archive.content_hash)
if await aiofiles.os.path.isdir(cache_dir):
@@ -111,11 +126,10 @@ async def _extract_archives_to_cache(
await aiofiles.os.makedirs(extract_dir, exist_ok=True)
try:
await asyncio.to_thread(
- atr.archives.extract,
+ exarch.extract_archive,
archive_path,
extract_dir,
- max_size=conf.MAX_EXTRACT_SIZE,
- chunk_size=conf.EXTRACT_CHUNK_SIZE,
+ extraction_config,
)
except Exception:
log.exception(f"Failed to extract archive {archive.rel_path} to
cache")
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]