Back to skill

Security audit

Seo Keyword Research

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent SEO tooling, but it has review-worthy network and credential risks in its bundled helpers.

Review before installing. Use this only for public SEO research data you are comfortable sending to AIsa/DataForSEO, avoid private or internal URLs, and patch or restrict the helpers so AISA_API_KEY is sent only to api.aisa.one and the crawler cannot access internal network addresses or follow unsafe redirects.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/aisa_client.py:54
Finding

AIsa API Key Disclosure to Arbitrary HTTPS Destinations

Content
View full analysis
str: if path_or_url.startswith("https://"): return path_or_url if not path_or_url.startswith("/"): path_or_url = "/" + path_or_url return API_BASE + path_or_url def post_json(url: str, payload: object) -> object: body = json.dumps(payload).encode("utf-8") request = urllib.request.Request( url, data=body, method="POST", headers={ "Authorization": f"Bearer {api_key()}", "Content-Type": "application/json", }, ) ``` The affected function is reached directly by the `data` command: ```python def command_data(args: argparse.Namespace) -> None: payload = load_json(args.payload) result = post_json(resolve_url(args.path), payload) write_output(args.out, json.dumps(result, ensure_ascii=False, indent=2)) ``` ### Technical Analysis The `data` command accepts a caller-controlled API path. The `resolve_url()` function treats any string beginning with `https://` as an acceptable absolute destination, without verifying that its parsed hostname is exactly `api.aisa.one`. The resulting URL is passed to `post_json()`, which unconditionally adds the value of `AISA_API_KEY` to the `Authorization` header. Consequently, the script forwards the credential to any HTTPS server selected by the caller. A string-prefix check is not an adequate origin validation mechanism. The destination must be parsed and validated by scheme, normalized hostname, port, and redirect behavior. `urllib.request.urlopen()` also follows HTTP redirects by default, so authorization forwarding and redirect handling should be explicitly constrained. ### Attack Path 1. An attacker persuades the user or agent to invo ...[truncated 1046 chars]
Remediation
View remediation
str: if "://" in path or path.startswith("//"): raise ValueError("Only relative AIsa API paths are allowed") if not path.startswith("/"): path = "/" + path return API_BASE + path ``` 2. If absolute URLs are required, parse and enforce an exact destination allowlist: - Scheme must be `https`. - Hostname must be exactly `api.aisa.one`. - Port must be absent or explicitly allowed. - URL user information must be rejected. - Do not use suffix or substring hostname matching. 3. Disable automatic redirects or validate every redirect target before following it. Never forward the `Authorization` header across origins. 4. Keep credential attachment destination-aware. Add the bearer token only after confirming the final request origin is the trusted AIsa API origin. 5. Add tests covering attacker-controlled hosts, deceptive hostnames, user-information syntax, alternate ports, protocol-relative URLs, and cross-origin redirects. 6. Rotate any API key that may previously have been used with an untrusted absolute URL. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/site_crawler.py:138
Finding

Server-Side Request Forgery Through Unrestricted Website Crawling and Redirects

Content
View full analysis
bool: parsed = urllib.parse.urlsplit(url) if parsed.scheme not in {"http", "https"}: return False path = parsed.path.lower() if re.search(r"\.(jpg|jpeg|png|gif|webp|svg|pdf|zip|mp4|mp3|css|js|ico)$", path): return False if any(x in path for x in ["/login", "/signup", "/cart", "/checkout"]): return False return True ``` The request is then made without validating the resolved address or final redirect destination: ```python def fetch(url: str, timeout: int) -> tuple[int | None, str, str, str]: request = urllib.request.Request(url, headers={"User-Agent": DEFAULT_USER_AGENT}) try: with urllib.request.urlopen(request, timeout=timeout, context=ssl_context()) as response: content_type = response.headers.get("content-type", "") if "text/html" not in content_type and "application/xhtml" not in content_type: return response.status, "", content_type, "non-html response" raw = response.read(1_500_000) charset = response.headers.get_content_charset() or "utf-8" return response.status, raw.decode(charset, errors="replace"), content_type, "" except urllib.error.HTTPError as exc: return exc.code, "", "", str(exc) except urllib.error.URLError as exc: return None, "", "", str(exc.reason) ``` The caller-controlled starting URL reaches `fetch()` directly: ```python def crawl(start_url: str, max_pages: int, timeout: int, delay: float) -> dict[str, object]: if not urllib.parse.urlsplit(start_url).scheme: start_url = "https://" + start_url start ...[truncated 3349 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (14)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The invocation guidance is overly broad and authorizes the skill for generic 'web search, research, source discovery, or content extraction' requests, which can cause the orchestrator to select this skill far outside narrow SEO use cases. In an agent system, such scope creep increases the chance of unnecessary website crawling, external API use, and data handling in contexts where a more constrained skill should be used.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes Python scripts, reads environment variables, writes output files, and makes network requests, but it does not declare an explicit tool scope such as permissions or allowed-tools. In an agent runtime, this increases the chance the skill is executed with broader file, network, or secret access than intended, which can lead to unintended crawling, data export, or secret exposure through loosely governed execution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation text is broad enough to match generic web search, source discovery, and content extraction tasks beyond narrow SEO keyword research. In agent orchestrators, overly broad routing can cause this skill to activate on unrelated prompts, leading to unnecessary crawling, external API calls, and transmission of user-provided URLs or content to third-party services.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The documented Data API endpoint establishes that the skill depends on external network transmission for keyword and site-analysis workflows. Since the skill may crawl a user-supplied website first and then send derived data externally, there is a meaningful confidentiality and governance risk if users are not clearly informed or the runtime does not constrain outbound requests.

Content

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

md
Use these AIsa endpoints:

- Data APIs: `https://api.aisa.one/apis/v1/...`
- LLM gateway: `https://api.aisa.one/v1/chat/completions`

Never print or commit API keys. If the key is missing, ask the user to set `AISA_API_KEY`.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The LLM gateway endpoint is another explicit external transmission path and may receive crawled page summaries, seed topics, keyword lists, and competitive analysis data. In the context of an agent skill, this can expose proprietary marketing strategy or customer domain information to an external processor if not tightly scoped and consented.

Content

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

md
Use these AIsa endpoints:

- Data APIs: `https://api.aisa.one/apis/v1/...`
- LLM gateway: `https://api.aisa.one/v1/chat/completions`

Never print or commit API keys. If the key is missing, ask the user to set `AISA_API_KEY`.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Defaulting geography and language to United States and English without explicit user opt-in can cause the skill to process data under incorrect assumptions and query third-party services using inferred market attributes. While this is more of a privacy, consent, and integrity issue than a direct exploit, it can still lead to unintended external processing and misleading outputs.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The concrete curl example at this line operationalizes outbound transmission and use of the AISA_API_KEY, making the egress path directly actionable. Although there is no obvious exfiltration of local secrets besides the intended API key, the example lowers friction for sending potentially sensitive user and crawl-derived data to an external service.

Content

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

Recommended request pattern:

bash
curl -sS "https://api.aisa.one/v1/chat/completions" \
  -H "Authorization: Bearer $AISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The concrete curl example at this line operationalizes outbound transmission and use of the AISA_API_KEY, making the egress path directly actionable. Although there is no obvious exfiltration of local secrets besides the intended API key, the example lowers friction for sending potentially sensitive user and crawl-derived data to an external service.

Content

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

Recommended request pattern:

bash
curl -sS "https://api.aisa.one/v1/chat/completions" \
  -H "Authorization: Bearer $AISA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/aisa-api-map.md (reported line 21)May include surrounding context.

md
API_BASE = "https://api.aisa.one"
CHAT_ENDPOINT = "https://api.aisa.one/v1/chat/completions"


def ssl_context() -> ssl.SSLContext:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/aisa-api-map.md (reported line 22)May include surrounding context.

md
API_BASE = "https://api.aisa.one"
CHAT_ENDPOINT = "https://api.aisa.one/v1/chat/completions"


def ssl_context() -> ssl.SSLContext:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/aisa-api-map.md (reported line 120)May include surrounding context.

md
API_BASE = "https://api.aisa.one"
CHAT_ENDPOINT = "https://api.aisa.one/v1/chat/completions"


def ssl_context() -> ssl.SSLContext:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/aisa_client.py (reported line 17)May include surrounding context.

python
API_BASE = "https://api.aisa.one"
CHAT_ENDPOINT = "https://api.aisa.one/v1/chat/completions"


def ssl_context() -> ssl.SSLContext:

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code performs outbound HTTP requests to user-supplied targets and retrieves page contents, but the script provides no confirmation prompt, visible notice, or comment warning the user that network access and content collection will occur. In a code file, network calls that transmit system-identifying data such as the user agent should include some form of disclosure unless clearly warned elsewhere.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script creates parent directories and writes JSON output to the provided path, which is a file-system modifying operation. Although the --out flag implies output behavior, there is no explicit warning in help text, comments, or runtime messaging that files and directories may be created or overwritten.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.