ADR-1307: Pure SHA-256 memoization keys with cold cache invalidation¶
- Status: Accepted
- Supersedes: Partially supersedes ADR-1222 (only for Alerts 947–949 SHA-1 keep-open disposition)
- Date: 2026-09-24
- Deciders: Lusoris
- Tags:
python,security,concurrency,compatibility
Context¶
GitHub Code Scanning reported three warning-level alerts from Semgrep rule python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-sha1:
- Alert 947:
compat/python-vmaf/tools/decorator.pyin@persist - Alert 948:
compat/python-vmaf/tools/decorator.pyin@persist_to_file - Alert 949:
compat/python-vmaf/tools/decorator.pyin@persist_to_dir
Upstream Netflix VMAF used hashlib.sha1(..., usedforsecurity=False) to generate cache key digests from serialized function names and arguments.
ADR-1222 previously documented these alerts under its disposition table as "Correct as written", intending to keep them open with in-code # nosemgrep comments. However, Semgrep CLI records in-code suppressions in SARIF output under "suppressions": [{"kind": "inSource"}]. Because security-scans.yml uploads raw SARIF to GitHub Code Scanning, GitHub does not close alerts that appear in SARIF regardless of inSource annotations. Consequently, in-code suppressions cannot close alerts 947–949.
An architectural audit of compat/python-vmaf/tools/decorator.py established:
decorator.pyprovides runtime memoization for python-side execution only.- In-memory
@persistcaches exist only for process lifetime and cannot survive restarts. - For
@persist_to_fileand@persist_to_dir, cached entries represent ephemeral, reconstructible intermediate function evaluations. - Durable pipeline artifacts in VMAFx (such as
.vmafoutput stores,Resultserialization incompat/python-vmaf/core/result.py, andAssethashing incompat/python-vmaf/core/asset.py/executor.py) do not usedecorator.py. - Core scoring routines, C library computations, and Netflix golden assertions in
python/test/are completely independent ofdecorator.py. - Concurrency bugs existed in
decorator.py:persist_to_filewrote JSON directly to the destination withopen(file_name, "wt"), so interruption could expose a partial file and uncoordinated processes could clobber entries.
Decision¶
- Pure SHA-256 Memoization: Replace
hashlib.sha1withhashlib.sha256(..., usedforsecurity=False)across@persist,@persist_to_file, and@persist_to_dir. Cache digests are now 64-character hex strings. - Clean Cold Invalidation: Accept cold invalidation of legacy memoization caches on upgrade. Pre-existing SHA-1 cache entries on disk miss cleanly, prompting recomputation and persistence under SHA-256 keys. No legacy SHA-1 fallback, dual-hash read-through, or
# nosemgrepsuppression is retained. This eliminates alerts 947–949 from SARIF (0 findings). - Explicit Partial Supersession of ADR-1222: ADR-1222's disposition classifying alerts 947–949 as keep-open / "Correct as written" is superseded. Alert 946 is governed separately by ADR-1309.
-
Concurrency and Cross-Process Safety:
-
Atomic replacement:
_write_json_cache_atomiccreates unique temporary files usingtempfile.mkstemp(dir=file_dir, prefix=f".{base_name}.", suffix=".tmp")with error cleanup (os.closeandos.unlink), followed by atomicos.replace. - In-process recursion safety:
threading.RLock()serializes threads while allowing re-entrant acquisition for recursive dynamic programming algorithms. - Cross-process synchronization:
_file_lockimplements a re-entrant cross-process file lock overf"{file_name}.lock"usingfcntl.flockon POSIX andmsvcrt.lockingon Windows. - Cache merging:
persist_to_filereloads and merges disk state under_file_lockon cache misses prior to atomic write, preventing concurrent processes from clobbering each other's keys.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| Clean SHA-256 cold invalidation (chosen) | 0 Semgrep findings in SARIF; genuinely closes alerts 947–949 on GitHub; eliminates dead hashing paths and legacy baggage | One-time cache miss on legacy disk caches | Chosen: memoization is ephemeral and reconstructible; Netflix goldens are unaffected. |
| Read-through fallback with SHA-1 keep-open | Preserves pre-existing legacy on-disk cache hits | Retains SHA-1 code and # nosemgrep comments; leaves findings in SARIF as inSource suppressions, keeping GitHub alerts open | Defeats the primary goal of resolving code-scanning alerts at source. |
| Dual hashing (check SHA-256, fall back to SHA-1) | Seamless upgrade transition | Requires ongoing execution of SHA-1, triggering Semgrep alerts on fresh code | Unacceptable under whole-codebase security standards. |
Third-party locking libraries (filelock, portalocker) | Pre-packaged cross-platform file locking | Introduces new external dependencies into upstream-compat tooling; fails offline/distroless builds | Rejected in favor of standard-library fcntl and msvcrt implementations. |
Standard non-reentrant threading.Lock | Simpler lock structure | Deadlocks immediately when memoized functions recurse (e.g. Fibonacci dynamic programming) | Incompatible with common memoization recursion patterns. |
PID-based temporary files (target.tmp.<pid>) | Would avoid partial destination writes without a new dependency | Multiple threads within one process share a PID, so this hypothetical design would collide | Rejected during design; the base implementation wrote the destination directly and never used PID temp files. |
Consequences¶
- Positive:
- Semgrep alerts 947, 948, and 949 are eliminated at source with 0 findings in SARIF.
- Multi-threaded and multi-process cache persistence is verified race-free and clobber-free.
- Recursive memoization functions operate without deadlocks.
- Negative:
- Pre-existing on-disk cache files with SHA-1 keys will be orphaned and ignored.
- Neutral / follow-ups:
- ADR-1222 disposition table updated to record supersession of alerts 947–949.
References¶
- ADR-1222: In-code suppressions do not close code-scanning alerts; scope the scan instead
- Research-2095: Semgrep Warning Alerts 946–949 Audit and Resolution
- ADR-1309: Owner-only sidecar socket with identity-checked lifecycle
- ADR-1278: Bounded process execution and safe parallelism
- Source:
req— "well we have a lot of warnings lol as well..."