Back to skill

Security audit

Dialogflow CX to CES Migration

Security checks for vulnerabilities and agentic risk

Overview

This migration skill is purpose-aligned and user-directed, but users should carefully review generated CES instructions and webhook tools before importing them into production.

Install only in an isolated environment with the minimum GCP permissions needed to read the source Dialogflow CX agent. Before importing generated CES files, review every generated instruction for prompt-injection text, confirm every webhook endpoint and authentication setting, and pin dependencies if this will be used in a production migration workflow.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
migrate.py:347
Finding
Untrusted Dialogflow CX Content Is Embedded into Authoritative CES Instructions<![CDATA[ ## Vulnerability Details **File Location**: `migrate.py:347`, `migrate.py:359-375`, and `migrate.py:479-488` **Vulnerability Type**: Prompt injection through untrusted migration data **Risk Level**: High ### Vulnerable Code ```python # Add intent-based routing hints instructions.append("") instructions.append("## Intent routing hints:") for intent in intents: if intent.display_name.startswith("Default"): continue if intent.training_phrases: samples = [tp.parts[0].text for tp in intent.training_phrases[:2] if tp.parts] instructions.append(f"- '{intent.display_name}': triggered by phrases like {samples}") ``` ```python if page.entry_messages: instructions.append(f"Say: \"{page.entry_messages[0]}\"") if page.parameters: instructions.append("Collect the following information from the user:") for param in page.parameters: req = "required" if param.required else "optional" prompt = f" Ask: \"{param.prompts[0]}\"" if param.prompts else "" instructions.append(f" - **{param.name}** ({param.entity_type}, {req}).{prompt}") if page.routes: instructions.append("Transition rules:") for route in page.routes: if route["condition"] or route["messages"]: cond = route["condition"] or "after collecting parameters" msgs = f" Respond: \"{route['messages'][0]}\"" if route["messages"] else "" target = route["target"] instructions.append(f" - When {cond}:{msgs} → go to {target}") ``` ```python ces_agent = { "displayName": result.source_agent_name, "defaultLanguageCode": "en", "timeZone": "America/Los_Angeles", "description": f"Migrated from Dialogflow CX agent {result.source_agent_id}", "globalInstruction": "\n".join(result.root_agent_instructions), "agents": [], "tools": [], } for sub in result.sub_agents: ces_agent["agents"].append({ "displayName": sub.name, "description": sub.description, ...[truncated 2300 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all source-agent values as untrusted data rather than instruction text. 2. Represent routes, messages, parameters, and examples in structured CES fields wherever possible instead of concatenating them into system instructions. 3. Place unavoidable source text inside strongly delimited data blocks and explicitly state that quoted content must never be treated as instructions. 4. Escape control characters and Markdown constructs that can break out of the intended representation. 5. Detect instruction-like phrases, including attempts to override prior instructions, disclose data, or invoke tools. Block the migration or emit a high-visibility warning when such content is found. 6. Generate a review manifest listing every source value promoted into an instruction field. 7. Require explicit human approval before producing an importable file when untrusted content is present. 8. Add adversarial tests containing prompt-injection payloads in every migrated source field and verify that they cannot alter generated agent behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
migrate.py:175
Finding
Source-Controlled Webhook URLs Become Callable CES Tools Without Destination Validation<![CDATA[ ## Vulnerability Details **File Location**: `migrate.py:175-190` and `migrate.py:493-520` **Vulnerability Type**: Unvalidated external endpoint migration and unsafe tool configuration **Risk Level**: Medium ### Vulnerable Code ```python for wh in webhooks: endpoint = "" auth_type = "NONE" if wh.generic_web_service.uri: endpoint = wh.generic_web_service.uri if wh.generic_web_service.allowed_ca_certs: auth_type = "MTLS" result.tools.append(CESTool( name=wh.display_name.lower().replace(" ", "_").replace("-", "_"), cx_webhook_id=wh.name.split("/")[-1], description=f"Migrated from Dialogflow CX webhook: {wh.display_name}", endpoint=endpoint, auth_type=auth_type, )) tool_map = {t.cx_webhook_id: t.name for t in result.tools} ``` ```python for tool in result.tools: ces_agent["tools"].append({ "displayName": tool.name, "description": tool.description, "openapiTool": { "textSchema": json.dumps({ "openapi": "3.0.0", "info": {"title": tool.name, "version": "1.0.0"}, "servers": [{"url": tool.endpoint or "https://REPLACE_WITH_ENDPOINT"}], "paths": { "/": { "post": { "summary": tool.description, "operationId": tool.name, "requestBody": { "content": {"application/json": {"schema": {"type": "object"}}} }, "responses": {"200": {"description": "Success"}} } } } }, indent=2), "authentication": {"authType": tool.auth_type}, } }) ``` ### Technical Analysis The source webhook URI is copied directly into an OpenAPI `servers` entry. The implementation does not validate the URL ...[truncated 1860 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate migrated tools in a disabled or unapproved state by default. 2. Permit only HTTPS endpoints and reject URLs containing embedded credentials or unsupported schemes. 3. Resolve hostnames and reject loopback, private, link-local, multicast, reserved, and cloud metadata addresses. Repeat validation after redirects and DNS resolution. 4. Enforce an organization-managed allowlist of approved domains and endpoint owners. 5. Require explicit operator confirmation for every migrated webhook before producing an importable CES configuration. 6. Preserve supported authentication settings accurately and fail closed when the source authentication cannot be represented securely. 7. Do not default an existing endpoint to unauthenticated operation merely because mTLS certificates are absent. 8. Add a report section identifying endpoint, authentication mode, validation result, and every sub-agent referencing each tool. 9. Apply strict request and response schemas rather than using an unrestricted object body. 10. Use network egress controls in the CES environment to prevent access to internal and metadata services. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:11
Finding
Runtime Dependencies Are Not Reproducibly Pinned<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:11` and `SKILL.md:46` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Low ### Vulnerable Code ```yaml metadata: openclaw: requires: bins: ["python", "gcloud"] pip: ["google-cloud-dialogflow-cx>=1.28.0", "google-auth"] ``` ```bash pip install google-cloud-dialogflow-cx google-auth ``` ### Technical Analysis The dependency declaration accepts any version of `google-cloud-dialogflow-cx` at or above 1.28.0 and places no version constraint on `google-auth`. The documented installation command also installs whatever versions the package index currently resolves. The named packages appear consistent with official Google packages; the audit found no evidence of typosquatting or an intentionally malicious dependency. Nevertheless, mutable dependency resolution prevents reproducible installation and means future releases can be consumed without review. Python packages may execute code during installation or import. Therefore, compromise of a package release, package index, or dependency chain could affect the migration environment. ### Attack Path 1. An operator follows the documented installation process or the Skill runtime installs the declared dependencies. 2. The package index resolves versions that were not reviewed with this project. 3. A compromised, malicious, or incompatible package or transitive dependency is downloaded. 4. Package code executes during installation or when `migrate.py` imports the dependency. 5. The package obtains the privileges of the user running the migration and may access resources available to that process, including Application Default Credentials. ### Impact Assessment A compromised dependency would run with the migration process's local user privileges. Because the tool loads Google Application Default Credentials and communicates with Dialogflow CX APIs, malicious dependency code could potentially access those creden ...[truncated 231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin direct dependencies to exact versions that have been reviewed and tested. 2. Generate and commit a lock file containing all transitive dependency versions. 3. Record cryptographic hashes and install with `pip --require-hashes`. 4. Use an organization-controlled package mirror or explicitly approved package index. 5. Run dependency vulnerability and provenance scanning in continuous integration. 6. Update dependencies through a controlled review process rather than resolving unrestricted versions at runtime. 7. Install dependencies in an isolated virtual environment or container with minimal filesystem and credential access. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • 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 (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared behavior says the skill performs a full migration with retry logic and dry-run support, but the observed behavior only validates local output files and reports status. This mismatch is dangerous because users may trust it with production migration workflows, assume artifacts were generated correctly, and make operational decisions based on incomplete or nonexistent migration actions.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
samples = [tp.parts[0].text for tp in intent.training_phrases[:2] if tp.parts]
                instructions.append(f"- '{intent.display_name}': triggered by phrases like {samples}")

        return instructions

    def _pages_to_instructions(self, flow_name: str, pages: list[CESPage]) -> list[str]:
        """Convert CX pages to natural language instructions for a CES sub-agent."""
Confidence
89% confidence
Finding
This tool directly copies Dialogflow intent training phrases into generated CES root instructions as routing hints. If the source agent contains unsafe, adversarial, or sensitive prompt-like content, that content is promoted into system-level instructions for the migrated agent, creating a prompt injection and instruction poisoning risk in a production migration context.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
instructions.append("")

        return instructions

    def _convert_test_cases(self, test_cases, intent_map: dict) -> list[dict]:
        """Convert Dialogflow CX test cases to CES golden eval format."""
Confidence
91% confidence
Finding
The migration logic converts page content, prompts, fulfillment messages, and route text from the source agent into natural-language instructions for CES sub-agents. Because all source content is treated as trusted instruction text, a malicious or compromised Dialogflow CX agent could inject hidden directives, override behavior, or exfiltration-oriented instructions into the migrated agent's control plane.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill advertises capabilities that imply reading and writing files, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization boundary gap where a caller or runtime may grant broader filesystem access than intended, increasing the chance of unintended data exposure or file modification during execution.

Skill Enumeration

Medium
Category
Agent Snooping
Content
# Autoresearch Config for dialogflow-cx-to-ces-migration

## Target Skill
`skills/dialogflow-cx-to-ces-migration/SKILL.md`

## Test Inputs (5 scenarios)
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The generated instruction string says the agent 'communicate[s] in {agent.default_language_code}', which hard-codes a language choice into the migrated agent behavior. This is a natural-language locale policy issue because it does not offer the user any language choice or opt-in.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The exported CES agent definition sets "defaultLanguageCode" to "en" regardless of the source agent's actual language. This forces a specific language/locale in generated configuration and can violate language-choice policy when the source agent is not English or when no user opt-in exists.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The global rejection phrase 'No' is also overly generic and may be matched during unrelated conversation turns, causing premature cancellation, session termination, or navigation to the wrong flow. Because this agent uses 'NO' to end flows and reset behavior, an accidental or adversarially induced match could disrupt user actions such as bookings or support requests.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The global rejection phrase 'No' is also overly generic and may be matched during unrelated conversation turns, causing premature cancellation, session termination, or navigation to the wrong flow. Because this agent uses 'NO' to end flows and reset behavior, an accidental or adversarially induced match could disrupt user actions such as bookings or support requests.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The natural-language instruction explicitly states 'You communicate in en,' which imposes a single language choice on all interactions. There is no indication that users may choose another language or opt into this locale restriction.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The file defines a six-digit "pin_code" entity, which is a locale-specific term and format commonly associated with India, and the surrounding entities also reference India-specific vehicle models and registration formats. In a globally scoped skill artifact, this can amount to an implicit locale constraint without user choice or an explicit statement that the skill is region-specific.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The natural-language input "ANDROID" appears to encode a platform/locale-style constraint as a fixed utterance, but the file provides no indication that users are offered a language or locale choice or that this constraint is justified. Under the policy rule, fixed natural-language constraints should be documented or optional when they could steer behavior without opt-in.

Static analysis

No suspicious patterns detected.