T09 · Insecure Skill Coding Practices
- Location
scripts/random_coffee_matcher.py:10- Finding
Untrusted Ancestor-Path Python Module Loading
- Content
View full analysis
Vulnerability Details
File Location:
scripts/random_coffee_matcher.py, lines 10–15 and 23–27
Vulnerability Type: Python module search-path hijacking
Risk Level: MediumVulnerable Code
python def add_repo_src() -> None: for parent in Path(__file__).resolve().parents: src = parent / "src" package = src / "random_coffee_matcher" if package.exists(): sys.path.insert(0, str(src)) return raise SystemExit( "Could not find src/random_coffee_matcher. Run this script from the " "random-coffee-best-fit-outreach repo clone or install the package first." ) def main() -> int: add_repo_src() from random_coffee_matcher.cli import main as cli_main return cli_main()Technical Analysis
The wrapper traverses every ancestor of its own resolved path and accepts the first entry named
src/random_coffee_matcher. It then places that ancestor-controlledsrcdirectory at the beginning ofsys.pathand importsrandom_coffee_matcher.cli.The code does not establish a trusted repository root, verify that the selected package belongs to the intended project, or ensure that the selected package path is a directory. Because the selected path takes precedence over other Python module locations, a package placed in a writable ancestor can control the imported module.
Python executes package initialization and module-level statements during import. Consequently, merely invoking the documented wrapper can execute attacker-supplied Python before the expected CLI function is called.
The audited artifact does not contain
src/random_coffee_matcher. In an environment without a matching ancestor package, the wrapper fails safely withSystemExit. Exploitation therefore requires an attacker to be able to create or influence the expected path in an ancestor directory of the installed skill.Attack Path
- An attacker obtains write access to an ancestor directory ...[truncated 1596 chars]
- Remediation
View remediation
Remediation Suggestions
- Bundle the required
random_coffee_matcherpackage within the skill, or declare and install it through a pinned, integrity-verified dependency mechanism. - Resolve modules from one explicit directory beneath a known project root rather than searching every ancestor.
- Validate the candidate with
is_dir()and usePath.resolve()to ensure it remains inside the trusted root. - Fail closed when the exact trusted package path is absent.
- Avoid prepending dynamically discovered directories to
sys.path. Prefer a normal installed package or an explicit package-relative import. - If repository-layout support is required, identify the repository root using a trusted manifest with validated contents rather than a generic directory name.
- Add tests that create decoy
src/random_coffee_matcherpackages in ancestor directories and confirm that the wrapper refuses to import them.
A hardened approach should use a single expected path, for example:
python def add_repo_src() -> None: project_root = Path(__file__).resolve().parents[1] src = (project_root / "src").resolve() package = (src / "random_coffee_matcher").resolve() if src.parent != project_root or package.parent != src or not package.is_dir(): raise SystemExit("Trusted bundled random_coffee_matcher package not found.") sys.path.insert(0, str(src))This example assumes that
parents[1]is the intentionally defined project root; deployment should enforce that layout and add stronger integrity controls where the surrounding directory is not trusted.- Bundle the required
