T07 · Tool Hijacking and Spoofing
Error
- Location
- scripts/run.py:220
- Finding
- Unverified External Python Module Execution During Preprocessing Fallback<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.py:220-227` **Vulnerability Type**: Untrusted local module loading and execution **Risk Level**: High ### Complete Code Snippet ```python _shared_dir = Path(__file__).resolve().parent.parents[3] / "_shared" / "doc-preprocess" / "scripts" if not _shared_dir.exists(): print(f"ERROR: Unable to read the input file because local preprocessing failed and the shared preprocessor is unavailable. Cause: {exc}", file=sys.stderr) return 1 import importlib.util as _iu _spec = _iu.spec_from_file_location("_shared_preprocess", _shared_dir / "preprocess.py") _sp = _iu.module_from_spec(_spec) _spec.loader.exec_module(_sp) ``` The displayed error message has been translated into English; the executable module-loading statements are reproduced from the source. ### Technical Analysis When local preprocessing raises `PreprocessError`, the application calculates a path outside the skill package and dynamically executes the `preprocess.py` file found there. The code verifies only that the directory exists. It does not verify: - The ownership or permissions of the directory and file. - Whether the resolved file is a symbolic link. - Whether the file belongs to an approved package. - A cryptographic hash or signature. - Whether the module path remains within a trusted root after resolution. Calling `exec_module()` executes all top-level statements in the selected Python file. Consequently, control over that external file is equivalent to arbitrary Python code execution under the identity and privileges of the skill process. This behavior crosses the package trust boundary: reviewing the files included in this skill does not establish what code will run in the fallback path. ### Attack Path 1. An attacker obtains the ability to create, replace, or influence the external `_shared/doc-preprocess/scripts/preprocess.py` file, or redirects it through filesystem manipulation. 2. The attacker places arbitrar ...[truncated 1251 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove the dynamic fallback and use only the preprocessor shipped with the reviewed skill. 2. If shared functionality is required, package it as a version-pinned, trusted Python dependency and import it through the normal package mechanism. 3. Resolve the candidate path with `Path.resolve(strict=True)` and verify that it remains under an explicitly configured trusted root. 4. Reject symbolic links and require restrictive ownership and write permissions. 5. Verify the module against a pinned cryptographic digest or signed manifest before loading it. 6. Do not use `exec_module()` on files selected from mutable shared directories. 7. Run the skill in a sandbox with minimal filesystem access, a restricted environment, limited outbound networking, and no unnecessary operating-system privileges. 8. Add a test that intentionally triggers `PreprocessError` and confirms that no code outside the installed package is executed. ]]>
