T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/validate_html_first_skill.py:439- Finding
Validators Read Potentially Sensitive Life Reports Outside the Skill Directory
- Content
View full analysis
Vulnerability Details
File Locations:
scripts/validate_html_first_skill.py:439-480scripts/validate_magic_score.py:10-31scripts/validate_magic_score.py:373-381- Automatically invoked through
scripts/validate_skill_package.py:324-334
Vulnerability Type: Excessive filesystem read scope and undeclared cross-project data access
Risk Level: MediumVulnerable Code
scripts/validate_html_first_skill.py:439-480:python def validate_rendered_html(errors: list[str]) -> None: html_paths = sorted((ROOT.parents[1] / "final-review").glob("life-fork-*-user-report-*/report.html")) if not html_paths: return for path in html_paths: validate_html_document(path, path.read_text(encoding="utf-8"), errors) def load_renderer(): renderer_path = ROOT / "scripts" / "render_html_report.py" spec = importlib.util.spec_from_file_location("life_fork_render_html_report", renderer_path) if spec is None or spec.loader is None: raise RuntimeError(f"Unable to load renderer: {renderer_path}") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def validate_fresh_rendered_html(errors: list[str]) -> None: package_fixtures = [ ROOT / "examples" / "html-render-fixture.md", ] report_paths = [ path for path in package_fixtures if path.is_file() ] + sorted((ROOT.parents[1] / "final-review").glob("life-fork-*-user-report-*/report.md")) if not report_paths: fail("missing markdown render fixture for fresh HTML validation", errors) return renderer = load_renderer() for path in report_paths: markdown_text = path.read_text(encoding="utf-8") title = renderer.infer_title(markdown_text, path.stem) rendered = renderer.build_html(markdown_text, title) validate_html_document(f"{path} ...[truncated 4646 chars]- Remediation
View remediation
Remediation Suggestions
- Remove automatic discovery of workspace-level reports from the default validators.
- Restrict normal package validation to fixtures stored under the resolved Skill root.
- Add an explicit optional argument, such as
--final-review-dir, when external reports genuinely need validation. - Require affirmative user selection of that option; do not derive the external directory through
ROOT.parents. - Resolve the supplied directory and validate that it is the exact user-authorized location before reading files.
- Separate package tests from private-report tests so
validate_skill_package.pyinvokes only package-contained regression tests by default. - Avoid including report content in error messages; report only the file path, failed rule, and non-sensitive metadata.
- Document the precise files and data categories read whenever external-report validation is enabled.
- Add a regression test confirming that default validation performs no reads outside the Skill root.
- Apply the same change consistently to both
validate_html_first_skill.pyandvalidate_magic_score.py.
A safer interface would resemble:
python def external_report_paths(final_review_dir: Path | None) -> list[Path]: if final_review_dir is None: return [] authorized = final_review_dir.expanduser().resolve() if not authorized.is_dir(): raise ValueError("The explicitly supplied final-review directory is invalid") return sorted( authorized.glob("life-fork-*-user-report-*/report.md") )The package validator should omit this option by default and use only
examples/html-render-fixture.md.
