From 51e06223da3011754e0032728985df7a16456ae0 Mon Sep 17 00:00:00 2001 From: dewhush Date: Sat, 13 Jun 2026 00:08:52 +0700 Subject: [PATCH] fix: sql injection fix in triplets Addressed unsafe code patterns found during security review: - sqli in triplets/ accessor.py: The table name parameter is directly interpolated into SQL queries using f-strings without sanitization or parameteriz - xss in triplets/cgmes tools/static/relations graph.html: The objectTable property of a selected node is inserted directly into the DOM using innerHTML. If the underlying RDF - path traversal in triplets/rdfs tools/cim rdfs to html.py: The directory path for exporting HTML files is constructed using metadata values (entsoeUML, shortName) extracted di - sqli in triplets/ init .py: Similar to the accessor module, the table name parameter is directly interpolated into multiple SQL queries using f-st Tested locally, no regressions observed. --- triplets/__init__.py | 6 +++++- triplets/_accessor.py | 5 +++++ triplets/cgmes_tools/static/relations_graph.html | 5 ++++- triplets/rdfs_tools/cim_rdfs_to_html.py | 7 ++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/triplets/__init__.py b/triplets/__init__.py index ffd55a8..d0cea8b 100644 --- a/triplets/__init__.py +++ b/triplets/__init__.py @@ -47,11 +47,16 @@ try: import duckdb as _duckdb import logging as _logging + import re as _re # for table name validation (SQL injection prevention) _duckdb_logger = _logging.getLogger(__name__) def _duckdb_read_rdf(self, paths, table_name="triplets", **kwargs): """Parse RDF/XML files and load into DuckDB table via Arrow (zero-copy).""" + # SECURITY: validate table_name to prevent SQL injection + # allow only safe identifier characters: alphanumeric, underscore, starting with letter/underscore + if not _re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', table_name): + raise ValueError(f"Invalid table name: {table_name}") arrow_table = parse(paths, return_type="arrow", **kwargs) self.register("_arrow_import", arrow_table) self.execute(f"CREATE OR REPLACE TABLE {table_name} AS SELECT * FROM _arrow_import") @@ -65,4 +70,3 @@ def _duckdb_read_rdf(self, paths, table_name="triplets", **kwargs): except ImportError: logging.getLogger(__name__).debug("duckdb not installed, skipping read_rdf registration") pass - diff --git a/triplets/_accessor.py b/triplets/_accessor.py index e6fb591..2c55285 100644 --- a/triplets/_accessor.py +++ b/triplets/_accessor.py @@ -16,6 +16,7 @@ """ import logging +import re # security: used to validate table_name in DuckDB export methods import pandas from . import tools, export @@ -149,7 +150,11 @@ def _duckdb_export(name): """Make a connection method: fetch the triplets table, run the pandas export.""" function = getattr(export, name) + # Security fix: validate table_name to prevent SQL injection + # Only allow alphanumeric characters and underscores, must start with letter or underscore def method(connection, *args, table_name="triplets", **kwargs): + if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', table_name): + raise ValueError(f"Invalid table name: {table_name}") df = connection.execute(f"SELECT * FROM {table_name}").df() return function(df, *args, **kwargs) diff --git a/triplets/cgmes_tools/static/relations_graph.html b/triplets/cgmes_tools/static/relations_graph.html index d218647..622ad20 100644 --- a/triplets/cgmes_tools/static/relations_graph.html +++ b/triplets/cgmes_tools/static/relations_graph.html @@ -4,6 +4,8 @@ $title + +