Metadata: Tighten to prefer stat taken from the no-follow descriptor that was inspected, to avoid tiny TOCTOU. Ensure schema forbids .. in src_rel (even though caught in validate anyway - defense in depth)
All checks were successful
CI / test (push) Successful in 50s
CI / test (almalinux, docker.io/library/almalinux:9, python3.11) (push) Successful in 11m11s
CI / test (debian, docker.io/library/debian:13, python3) (push) Successful in 17m36s
Lint / test (push) Successful in 51s

This commit is contained in:
Miguel Jacq 2026-07-01 15:52:04 +10:00
parent c6e171e0f0
commit 47c2c8accc
Signed by: mig5
GPG key ID: 03906B4110AAD3B8
3 changed files with 91 additions and 4 deletions

View file

@ -170,10 +170,22 @@ def capture_file(
return False
try:
if metadata is not None:
owner, group, mode = metadata
elif inspection is not None:
if inspection is not None:
# Prefer the stat taken from the no-follow descriptor that was
# actually inspected and whose bytes are about to be written. A
# caller-supplied ``metadata`` triplet (see below) may have been
# derived from a separate, symlink-following stat with its own
# time-of-check/time-of-use window, so it must not override the
# authoritative descriptor stat when we have one.
owner, group, mode = stat_triplet_from_stat(inspection.stat_result)
elif metadata is not None:
# Fallback for callers that pre-computed metadata and are not using a
# real IgnorePolicy that returns a FileInspection (e.g. tests, or the
# /usr/local scanner when inspection is unavailable). This value is a
# convenience/perf optimisation only; it never widens what gets
# captured, since inspection (secret scan, no-follow, size/hardlink
# checks) has already run above.
owner, group, mode = metadata
else:
owner, group, mode = stat_triplet(abs_path)
except OSError:

View file

@ -411,7 +411,7 @@
},
"src_rel": {
"minLength": 1,
"pattern": "^[^/].*",
"pattern": "^(?!\\.\\.?(?:/|$))[^/\\u0000-\\u001f\\\\]+(?:/(?!\\.\\.?(?:/|$))[^/\\u0000-\\u001f\\\\]+)*$",
"type": "string"
}
},