Skip to content

recipe/_api.py: add usedforsecurity=False to md5 + replace time.sleep mtime hack in test #1963

@Trecek

Description

@Trecek

Cache Invalidation Hardening — recipe/_api.py FIPS Fix + Test Fix

Source: review-decisions audit (2026-05-05) — adversarial review confirmed | Scope: small


Validated Findings

[F24] recipe/_api.py: add usedforsecurity=False to hashlib.md5 call

File: src/autoskillit/recipe/_api.py:118 | Severity: warning/defense

hashlib.md5(str(entries).encode()).hexdigest() has no usedforsecurity keyword argument. On FIPS-140 hardened systems (RHEL 8+), OpenSSL rejects MD5 calls lacking usedforsecurity=False with ValueError: [digital envelope routines] unsupported. The hash is purely for cache invalidation — non-cryptographic use.

Fix: Add usedforsecurity=False parameter. Project targets Python >=3.11 (pyproject.toml:25), so the parameter is supported.

# Before:
hashlib.md5(str(entries).encode()).hexdigest()

# After:
hashlib.md5(str(entries).encode(), usedforsecurity=False).hexdigest()

[F23] tests/recipe: replace time.sleep mtime hack with os.utime()

File: tests/recipe/test_api.py:743 | Severity: warning/tests

time.sleep(0.01) is used to force an mtime change between two writes of identical content. On 1-second-resolution filesystems (HFS+, some ext4 mount configs), 10 ms will not advance mtime, causing intermittent h1 == h2 assertion failures.

Fix: Use os.utime() with an explicit offset instead of sleeping:

# Before:
time.sleep(0.01)
f.write_text("name: test\\n")  # same content, new mtime

# After:
f.write_text("name: test\\n")  # same content
stat = f.stat()
os.utime(f, ns=(stat.st_atime_ns, stat.st_mtime_ns + 1_000_000_000))  # +1 second

Adversarial Review Notes

  • F24 confirmed: AGREE — genuine FIPS compliance issue, trivial fix
  • F23 confirmed: AGREE — architecturally insufficient sleep, though works on project's WSL2/tmpfs CI
  • F03 (mtime_ns without st_size) was downgraded to info/cohesion and excluded from this ticket — _file_size() IS already used for the recipe-file cache path (lines 349-358); the omission from _compute_registry_hash is a consistency gap on a low-churn path, not a hot-path correctness bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    auditAudit-generated issuerecipe:implementationRoute: proceed directly to implementationstagedImplementation staged and waiting for promotion to main

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions