Back to skill

Security audit

AEO Prompt Question Finder

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed keyword-research helper with an optional credentialed search-volume feature, and I did not find hidden persistence, destructive behavior, or deception.

Install this only if you are comfortable with outbound keyword queries. Normal use contacts Google Autocomplete; using --volume also contacts DataForSEO and may use local DataForSEO credentials from environment variables or macOS Keychain. Avoid --volume for sensitive client, legal, medical, or confidential research terms unless that third-party sharing is acceptable.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (14)

Tainted flow: 'req' from os.environ.get (line 60, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/find_questions.py (reported line 26)May include surrounding context.

python
params = urllib.parse.urlencode({"client": "firefox", "q": query})
    url = f"{BASE_URL}?{params}"
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(req, timeout=10) as resp:
        data = json.loads(resp.read().decode())
    return data[1] if len(data) > 1 else []

Tainted flow: 'req' from os.environ.get (line 60, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

When --volume is used, the script retrieves DataForSEO credentials from environment variables or the macOS Keychain and transmits them in a Basic Authorization header to a third-party API. This is intentional for authentication, but it still expands the skill's privilege and external data exposure beyond basic autocomplete collection, increasing the risk of unintended secret use or data leakage to an unnecessary service.

Content

Scanner excerpt · scripts/find_questions.py (reported line 66)May include surrounding context.

python
})

        try:
            with urllib.request.urlopen(req, timeout=30) as resp:
                result = json.loads(resp.read().decode())
            if result.get("tasks"):
                for task in result["tasks"]:

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The stated purpose presents the skill as a simple Google Autocomplete helper, but the behavior also includes authenticated third-party API use and retrieval of stored credentials from macOS Keychain or environment variables. This is dangerous because users may invoke the skill expecting anonymous suggestion lookups while unintentionally sending topics to another provider under local credentials.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The function is explicitly designed to read secrets from the macOS Keychain. In an agent skill, local credential access is sensitive by default, especially when the skill's primary purpose does not require privileged secret retrieval to perform basic autocomplete queries.

Content

Scanner excerpt · scripts/find_questions.py (reported line 31)May include surrounding context.

python
return data[1] if len(data) > 1 else []


def get_keychain(service: str) -> str:
    """Read a value from macOS Keychain."""
    try:
        return subprocess.check_output(

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The use of the macOS Keychain via the security tool indicates direct access to stored credentials. Even without shell injection, this behavior is security-sensitive and expands the skill from harmless query collection into privileged secret retrieval.

Content

Scanner excerpt · scripts/find_questions.py (reported line 32)May include surrounding context.

python
def get_keychain(service: str) -> str:
    """Read a value from macOS Keychain."""
    try:
        return subprocess.check_output(
            ["security", "find-generic-password", "-s", service, "-w"],

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

At this line the script automatically pulls the DataForSEO login from either environment variables or the Keychain. Automatic secret consumption in a skill increases the chance of unintended credential use and makes the behavior more dangerous than its autocomplete-focused description suggests.

Content

Scanner excerpt · scripts/find_questions.py (reported line 45)May include surrounding context.

python
def fetch_search_volumes(keywords: list[str], location_code: int = 2840, language_code: str = "en") -> dict[str, int | None]:
    """Fetch avg monthly search volumes from DataForSEO Keywords Data API.
    Returns {keyword: volume} dict. Volume is None if not found."""
    login = os.environ.get("DATAFORSEO_LOGIN") or get_keychain("dataforseo-login")
    password = os.environ.get("DATAFORSEO_PASSWORD") or get_keychain("dataforseo-password")
    if not login or not password:
        print("WARNING: DataForSEO credentials not found. Skipping volume lookup.", file=sys.stderr)

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

This line automatically retrieves the DataForSEO password from environment variables or the Keychain, enabling credentialed third-party access without strong user awareness. In the context of a simple research helper, that is an unnecessary increase in sensitivity and attack surface.

Content

Scanner excerpt · scripts/find_questions.py (reported line 46)May include surrounding context.

python
"""Fetch avg monthly search volumes from DataForSEO Keywords Data API.
    Returns {keyword: volume} dict. Volume is None if not found."""
    login = os.environ.get("DATAFORSEO_LOGIN") or get_keychain("dataforseo-login")
    password = os.environ.get("DATAFORSEO_PASSWORD") or get_keychain("dataforseo-password")
    if not login or not password:
        print("WARNING: DataForSEO credentials not found. Skipping volume lookup.", file=sys.stderr)
        return {}

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documentation declares no explicit tool scope even though the described behavior requires shell execution, outbound network access, and access to environment variables or credentials. That mismatch can cause the skill to run with broader capabilities than users expect, reducing transparency and making accidental misuse or over-privileged execution more likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation does not warn users that enabling --volume sends their query topics to DataForSEO and uses stored credentials from Keychain or environment variables. This creates a privacy and consent risk, particularly if topics contain sensitive business terms, client data, or research subjects that users believed would only be sent to Google Autocomplete.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script reads credentials from environment variables and the macOS Keychain for a third-party SEO API even though the core skill description does not justify credential access. In agent settings, automatic secret discovery is dangerous because it broadens access to local sensitive material and can be surprising to operators who expect a simple public autocomplete lookup.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/find_questions.py (reported line 34)May include surrounding context.

python
def get_keychain(service: str) -> str:
    """Read a value from macOS Keychain."""
    try:
        return subprocess.check_output(
            ["security", "find-generic-password", "-s", service, "-w"],
            stderr=subprocess.DEVNULL
        ).decode().strip()

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill's stated purpose is finding Google Autocomplete questions, but it also performs optional enrichment by calling a separate commercial API. That scope expansion is risky because it causes additional outbound transmission of collected keywords and can trigger authenticated access not obviously expected from the skill description.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

This code sends collected keywords to api.dataforseo.com, an external third party, for enrichment. In context, the transmission is not inherently malicious, but it increases privacy and supply-chain risk because user research terms and associated metadata leave the local environment for a service not central to the advertised autocomplete-only function.

Content

Scanner excerpt · scripts/find_questions.py (reported line 57)May include surrounding context.

python
batch = keywords[i:i + DATAFORSEO_BATCH]
        payload = [{"keywords": batch, "location_code": location_code, "language_code": language_code}]
        data = json.dumps(payload).encode()
        url = "https://api.dataforseo.com/v3/keywords_data/google_ads/search_volume/live"

        auth = base64.b64encode(f"{login}:{password}".encode()).decode()
        req = urllib.request.Request(url, data=data, method="POST", headers={

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The documentation states --lang en with default: en, which establishes English as the default locale behavior. Under the policy, locale constraints should either be user-selectable with clear opt-in or explicitly justified as region-specific; this file does not provide that justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.