Back to skill

Security audit

NL-to-SQL Query Builder

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent NL-to-SQL toolkit, but it handles database queries, audit logs, and clarification sessions with weak controls that should be reviewed before installation.

Review this skill carefully before installing in any environment with real business data. Use only with sandbox or read-only databases unless you add authorization, SQL safety checks, log redaction, retention limits, safe export paths, and non-debug authenticated clarification routes. Note that audit_logger.py also has a syntax error and will not load unchanged.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (6)

T09 · Insecure Skill Coding Practices

Warning
Location
schema_context_builder.py:96
Finding

Database connection information is persisted without redaction

Content
View full analysis
Remediation
View remediation
" }, "tables": tables, "generated_at": datetime.now().isoformat() } ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
schema_context_builder.py:45
Finding

Database identifiers are interpolated into SQL without safe quoting

Content
View full analysis
List[Dict[str, Any]]: """Get column metadata for a table""" cursor = self.conn.cursor() cursor.execute(f"PRAGMA table_info({table_name})") columns = [] for row in cursor.fetchall(): columns.append({ 'name': row[1], 'type': row[2], 'nullable': not bool(row[3]), 'primary_key': bool(row[5]) }) return columns def get_row_count(self, table_name: str) -> int: """Get approximate row count""" cursor = self.conn.cursor() cursor.execute(f"SELECT COUNT(*) FROM {table_name}") return cursor.fetchone()[0] def get_foreign_keys(self, table_name: str) -> List[Dict[str, str]]: """Get foreign key relationships""" cursor = self.conn.cursor() cursor.execute(f"PRAGMA foreign_key_list({table_name})") fks = [] for row in cursor.fetchall(): fks.append({ 'from_column': row[3], 'to_table': row[2], 'to_column': row[4] }) return fks def get_indexes(self, table_name: str) -> List[Dict[str, Any]]: """Get table indexes""" cursor = self.conn.cursor() cursor.execute(f"PRAGMA index_list({table_name})") ``` ### Technical Analysis Table names obtained from `sqlite_master` are inserted directly into SQL and PRAGMA statements using f-strings. SQL value parameters cannot safely bind identifiers, but this does not make direct interpolation safe. Identifiers must be validated and quoted according to SQLite identifier rules. A crafted SQLite database can contain unusual table names with whitespace, quotes, parentheses, keywords, or other syntax-significant characters. When such a name is interpolated, it can alter parsing or cause metadata extraction ...[truncated 1143 chars]
Remediation
View remediation
str: if not isinstance(identifier, str) or "\x00" in identifier: raise ValueError("Invalid SQLite identifier") return '"' + identifier.replace('"', '""') + '"' quoted_table = quote_identifier(table_name) cursor.execute(f"SELECT COUNT(*) FROM {quoted_table}") cursor.execute(f"PRAGMA table_info({quoted_table})") ``` - Add tests using table names containing spaces, quotes, parentheses, SQL keywords, and Unicode characters. - Open untrusted databases in read-only mode and apply SQLite defensive configuration where supported. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
disambiguation_ui.py:151
Finding

Flask application starts with the development debugger enabled

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
disambiguation_ui.py:119
Finding

Clarification sessions are accessible without authentication or ownership checks

Content
View full analysis
Dict[str, Any]: """Create a clarification request for user""" clarification = { 'session_id': session_id, 'original_query': original_query, 'ambiguities': ambiguities, 'options': self._generate_options(ambiguities), 'created_at': None # Set by caller } clarification_sessions[session_id] = clarification return clarification ``` ```python @app.route('/clarify/', methods=['GET', 'POST']) def clarify(session_id): """Render or process clarification UI""" session = clarification_sessions.get(session_id) if not session: return jsonify({'error': 'Session not found'}), 404 if request.method == 'GET': return render_template_string( CLARIFICATION_HTML, original_query=session['original_query'], options=session['options'], session_id=session_id ) # POST - process answers answers = {} for key, value in request.form.items(): if key.endswith('_custom') and value: field = key[:-7] answers[field] = value elif not key.endswith('_custom'): answers[key] = value return jsonify({'status': 'clarified', 'answers': answers}) ``` ### Technical Analysis The route treats knowledge of `session_id` as sufficient authorization. Sessions are created using a caller-supplied identifier and are not associated with an authenticated user, tenant, or authoriza ...[truncated 1514 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
audit_logger.py:39
Finding

Audit records retain complete sensitive query data in plaintext

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
fallback_handler.py:164
Finding

Negative review identifiers bypass review bounds validation

Content
View full analysis
Dict[str, Any]: """Mark review as approved, return for execution""" with self._lock: if review_id >= len(self.fallback_log): return {'error': 'Review not found'} review = self.fallback_log[review_id] review['status'] = 'approved' review['modified_query'] = modified_query return { 'status': 'ready_for_execution', 'query': modified_query or review['query'], 'context': review['context'] } def reject(self, review_id: int, reason: str) -> Dict[str, Any]: """Reject a query from human review""" with self._lock: if review_id >= len(self.fallback_log): return {'error': 'Review not found'} review = self.fallback_log[review_id] review['status'] = 'rejected' review['rejection_reason'] = reason ``` ### Technical Analysis The validation checks only whether `review_id` is greater than or equal to the list length. Python lists accept negative indexes, where `-1` refers to the last element, `-2` to the second-to-last element, and so forth. Consequently, a negative identifier passes the validation and selects a review from the end of the queue. The methods also contain no reviewer identity or ownership checks, although the direct vulnerability here is the lower-bound validation failure. ### Attack Path 1. At least one item exists in `fallback_log`. 2. A caller invokes `approve_and_execute(-1)` or `reject(-1, reason)`. 3. The condition `review_id >= len(self.fallback_log)` evaluates to false. 4. Python resolves `self.fallback_log[-1]` as the most recent review. 5. The unintended review is approv ...[truncated 643 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (19)

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 18)May include surrounding context.

md
### Features
- **Multi-domain support**: Tested across 10+ industries
- **Confidence thresholds**: 85% auto-execute, 60% clarify, below 60% human review
- **Disambiguation flow**: Interactive UI for clarifying ambiguous queries
- **Error recovery**: Automatic retry with modified prompts
- **Full audit trail**: Compliance-ready logging

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The export function writes the full audit trail, including user queries, generated SQL, user IDs, session IDs, and errors, to any caller-supplied file path with no validation, access control, or safety checks. In an NL-to-SQL context, these records can contain sensitive business data, credentials embedded in queries, or behavioral history, so unrestricted export increases the risk of data exfiltration or accidental disclosure.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · confidence_scorer.py (reported line 20)May include surrounding context.

python
score: float  # 0-100
    factors: List[str]
    warnings: List[str]
    recommended_action: str  # 'auto_execute', 'clarify', 'human_review'


class ConfidenceScorer:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · confidence_scorer.py (reported line 66)May include surrounding context.

python
score: float  # 0-100
    factors: List[str]
    warnings: List[str]
    recommended_action: str  # 'auto_execute', 'clarify', 'human_review'


class ConfidenceScorer:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · fallback_handler.py (reported line 29)May include surrounding context.

python
score: float  # 0-100
    factors: List[str]
    warnings: List[str]
    recommended_action: str  # 'auto_execute', 'clarify', 'human_review'


class ConfidenceScorer:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · fallback_handler.py (reported line 42)May include surrounding context.

python
score: float  # 0-100
    factors: List[str]
    warnings: List[str]
    recommended_action: str  # 'auto_execute', 'clarify', 'human_review'


class ConfidenceScorer:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · fallback_handler.py (reported line 47)May include surrounding context.

python
score: float  # 0-100
    factors: List[str]
    warnings: List[str]
    recommended_action: str  # 'auto_execute', 'clarify', 'human_review'


class ConfidenceScorer:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · confidence_scorer.py (reported line 27)May include surrounding context.

python
"""Calculate confidence scores for NL-to-SQL queries."""
    
    # Thresholds from production system
    AUTO_EXECUTE_THRESHOLD = 85
    CLARIFY_THRESHOLD = 60
    
    def __init__(self, schema: Dict, threshold_override: Optional[Dict] = None):

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · fallback_handler.py (reported line 37)May include surrounding context.

python
"""Calculate confidence scores for NL-to-SQL queries."""
    
    # Thresholds from production system
    AUTO_EXECUTE_THRESHOLD = 85
    CLARIFY_THRESHOLD = 60
    
    def __init__(self, schema: Dict, threshold_override: Optional[Dict] = None):

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Allowing caller-supplied threshold_override to lower the auto-execute threshold can weaken safety policy and increase the chance that ambiguous NL-to-SQL requests are automatically executed. In a system where this scorer gates database actions, an attacker or misconfigured component could set permissive thresholds and bypass intended human review.

Content

Scanner excerpt · confidence_scorer.py (reported line 37)May include surrounding context.

python
self.relationships = schema.get('relationships', [])
        
        if threshold_override:
            self.AUTO_EXECUTE_THRESHOLD = threshold_override.get('auto', 85)
            self.CLARIFY_THRESHOLD = threshold_override.get('clarify', 60)
    
    def score(self, query: str, intent: Dict, constraints: Dict) -> ConfidenceResult:

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

This branch explicitly authorizes an 'auto_execute' recommendation based on a heuristic score derived from simple string matching and lightweight penalties. In NL-to-SQL contexts, heuristic confidence scoring can misclassify ambiguous or adversarial prompts, leading to unsafe autonomous execution of database queries without adequate review.

Content

Scanner excerpt · confidence_scorer.py (reported line 65)May include surrounding context.

python
total_score = max(0, min(100, total_score))
        
        # Determine recommended action
        if total_score >= self.AUTO_EXECUTE_THRESHOLD:
            action = 'auto_execute'
        elif total_score >= self.CLARIFY_THRESHOLD:
            action = 'clarify'

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

The top-level documentation frames the file as UI components for a larger NL-to-SQL skill, implying an embedded helper role. In practice, the code defines a global Flask app, registers a public route, and runs a debug web server when executed directly, which is a materially broader behavior than 'components' alone.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

This code path allows automatic approval when confidence exceeds a threshold and ambiguity is not detected, relying only on a boolean safety_check_passed supplied by the caller. In an NL-to-SQL skill, confidence is not a security boundary; misclassification or weak upstream checks could allow unintended or over-privileged SQL to be auto-executed without human review.

Content

Scanner excerpt · fallback_handler.py (reported line 74)May include surrounding context.

python
fallback_queue=None
            )
        
        # Auto-execute if high confidence
        if confidence >= self.auto_execute_threshold and not ambiguity_detected:
            return FallbackDecision(
                should_fallback=False,

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

The conditional grants an execution path based on confidence and ambiguity rather than explicit query risk or authorization. In this skill context, that makes the component more dangerous because NL-to-SQL systems can generate harmful or privacy-impacting queries even when they appear semantically confident.

Content

Scanner excerpt · fallback_handler.py (reported line 75)May include surrounding context.

python
)
        
        # Auto-execute if high confidence
        if confidence >= self.auto_execute_threshold and not ambiguity_detected:
            return FallbackDecision(
                should_fallback=False,
                reason=None,

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

Returning recommended_action='auto_execute' operationalizes autonomous execution and may be consumed by downstream components as approval to run SQL without additional safeguards. In an NL-to-SQL environment, this can lead to unauthorized data access or unintended query execution if downstream systems trust this recommendation too broadly.

Content

Scanner excerpt · fallback_handler.py (reported line 80)May include surrounding context.

python
should_fallback=False,
                reason=None,
                confidence=confidence,
                recommended_action='auto_execute',
                fallback_queue=None
            )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The export_log method writes raw user queries, user IDs, timestamps, and arbitrary context to any supplied filepath without validation, access control, redaction, or safety prompts. In an NL-to-SQL workflow, this data can contain sensitive business queries, schema clues, or personal data, so unrestricted export increases the risk of data leakage or unsafe file writes by callers.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The docstring advertises broad compliance tracking for all queries and interpretations, which implies comprehensive capture of NL-to-SQL reasoning artifacts. In practice, the logger only stores a fixed set of fields passed into log_query and has no logic to independently capture full interpretations, reasoning traces, or guarantee that all queries are recorded.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code displays the user's original query in the web UI, which may contain sensitive business or personal data, but there is no accompanying notice, comment, or visible disclosure about that exposure. For a code file, user-data display/handling that could affect privacy should have at least some warning or explanation when not obviously disclosed elsewhere in the file.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The docstring at L127 says the method 'Route query to human review queue', which implies routing behavior, but the implementation also stores the full review item in an internal log (L138-L139) and a separate method can export that accumulated log to a JSON file (L187-L191). This is not merely an implementation detail of queueing because it changes data handling behavior by retaining and later writing user queries, IDs, and context.

Content

No source excerpt is available for this finding.