T09 · Insecure Skill Coding Practices
- Location
src/template_engine.py:24- Finding
Unrestricted Template Path Traversal
- Content
View full analysis
Vulnerability Details
File Location:
src/template_engine.py:24-32
Vulnerability Type: Path traversal leading to arbitrary local file read
Risk Level: MediumVulnerable Code
python def load_template(self, template_name: str) -> str: if self.template_dir: template_path = self.template_dir / template_name else: template_path = Path(template_name) if not template_path.exists(): raise FileNotFoundError(f"Template not found: {template_path}") with open(template_path, 'r', encoding='utf-8') as f: return f.read()Technical Analysis
When
template_diris configured,load_template()directly joins the attacker-controllabletemplate_nameto that directory. It does not reject absolute paths, resolve traversal components, or verify that the resolved target remains inside the configured template root.A value such as
../../sensitive-filecan therefore escape the template directory. Depending onpathlibpath-joining behavior, an absolutetemplate_namemay also replace the intended base directory. The resulting path is opened with the process's current filesystem privileges.The same behavior is reachable through
render_file(), which passes itstemplate_nameargument directly toload_template().Attack Path
- An application creates
TemplateEnginewith a restricted template directory. - The application permits an untrusted party to influence the template name passed to
render_file()orload_template(). - The attacker submits a traversal path such as
../../path/to/readable/file. - The engine joins that value to
template_dirwithout a containment check. open()reads the escaped file if it exists and is readable by the process.- The file contents are returned to the caller and may be exposed in rendered output, logs, API responses, or other downstream processing.
Impact Assessment
Successful exploitation permits reading arbitrary UTF-8-compatible ...[truncated 402 chars]
- An application creates
- Remediation
View remediation
Remediation Suggestions
Resolve the configured template root and candidate path, then verify that the candidate is contained within the root before opening it. Explicitly reject absolute template names and consider allowlisting supported file extensions.
python def load_template(self, template_name: str) -> str: supplied_path = Path(template_name) if supplied_path.is_absolute(): raise ValueError("Absolute template paths are not allowed") if self.template_dir is None: raise ValueError("A template directory must be configured") root = self.template_dir.resolve() template_path = (root / supplied_path).resolve() try: template_path.relative_to(root) except ValueError: raise ValueError("Template path escapes the configured directory") if template_path.suffix not in {".md", ".txt", ".html"}: raise ValueError("Unsupported template file type") if not template_path.is_file(): raise FileNotFoundError(f"Template not found: {template_path}") with template_path.open("r", encoding="utf-8") as f: return f.read()Additionally:
- Do not pass user-controlled template names directly to the engine.
- Map external template identifiers to server-controlled filenames.
- Run the application with least-privilege filesystem permissions.
- Add tests covering
../traversal, nested traversal, absolute paths, symlink escapes, nonexistent files, and valid in-root templates. - If symlinks are permitted inside the template directory, ensure resolution occurs before the containment check, as shown above.
