Back to skill

Security audit

Lucid Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local data-analysis tool, but its read-only and connected-data boundaries are not strong enough for the sensitive database and filesystem access it can receive.

Review before installing. Use only with least-privilege, read-only database accounts and non-production data where possible. Avoid passing secrets through shell history, set LUCID_DATA_DIR to a controlled directory, do not connect untrusted file paths or Excel sheet names, and assume local catalogs may retain schema details and sample values until deleted.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
lucid_skill/connectors/csv_conn.py:27
Finding

SQL Injection Through CSV and Excel Path Parameters

Content
View full analysis
None: csv_path = os.path.abspath(config["path"]) self.source_id = f"csv:{os.path.basename(csv_path)}" self._db = duckdb.connect(":memory:") if os.path.isdir(csv_path): files = [f for f in os.listdir(csv_path) if f.endswith(".csv")] for file in files: name = _sanitize_name(os.path.splitext(file)[0]) file_path = os.path.join(csv_path, file) self._db.execute( f'CREATE TABLE "{name}" AS SELECT * FROM read_csv_auto(\'{file_path}\')' ) self._tables.append(name) self._table_files[name] = file_path else: name = _sanitize_name(os.path.splitext(os.path.basename(csv_path))[0]) self._db.execute( f'CREATE TABLE "{name}" AS SELECT * FROM read_csv_auto(\'{csv_path}\')' ) ``` ```python # lucid_skill/connectors/csv_conn.py:70-74 def register_to_duckdb(self, db) -> list[str]: for table_name, file_path in self._table_files.items(): db.execute( f'CREATE OR REPLACE TABLE "{table_name}" AS SELECT * FROM read_csv_auto(\'{file_path}\')' ) return list(self._tables) ``` ```python # lucid_skill/connectors/excel_conn.py:28-48 def connect(self, config: dict) -> None: self._file_path = os.path.abspath(config["path"]) file_name = os.path.splitext(os.path.basename(self._file_path))[0] self.source_id = f"excel:{file_name}" self._db = duckdb.connect(":memory:") self._db.execute("INSTALL spatial; LOAD spatial;") sheets = config.get("sheets") if sheets and len(sheets) > 0: for sheet in s ...[truncated 2972 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
lucid_skill/query/safety.py:4
Finding

Read-Only SQL Guard Allows Arbitrary Local File Access Through DuckDB Functions

Content
View full analysis
tuple[bool, str | None]: """Check if SQL is safe (read-only). Returns (safe, reason).""" trimmed = sql.strip() if not trimmed: return False, "Empty SQL statement" # Remove comments cleaned = re.sub(r'--.*$', '', trimmed, flags=re.MULTILINE) cleaned = re.sub(r'/\*[\s\S]*?\*/', '', cleaned).strip() if not cleaned: return False, "Empty SQL after removing comments" # Check starts with SELECT or WITH first_word = cleaned.upper().split()[0] if first_word not in ("SELECT", "WITH"): return False, f"Only SELECT statements are allowed. Got: {first_word}" # Remove string literals for keyword checking without_strings = re.sub(r"'[^']*'", "''", cleaned) without_strings = re.sub(r'"[^"]*"', '""', without_strings) upper_no_strings = without_strings.upper() # Check forbidden keywords for keyword in FORBIDDEN_KEYWORDS: pattern = rf'(^|;|\s){keyword}(\s|\(|$)' if re.search(pattern, upper_no_strings, re.IGNORECASE): return False, f"Forbidden keyword detected: {keyword}" # Check multiple statements statements = [s for s in without_strings.split(';') if s.strip()] if len(statements) > 1: return False, "Multiple statements are not allowed" return True, None ``` ```python # lucid_skill/query/engine.py:19-29 def execute(self, sql: str, max_rows: int | None = ...[truncated 2554 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
lucid_skill/semantic/layer.py:26
Finding

Semantic Table Name Traversal Permits Writes Outside the Intended Store

Content
View full analysis
Path: """Return the path to the YAML file for a given source and table.""" config = get_config() store_path = Path(config.semantic.store_path) return store_path / sanitize_source_id(source_id) / f"{table_name}.yaml" ``` ```python # lucid_skill/semantic/layer.py:83-90 def write_table_semantic(source_id: str, table_name: str, semantic: TableSemantic) -> None: """Write a TableSemantic to the YAML file, creating directories as needed.""" path = get_semantic_file_path(source_id, table_name) path.parent.mkdir(parents=True, exist_ok=True) # Update the timestamp semantic.updated_at = datetime.now(timezone.utc).isoformat() data = _table_semantic_to_dict(semantic) with open(path, "w", encoding="utf-8") as f: ``` ### Technical Analysis `source_id` is sanitized before use as a directory component, but `table_name` is appended directly ...[truncated 2010 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
lucid_skill/connectors/excel_conn.py:32
Finding

Excel Connection Installs and Loads an Unpinned Native DuckDB Extension at Runtime

Content
View full analysis
list[str]: db.execute("INSTALL spatial; LOAD spatial;") for table_name, sheet in self._table_sheets.items(): ``` ### Technical Analysis Every Excel connection executes DuckDB's `INSTALL spatial` followed by `LOAD spatial`. If the extension is not already installed, `INSTALL` can retrieve a platform-specific native extension from DuckDB's configured extension repository. `LOAD` then executes that native component inside the `lucid-skill` process. The code does not pin an extension artifact or version, verify an application-managed checksum, request explicit approval at connection time, or configure a fixed trusted repository in the shown implementation. Runtime retrieval makes behavior dependent on external infrastructure and the DuckDB version available in the environment. The operation is needed to expose `read_xlsx` in this design, but installing executable code dynamically grants more privilege than reading a spreadsheet from an already provisioned parser. ### Attack Path 1. A user or agent invokes an Excel connection. 2. The connector unconditionally executes `INSTALL spatial`. 3. DuckDB checks for or downloads the extension from its configured source. 4. The connector immediately executes `LOAD spatial`. 5. Native extension code runs in the same process and with the same privileges as the Skill. 6. A compromised distribution source, network interception under an improperly trusted configuration, or compromised cached artifact could affect the loaded code. ### Impact Ass ...[truncated 522 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (60)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is described as a read-only data analysis tool, but the documentation also shows persistent local state management for semantic metadata and mentions deletion behavior. This mismatch can mislead users and reviewers about what the skill changes on disk, which matters because semantic files may contain sensitive schema or business-context information and local file operations expand the attack surface beyond simple SQL reads.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The connector exposes a raw execute_query(sql) method that sends arbitrary SQL directly to MySQL with no validation or enforcement of the skill's declared read-only scope. In this skill context, where natural-language requests may be translated into SQL, this creates a direct path for destructive or sensitive statements such as UPDATE, DELETE, DROP, DDL, or privilege-affecting queries if upstream controls fail or are bypassed.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The connector exposes a generic execute_query(sql) method that sends arbitrary SQL directly to PostgreSQL with cursor.execute(sql) and performs no enforcement that queries are read-only. In the context of a skill whose manifest explicitly promises INSERT/UPDATE/DELETE/DROP are blocked, this creates a direct policy bypass that could allow destructive writes, privilege changes, or execution of dangerous statements if an upstream agent or prompt supplies them.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill manifest claims read-only SQL, but execute_raw() and run() bypass the safety check entirely and allow arbitrary SQL to be executed against the backing DuckDB connection. If these methods are reachable by agent logic, prompts, or future integrations, an attacker could perform INSERT/UPDATE/DELETE/DROP, attach external resources, or otherwise modify or destroy connected data, violating the skill's stated security boundary.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest explicitly frames the skill as read-only and says data modification operations are blocked. This module persists semantic metadata to disk and updates timestamps, which is a state-changing write capability outside a purely read-only analytics scope.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest says the skill is not for data modification and emphasizes read-only behavior. Even if this function targets local semantic metadata rather than source databases, file deletion is still a destructive modification capability not disclosed by the description.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill advertises capabilities that necessarily involve reading environment variables and reading/writing local files, but it does not declare any explicit tool scope or permissions boundary. That omission weakens reviewability and least-privilege enforcement, increasing the chance that a host agent grants broader file or env access than users expect when they only intended read-only data analysis.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The manifest description defines activation using expansive natural-language conditions rather than a tightly bounded invocation scope. Although some examples are data-related, phrases like 'analyze' or '帮我看下数据' are broad enough that the skill could be invoked for general requests involving data without clear exclusion boundaries beyond a few negative cases.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill asks users to connect files and databases, including potentially providing credentials, but it does not clearly warn that sensitive business data, schema details, and query results will be exposed to the tool during processing. That lack of disclosure can lead to inadvertent sharing of confidential information and weakens informed consent in a high-sensitivity context.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest emphasizes read-only querying against connected data sources and explicitly says data modification is blocked, but this module implements broad write/delete operations to a persistent DuckDB catalog, including inserts, updates, and deletes for sources, metadata, embeddings, join paths, cache state, and domains. Although these writes target internal metadata rather than user source databases, they still exceed a plain read-only analysis description and introduce stateful modification behavior not reflected in the manifest.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code serializes and persists sample_values from source columns into the local catalog without any visible minimization, masking, consent, or disclosure. Sample values often contain real records, so this can silently duplicate sensitive data such as emails, IDs, names, or tokens into a secondary store, increasing exposure and retention risk beyond the original datasource.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Accepting database passwords via command-line flags exposes secrets through shell history, process listings, audit logs, and orchestration tooling that captures argv. In a data-access skill, this is especially risky because the credentials may grant direct access to production databases and can be harvested by local users or monitoring systems.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

PostgreSQL passwords passed as CLI arguments have the same exposure problem: they can leak via process tables, shell history, terminal logging, and CI/CD job output. Because this skill is designed to connect to user-supplied databases for analysis, credential compromise can lead to unauthorized disclosure of the underlying business data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest emphasizes read-only analytics and explicitly says data modification is blocked, but this CLI exposes an update-semantic command that reads JSON from a file/stdin and writes semantic definitions via handle_update_semantic. While this may not modify source databases, it is still a persistent write capability beyond pure read-only querying/analysis as described.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The connector explicitly returns raw sample rows from connected CSV data without any built-in consent, masking, or warning mechanism. In a data-analysis skill that operates on user-provided files, sample extraction is expected behavior, but it still creates a genuine data exposure risk because sensitive fields may be surfaced to the model or end user automatically during schema inspection or preview.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code reads a user-specified Excel file, enumerates sheets, counts rows, and returns sample cell values, which can expose user data. There is no confirmation prompt, logging/print statement, or explanatory comment/docstring warning the user that file contents will be loaded into memory and sampled.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
78% confidence
Finding

The connector establishes a network connection to a MySQL server using host, username, and password from configuration. Although this is expected behavior for a connector, the file provides no docstring, comment, logging, or other user-facing disclosure that credentials are used to connect to an external system.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Table and column identifiers are interpolated into SQL using f-strings in metadata/sample helper methods, and identifiers cannot be safely parameterized the same way as values. If an attacker can influence table or column names through upstream input or poisoned metadata, this can lead to SQL injection in supposedly safe introspection paths and undermines the claim that unsafe operations are blocked.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code fetches and returns live row data from a MySQL table, which can expose sensitive user or business information. There is no confirmation prompt, logging, comment, or docstring warning that the method reads and returns sample records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code reads sensitive connection details including username and password and immediately uses them to establish a network connection, but the file contains no confirmation prompt, logging, comment, or docstring disclosing that credentialed external access occurs. For a code file, accessing credentials and transmitting them over a network is a safety-relevant action that should be visibly disclosed somewhere in the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

get_sample_data returns raw rows from arbitrary tables without any masking, sensitivity checks, or access controls, which can expose personal or confidential data during routine exploration. In a natural-language data-analysis skill, automatic sample retrieval is more dangerous because users and upstream agents may trigger it broadly across unknown schemas containing sensitive records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The column profiling logic fetches up to five distinct non-null values for every column, which can systematically exfiltrate representative values from sensitive columns such as emails, account numbers, or identifiers. Because it iterates across all columns, it increases exposure beyond ad hoc sampling and can leak high-value data even when only metadata discovery was intended.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The cross-source discovery routine unconditionally calls catalog.get_sources() and aggregates table/column metadata from every connected source, then correlates them using name matching and embeddings. In a multi-tenant or least-privilege setting, this expands visibility beyond the user-selected source and can leak existence of tables, business domains, and likely relationships across otherwise separate datasets.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The raw execution methods execute SQL directly with no safety validation, no warning, and no indication that database-changing statements are possible despite the skill being presented as read-only. This creates a dangerous mismatch between user expectations and actual capability, increasing the chance of accidental or adversarial data modification if these methods are invoked.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code file executes SQL directly against a MySQL or PostgreSQL connector via execute_query, which is a safety-relevant external operation. While the code performs a safety check on SQL, there is no confirmation prompt, logging/print statement, or inline comment/docstring warning the user that the query may be sent to a live external database rather than only processed locally.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.