affaan-m/ECC · error · ContractError
secure receipt validation requires O_NOFOLLOW
Error message
secure receipt validation requires O_NOFOLLOW
What it means
_sha256 refuses to hash receipt files unless the platform's os module supports O_NOFOLLOW, the open() flag that prevents following symlinks. This is a TOCTOU/security guard: hashing a symlinked path could be redirected to attacker-controlled content between check and hash. On platforms without O_NOFOLLOW (older/non-POSIX systems, notably Windows historically), secure validation is unavailable and the library fails closed.
Solutions
- Run receipt validation on Linux/macOS where os.O_NOFOLLOW exists
- Upgrade the OS or Python build so os.O_NOFOLLOW is available
- If the platform is fixed and security is not required, use a non-secure hash path instead of validate_artifact_receipt's secure mode
- Add a capability check in your tooling before invoking secure validation: hasattr(os, 'O_NOFOLLOW')
Example fix
// before (script ran on Windows and raised)
validate_artifact_receipt(bundle_root) # requires O_NOFOLLOW
// after
import os
if hasattr(os, 'O_NOFOLLOW'):
validate_artifact_receipt(bundle_root)
else:
print('secure validation unsupported on this platform; use a POSIX host') Defensive patterns
Strategy: fallback
Validate before calling
import os secure_ok = hasattr(os, 'O_NOFOLLOW')
Try / catch
try:
validate_artifact_receipt(root)
except ContractError as e:
if 'O_NOFOLLOW' in str(e):
print('Secure validation unsupported on this platform; run on Linux/macOS')
else:
raise Prevention
- Run receipt validation on POSIX platforms
- Feature-check O_NOFOLLOW in CI before secure validation jobs
- Document platform requirements in your pipeline
When it happens
Trigger: Calling validate_artifact_receipt on a system whose os.open lacks O_NOFOLLOW — typically Windows or very old Python/OS combos — while secure receipt validation is requested.
Common situations: Running the script on Windows or in an environment (older WSL setups, unusual embedded Python builds) where O_NOFOLLOW is not exposed; running with a Python version/platform combination that predates the flag.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
Related errors
- gate.variant_invalid
- Invalid ECC repo root: missing package.json at
- Nasiko executable must be a regular file, not a symlink.
- Nasiko install directory must be a real directory, not a…
- output artifact must be a regular file
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/66fb69856ddfdad5.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:72
_validate_numeric_evidence(probe, label=label)
if probe.get("duration") != source_duration:
raise ContractError(f"{label} probe duration is not bound to source duration")
for field in ("sample_times", "scene_changes"):
values = probe.get(field, [])
if not isinstance(values, list):
raise ContractError(f"{label} has invalid {field}")
for value in values:
_validate_media_time(value, source_duration, label=f"{label} {field}")
samples = probe.get("style_samples", [])
if not isinstance(samples, list) or any(not isinstance(sample, dict) for sample in samples):
raise ContractError(f"{label} has invalid style evidence")
for sample in samples:
_validate_media_time(sample.get("time"), source_duration, label=f"{label} style evidence")
def _sha256(path: Path) -> str:
if not hasattr(os, "O_NOFOLLOW"):
raise ContractError("secure receipt validation requires O_NOFOLLOW")
descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
digest = hashlib.sha256()
try:
metadata = os.fstat(descriptor)
if not stat.S_ISREG(metadata.st_mode):
raise ContractError(f"receipt source is not a regular file: {path}")
while True:
chunk = os.read(descriptor, 1024 * 1024)
if not chunk:
break
digest.update(chunk)
finally:
os.close(descriptor)
return digest.hexdigest()
def _semantic_signature(spec: dict[str, Any]) -> str:
signature = spec.get("signature", {})View on GitHub (pinned to 8321021c54)