fix: symlink guard placement, fetch_all_assets raw count, config TOCTOU (#31)
executor.py: - Move islink check into _safe_person_dir on the raw path, before realpath resolves it; the previous check at the rmtree site was unreachable dead code because realpath already followed any symlink immich_api.py / jobs.py: - fetch_all_assets now returns (assets, total_raw) where total_raw is the item count seen before non-dict filtering; callers use it for MIN_FACE_COUNT guard and display so transient non-dict API items can't incorrectly skip people - Add WARNING when pagination stops because a page had items but all were non-dict config.py: - Cache _data_cfg.exists() in _data_cfg_exists so the dual-config warning and config_file selection always read from the same stat() result; previously two calls created a TOCTOU window where log and code could disagree Bump version to 0.5.7
This commit is contained in:
+6
-4
@@ -43,8 +43,13 @@ def _safe_person_dir(output_dir: str, person_name: str) -> str:
|
||||
|
||||
os.path.join silently discards output_dir when person_name is absolute,
|
||||
and '../..' sequences resolve outside the tree. Both are rejected here.
|
||||
Symlinks on the raw (unresolved) path are also rejected — checking after
|
||||
realpath would be too late because realpath follows the link first.
|
||||
"""
|
||||
candidate = os.path.realpath(os.path.join(output_dir, person_name))
|
||||
raw = os.path.join(output_dir, person_name)
|
||||
if os.path.islink(raw):
|
||||
raise ValueError(f"Person name {person_name!r} resolves to a symlink — skipping")
|
||||
candidate = os.path.realpath(raw)
|
||||
base = os.path.realpath(output_dir)
|
||||
# Use the base path as its own prefix when it's the filesystem root ("/"),
|
||||
# otherwise append os.sep — avoids the false "//" double-slash when base == "/".
|
||||
@@ -100,9 +105,6 @@ def execute_jobs(jobs: list[dict]) -> None:
|
||||
logger.error(str(e))
|
||||
continue
|
||||
# Face crops are transient (uploaded then discarded); wipe before each run.
|
||||
if os.path.islink(person_dir):
|
||||
logger.error("person_dir %s is a symlink — refusing to remove", person_dir)
|
||||
continue
|
||||
if os.path.isdir(person_dir):
|
||||
shutil.rmtree(person_dir)
|
||||
os.makedirs(person_dir, exist_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user