From fa388c8556fe22e8dd9444ca5a9be0ddc955e229 Mon Sep 17 00:00:00 2001 From: Safi Date: Fri, 10 Apr 2026 16:07:51 +0100 Subject: [PATCH] Security: fix YAML injection in ingest.py, injection in export.py, bound collision loop Co-Authored-By: Claude Sonnet 4.6 --- graphify/export.py | 12 ++++++++---- graphify/ingest.py | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/graphify/export.py b/graphify/export.py index e58df176..d35edafc 100644 --- a/graphify/export.py +++ b/graphify/export.py @@ -396,10 +396,14 @@ def to_html( n = len(communities.get(cid, [])) legend_data.append({"cid": cid, "color": color, "label": lbl, "count": n}) - nodes_json = json.dumps(vis_nodes) - edges_json = json.dumps(vis_edges) - legend_json = json.dumps(legend_data) - hyperedges_json = json.dumps(getattr(G, "graph", {}).get("hyperedges", [])) + # Escape sequences so embedded JSON cannot break out of the script tag + def _js_safe(obj) -> str: + return json.dumps(obj).replace(" tuple now = datetime.now(timezone.utc).isoformat() content = f"""--- -source_url: {url} +source_url: "{_yaml_str(url)}" type: tweet -author: {tweet_author} +author: "{_yaml_str(tweet_author)}" captured_at: {now} -contributor: {contributor or author or 'unknown'} +contributor: "{_yaml_str(contributor or author or 'unknown')}" --- # Tweet by @{tweet_author} @@ -109,11 +109,11 @@ def _fetch_webpage(url: str, author: str | None, contributor: str | None) -> tup markdown = _html_to_markdown(html, url) now = datetime.now(timezone.utc).isoformat() content = f"""--- -source_url: {url} +source_url: "{_yaml_str(url)}" type: webpage title: "{_yaml_str(title)}" captured_at: {now} -contributor: {contributor or author or 'unknown'} +contributor: "{_yaml_str(contributor or author or 'unknown')}" --- # {title} @@ -149,13 +149,13 @@ def _fetch_arxiv(url: str, author: str | None, contributor: str | None) -> tuple now = datetime.now(timezone.utc).isoformat() content = f"""--- -source_url: {url} -arxiv_id: {arxiv_id.group(1) if arxiv_id else ''} +source_url: "{_yaml_str(url)}" +arxiv_id: "{_yaml_str(arxiv_id.group(1) if arxiv_id else '')}" type: paper -title: "{title}" -paper_authors: "{paper_authors}" +title: "{_yaml_str(title)}" +paper_authors: "{_yaml_str(paper_authors)}" captured_at: {now} -contributor: {contributor or author or 'unknown'} +contributor: "{_yaml_str(contributor or author or 'unknown')}" --- # {title} @@ -225,7 +225,7 @@ def ingest(url: str, target_dir: Path, author: str | None = None, contributor: s out_path = target_dir / filename # Avoid overwriting - append counter if needed counter = 1 - while out_path.exists(): + while out_path.exists() and counter < 1000: stem = Path(filename).stem out_path = target_dir / f"{stem}_{counter}.md" counter += 1