Skip to content

SEC-W71-001: CWE-22 path traversal in bin/compute-input-hash (LOW) #392

Description

@Zious11

Finding Summary

Finding ID: SEC-W71-001
Severity: LOW
CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Two path-safety hardening gaps in bin/compute-input-hash were identified during the wave-71 security review (STORY-157). Both are still open on develop (b642c0f). They are filed together because they share a fix.


Affected File and Lines

SEC-001 — Comment-stripping path-traversal enablement

File: bin/compute-input-hash
Lines: 137–140

# AC-157-010: strip inline comment suffix (` # ...`)
comment_idx = path.find(" #")
if comment_idx != -1:
    path = path[:comment_idx].strip()

After stripping the # ... suffix, no sanitization is applied to path before it is used in repo_root / path. An inputs: entry such as:

- ../../etc/shadow  # RETIRED 2026-06-19

has the comment stripped, leaving ../../etc/shadow, which resolves outside the repository. Before the comment-stripping PR, the literal comment text made the path unresolvable; after PR merge it silently traverses out of .factory/.

SEC-002 — Absolute-path swallowing via PurePath.__truediv__

File: bin/compute-input-hash (repo_root resolution step)

Python's PurePath.__truediv__ discards the left-hand side when the right-hand side is an absolute path (e.g., /etc/shadow). An inputs: entry containing an absolute path bypasses repo_root entirely and resolves to any path on the filesystem.


Exploitability

  • Requires write access to the factory-artifacts branch (privileged, small trust boundary).
  • The output is a 7-char hex MD5 truncation — not file contents. No direct information disclosure.
  • The tool is an internal developer CLI with no untrusted callers and no CI privilege-escalation path.
  • A successful traversal causes a hash of a file outside .factory/ to be computed and stored, producing spurious drift signals rather than data exfiltration.
  • Severity correctly classified as LOW.

Validation Provenance

  • Primary evidence: .factory/code-delivery/STORY-157/security-review.md §SEC-001 + §SEC-002
  • Confirmed still open on develop (b642c0f) during maint-2026-07-08 triage by research-agent per policy DF-VALIDATION-001
  • Status at triage close: VALIDATED-PENDING-FILING (2026-07-08)
  • Human-approved filing: 2026-07-09 at maint-2026-07-09 gate
  • DF-VALIDATION-001 prerequisites satisfied — finding is CONFIRMED and research-validated

Suggested Remediation

After the comment-strip step, validate path before resolution:

Option A — Reject traversal and absolute paths explicitly:

if ".." in Path(path).parts or Path(path).is_absolute():
    raise ValueError(f"Input path '{path}' contains traversal or is absolute")

Option B — Use Path.resolve(strict=True) + is_relative_to(repo_root) (preferred):

resolved = (repo_root / path).resolve(strict=True)
if not resolved.is_relative_to(repo_root.resolve()):
    raise ValueError(f"Input path '{path}' resolves outside the repository root")

Option B covers both SEC-001 and SEC-002 in one guard and is the canonical safe pattern for restricting path resolution to a base directory.


References

  • .factory/code-delivery/STORY-157/security-review.md §SEC-001, §SEC-002
  • .factory/maintenance/backlog-triage-maint-2026-07-08.md §1 (SEC-W71-001)
  • .factory/maintenance/sweep-report-2026-07-08.md DF-VALIDATION-001 Triage table
  • MITRE CWE-22: https://cwe.mitre.org/data/definitions/22.html

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions