Back to skill

Security audit

SEO Audit Suite

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real SEO/GEO audit skill, but it needs Review because crafted inputs can execute code or write outside the intended workspace, and external API/credential use is under-scoped.

Do not run this skill on untrusted URLs, internal services, staging hosts, or client-confidential keyword sets until the input handling is fixed. Use a sandboxed environment, restrict network egress, use dedicated low-privilege API keys, avoid Owner or Full service-account permissions, and review or delete the persisted audit workspace after use.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/technical-audit.sh:14
Finding
Arbitrary Python Code Execution Through URL Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/technical-audit.sh:14-19` **Vulnerability Type**: User-controlled input embedded in executable Python source **Risk Level**: Critical ### Vulnerable Code ```bash python3 -c " import requests, json, sys, time from urllib.parse import urlparse from bs4 import BeautifulSoup url = '$URL' deep = '$DEEP' ``` The same unsafe source-code interpolation pattern also appears in: - `scripts/geo-audit.sh:11-16` - `scripts/audit-page.sh:17` - `scripts/audit-geo.sh:9` - `scripts/audit-technical.sh:9` - `scripts/analyze-competitor.sh:29-41` - `scripts/track-keywords.sh:72-83` - `scripts/generate-report.sh:28-35` ### Technical Analysis The shell argument stored in `URL` is inserted directly into a Python program passed to `python3 -c`. Shell quoting does not make this safe because the substitution occurs before Python parses the generated source. An input containing a single quote can terminate the intended Python string. The attacker can then append arbitrary Python statements. Those statements can invoke operating-system commands, read environment variables, inspect local files, alter audit results, or make additional network requests. Unquoted heredocs in the other affected scripts similarly substitute values such as `KEYWORD`, `COMPETITORS`, `SITE`, and `TYPE` into executable Python source. ### Attack Path 1. An attacker persuades the Agent or user to audit a crafted URL, site, keyword, competitor value, or report type. 2. The shell stores the value without validating it as a safe URL or identifier. 3. The value is interpolated into Python source code. 4. A quote in the value closes the intended string literal. 5. Injected Python statements execute with the permissions and environment of the Skill process. 6. The injected code can access workspace files, environment-based API keys, and any other resources available to the Agent account. ### Impact Assessment Successful exploitation provides arbitra ...[truncated 554 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never construct Python source code from shell arguments. - Pass the URL as a positional argument: ```bash python3 "$SCRIPT_DIR/technical_audit.py" --url "$URL" ``` - Parse it in Python with `argparse`: ```python parser.add_argument("--url", required=True) args = parser.parse_args() url = args.url ``` - If a heredoc is unavoidable, use a single-quoted delimiter and retrieve values from the environment: ```bash export AUDIT_URL="$URL" python3 <<'PYEOF' import os url = os.environ["AUDIT_URL"] PYEOF ``` - Apply the same correction to every affected URL, keyword, competitor, site, type, and output-path interpolation. - Validate URLs, hostnames, report types, and numeric arguments independently after safely transporting them into Python. - Add regression tests containing quotes, backslashes, newlines, command substitutions, and traversal sequences. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audit-page.sh:39
Finding
Server-Side Request Forgery Through Unrestricted Audit URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit-page.sh:39-46` **Vulnerability Type**: Unrestricted network request and redirect handling **Risk Level**: High ### Vulnerable Code ```python url = sys.argv[1] if len(sys.argv) > 1 else "" if not url: url = os.environ["AUDIT_URL"] headers = {"User-Agent": "ReighlanSEOBot/1.0"} try: resp = requests.get(url, headers=headers, timeout=15, allow_redirects=True) except Exception as e: print(f"❌ Failed to fetch {url}: {e}") sys.exit(1) ``` Related unrestricted requests appear in: - `scripts/seo_auditor.py:20-27` - `scripts/site_crawler.py:52` - `scripts/audit-technical.sh:27-36` - `scripts/audit-technical.sh:86-90` - `scripts/technical-audit.sh:30` - `scripts/geo-audit.sh` through `seo_auditor.fetch_page` ### Technical Analysis The Skill accepts an arbitrary URL and sends a request without validating: - The scheme. - The resolved IP address. - Whether the destination is loopback, private, link-local, reserved, or multicast. - The destination port. - Redirect destinations. - DNS changes between validation and connection. Because redirects are enabled, a URL that initially resolves to a public host can redirect the request to a private address. The technical audit additionally reads a `Sitemap:` URL from remote `robots.txt` content and requests it without constraining it to the audited public host. Public-site SEO analysis requires outbound access to the requested site, but it does not require unrestricted access to localhost, private networks, cloud metadata services, or arbitrary internal ports. The current implementation therefore exceeds minimum necessary network privileges. ### Attack Path 1. An attacker supplies a URL pointing directly to an internal destination, or controls a public page that redirects to one. 2. The Skill accepts the URL without checking the resolved address. 3. `requests.get()` sends the request from the Agent host. 4. The destination receives a re ...[truncated 1028 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Permit only explicitly supported `http` and `https` URLs. - Resolve every hostname before connecting. - Reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 addresses. - Explicitly block known metadata destinations. - Reject embedded credentials and unexpected ports unless they are required and allowlisted. - Disable automatic redirects and validate every `Location` target before following it. - Re-resolve and revalidate the hostname immediately before each connection to reduce DNS rebinding risk. - Apply the same checks to URLs obtained from redirects, page links, canonical tags, and `robots.txt` sitemap directives. - Consider restricting sitemap requests to the audited registrable domain. - Enforce outbound firewall or sandbox rules as defense in depth. - Limit response sizes and crawl counts to prevent resource exhaustion. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/track-keywords.sh:18
Finding
Workspace Path Traversal Through Unvalidated Site Identifiers<![CDATA[ ## Vulnerability Details **File Location**: `scripts/track-keywords.sh:18-22` **Vulnerability Type**: Path traversal and unauthorized filesystem access **Risk Level**: High ### Vulnerable Code ```bash [ -z "$SITE" ] && { echo "Usage: track-keywords.sh --site <domain> --keywords \"kw1,kw2\" | --report"; exit 1; } BASE_DIR="${SEO_AUDIT_DIR:-$HOME/.openclaw/workspace/seo-audit}" KW_DIR="$BASE_DIR/sites/$SITE/keywords" mkdir -p "$KW_DIR" ``` Related unsafe path construction appears in: - `scripts/generate-report.sh:19-26` - `scripts/audit-page.sh:17-22` - `scripts/audit-geo.sh:9-14` - `scripts/audit-technical.sh:9-14` ### Technical Analysis `SITE` is accepted as an arbitrary string and directly inserted into a filesystem path. There is no hostname validation, path canonicalization, or verification that the resulting path remains below `BASE_DIR`. A value containing path separators or `..` components can escape the intended `sites` directory. Subsequent operations create directories, read tracking files, or write JSON output under the resolved path. Report generation has a similar issue because both `SITE` and `TYPE` become parts of directory and report filenames. It can therefore read audit data from an unintended directory or write a Markdown report outside the expected report naming boundary when suitable parent directories exist. ### Attack Path 1. An attacker supplies a crafted `--site` or `--type` value containing traversal components. 2. The script concatenates that value with the configured workspace path. 3. The operating system resolves the traversal components. 4. `mkdir`, Python file reads, or report/tracking writes operate on the unintended location. 5. Files accessible to the Skill account may be created, overwritten, or consumed as audit input. The source-code interpolation issue in these scripts can provide a more direct execution path, but path traversal remains independently exploitable in the filesystem construction. ### Im ...[truncated 583 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require `SITE` to be a normalized hostname rather than an arbitrary path component. - Reject `/`, `\`, `..`, control characters, null bytes, and unsupported hostname characters. - Restrict `TYPE` to a fixed enum such as `full`, `summary`, `geo`, or `monthly`. - Construct paths with `pathlib.Path`. - Resolve the candidate path and confirm that it remains under the intended base: ```python base = Path(base_dir).resolve() candidate = (base / "sites" / validated_site / "keywords").resolve() if base not in candidate.parents: raise ValueError("Path escapes audit workspace") ``` - Use application-generated opaque directory identifiers when practical. - Open new output files with exclusive creation where overwriting is unnecessary. - Apply restrictive permissions to workspace directories and sensitive output. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/audit-technical.sh:113
Finding
PageSpeed API Key Exposed in Request Query String<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audit-technical.sh:113-119` **Vulnerability Type**: Sensitive credential included in a URL **Risk Level**: Medium ### Vulnerable Code ```python # --- PageSpeed Insights (if API key available) --- pagespeed_key = os.environ.get("PAGESPEED_API_KEY", "") cwv = {} if pagespeed_key: try: ps_url = f"https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url={url}&key={pagespeed_key}&strategy=mobile" ps_resp = requests.get(ps_url, timeout=30) ``` The duplicate implementation in `scripts/technical-audit.sh:71-78` also includes `PAGESPEED_API_KEY` in the query string. ### Technical Analysis The key is intentionally transmitted to Google's official HTTPS PageSpeed endpoint as part of the declared PageSpeed integration. There is no evidence that it is sent to an unrelated attacker-controlled service. However, placing credentials in query strings increases exposure because complete URLs may be retained by: - HTTP client debugging and exception logs. - Forward proxies and monitoring infrastructure. - Network observability systems. - Error-reporting tools. - Shell or application diagnostics that print prepared requests. HTTPS protects the URL in transit from passive network observers, but it does not prevent disclosure through endpoint, proxy, or local application logs. The flagged request in `scripts/audit-page.sh` does not attach an API key or local sensitive information; it sends only the requested web request and User-Agent. ### Attack Path 1. The user configures `PAGESPEED_API_KEY` in the environment. 2. The technical audit constructs a complete URL containing the key. 3. The request passes through the local HTTP stack and any configured proxy or monitoring layer. 4. A component records the full request URL. 5. A party with access to those logs obtains the key and can consume its authorized quota. ### Impact Assessment Potential impact includes: - Unauthorized PageSpeed ...[truncated 371 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use an authentication header instead of a query parameter if the API supports it. - If the PageSpeed API requires a query parameter, ensure request URLs are never logged. - Redact `key` parameters from exceptions, diagnostics, proxy logs, and monitoring systems. - Restrict the Google API key to the PageSpeed Insights API. - Apply appropriate source restrictions where supported. - Configure strict quotas and usage alerts. - Use a dedicated key for this Skill rather than sharing a broadly privileged project key. - Rotate the key if it may already have appeared in logs. - Keep credentials in environment variables or a protected secret store, not in workspace configuration files. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:11
Finding
Unpinned Runtime Dependencies Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:11-14` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash ### Dependencies ```bash pip3 install requests beautifulsoup4 lxml ``` ``` ### Technical Analysis The setup instructions install three third-party packages without exact versions or cryptographic hashes. Package resolution therefore depends on the package index and package versions available at installation time. Although the specified package names are established projects and no malicious dependency is directly identified, the installation is not reproducible and lacks integrity pinning. A compromised package index, compromised upstream release, unsafe alternate index configuration, or future malicious release could introduce executable code into the Skill environment. Python packages execute code when imported, and some packages may execute build-system code during installation. A dependency compromise can therefore affect both setup and runtime. ### Attack Path 1. A user follows the documented setup command. 2. `pip` queries its configured package indexes. 3. It selects the latest versions satisfying the unconstrained names. 4. A compromised package, release, mirror, or transitive dependency is downloaded. 5. Malicious code executes during installation, build, or later import by the audit scripts. 6. The code receives the permissions and environment of the user running the Skill. ### Impact Assessment A successful supply-chain compromise could provide: - Code execution during dependency installation or import. - Access to the Agent workspace. - Access to environment-based API keys. - Modification of audit results. - Unauthorized network communication. - Compromise of other Python applications sharing the same environment. The finding represents unsafe dependency management rather than evidence that the currently named packages are malicious. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Create a reviewed dependency lock file with exact versions. - Include hashes and install with hash verification: ```bash pip install --require-hashes -r requirements.txt ``` - Pin transitive dependencies as well as direct dependencies. - Use a trusted and explicitly configured package index. - Install dependencies in a dedicated virtual environment. - Run dependency vulnerability and provenance scanning in CI. - Review and update pinned versions through a controlled process. - Avoid installing packages with elevated privileges. - Consider reproducible wheel-based deployment from an internally reviewed artifact repository. ]]>
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 (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code materially underdelivers relative to the declared description. Its actual purpose is narrowly a GEO audit for a single URL, not a comprehensive SEO/GEO toolkit. It does perform one declared area—GEO readiness evaluation for AI search engines—and saves a local report, but there is no evidence in this chunk of competitor analysis, keyword tracking, broad technical SEO auditing, PDF generation, or time-series monitoring. Resource access is broadly consistent with auditing a website (HTTP fetch of the target URL and local file write), and there are no suspicious unrelated capabilities. However, the declared description significantly overstates the functionality represented by this code chunk, so this should be flagged as a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code does match one declared area: on-page SEO auditing of a single page. However, the declared description presents a much broader and more comprehensive toolkit, including competitor analysis, keyword tracking, GEO scoring, PDF generation, and ongoing monitoring. None of those capabilities appear in this code chunk. The script only fetches one URL, evaluates basic on-page signals, writes a JSON file, and prints a terminal summary. While storing audit files under timestamped paths could support later historical review, the code itself does not implement monitoring or trend analysis. Therefore the supplied code chunk materially underdelivers relative to the declared purpose, so this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code is related to SEO auditing, so it partially matches the broad domain, but the declared description substantially overstates the implemented functionality in this supplied chunk. The actual script only performs a technical SEO audit and optional PageSpeed lookup for a single URL, storing results locally as JSON. There is no evidence here of competitor analysis, keyword ranking tracking, GEO readiness/scoring, PDF generation, or broader comprehensive toolkit behavior. Because the declared purpose presents a much wider set of core capabilities than the code actually provides, this is a meaningful description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code chunk is a report generator script, not a comprehensive SEO/GEO audit toolkit. It reads existing audit JSON files from a local workspace and writes a .md report. While this partially aligns with the declared reporting/audit context, several prominently declared capabilities are absent in the supplied code: no crawling or audit execution, no competitor analysis, no keyword tracking, no time-series monitoring logic, and no PDF generation. The actual primary purpose of this code is narrower than advertised, so the description does not accurately represent what this code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code substantially matches part of the description: it performs on-page SEO checks, technical SEO checks, content/schema/social checks, and a basic GEO readiness score for a supplied URL. However, the declared description presents a broader 'comprehensive' toolkit with competitor analysis, keyword tracking, PDF report generation, client reporting, and monitoring over time. None of those capabilities appear in this code chunk. The code only fetches a single page via HTTP, parses HTML, computes scores/issues, prints a console summary, and optionally writes JSON output. Because several headline capabilities in the declared purpose are absent and the actual scope is materially narrower, this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The provided code chunk only demonstrates crawling a single site and invoking an SEO audit for each page, then printing and saving a JSON summary. That partially matches the declared on-page/technical SEO audit functionality. However, the declared description presents a much broader toolkit with competitor analysis, keyword ranking tracking, GEO readiness scoring, PDF report generation, and longitudinal monitoring, none of which are evidenced in this code chunk. There are no suspicious undeclared capabilities beyond normal web fetching and file output for the audit task, but the description materially overstates the implemented functionality shown here.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a broad, comprehensive SEO and GEO toolkit with multiple major capabilities, including competitor analysis, keyword tracking, GEO readiness scoring, PDF report generation, and longitudinal monitoring. The supplied code chunk does not implement those features. Instead, it performs a narrow technical audit: HTTP status/load timing, HTTPS/redirect checks, robots.txt inspection including AI bot blocking, sitemap presence, and optional PageSpeed API scoring. While this does fit one subset of the declared purpose—technical SEO checks—it materially underdelivers relative to the comprehensive toolkit description. There is no evidence in this chunk of competitor analysis, keyword rank tracking, GEO scoring logic, PDF generation, or time-series monitoring. Therefore the description does not accurately represent what this code chunk actually does.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The script embeds the user-controlled URL directly into a Python string inside a `python3 -c` one-liner. An attacker can supply a crafted URL containing quotes and Python syntax to break out of the string and execute arbitrary Python code on the host, which goes far beyond the intended GEO audit behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises and documents capabilities that use network access, environment variables, and persistent file read/write, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, missing scope declarations can cause the skill to be invoked with broader privileges than users expect, increasing the chance of unintended data access, outbound requests, or filesystem modification.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The skill performs outbound requests to websites and third-party APIs/search services, but the description does not warn users that target URLs, keywords, and potentially business-sensitive audit data may be transmitted externally. This can create privacy, confidentiality, and compliance risks, especially for client work or internal domains.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The guide explicitly instructs users to create and download a service-account JSON key, which is a highly sensitive credential that can grant API access if exposed. Omitting handling, storage, rotation, and least-privilege guidance increases the chance that users will save, share, commit, or distribute the key insecurely.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script accesses BRAVE_API_KEY from the environment and uses it to authenticate outbound requests, but there is no warning or disclosure that credential material will be consumed for network activity. The surrounding output only reports failure states and does not clearly tell the user that an environment-provided API key will be used.

External Transmission

Medium
Category
Data Exfiltration
Content
if brave_key:
        try:
            search_resp = requests.get(
                "https://api.search.brave.com/res/v1/web/search",
                headers={"X-Subscription-Token": brave_key},
                params={"q": keyword, "count": 10},
                timeout=10
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if brave_key:
        try:
            search_resp = requests.get(
                "https://api.search.brave.com/res/v1/web/search",
                headers={"X-Subscription-Token": brave_key},
                params={"q": keyword, "count": 10},
                timeout=10
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script performs an outbound HTTP request to a user-supplied URL and writes a detailed report to local storage without any explicit user-facing disclosure or confirmation. In an agent setting, hidden network access and filesystem writes can surprise users, expose internal URLs or browsing targets, and leave potentially sensitive audit artifacts on disk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script automatically sends the user-supplied target URL and a locally available PAGESPEED_API_KEY to Google's external PageSpeed API without any user-facing notice, consent, or opt-in. This can unintentionally disclose sensitive audit targets, internal/staging URLs, or usage tied to the operator's API key to a third party, which is especially risky in an agent skill that may be run on arbitrary user-provided URLs.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
User-supplied keywords are sent to Brave's external search API, which can expose potentially sensitive client strategy, campaign terms, or unreleased business priorities to a third party. While this is necessary for the feature to work, the script does not provide explicit disclosure or consent at the point of transmission, creating a privacy and data-handling risk.

Description-Behavior Mismatch

Low
Confidence
93% confidence
Finding
The manifest description explicitly says the skill 'Generates PDF reports,' suggesting PDF output is part of the implemented behavior. However, the report generation section documents only Markdown and HTML output formats, creating a direct mismatch between the advertised capability and the described behavior in the skill file.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The skill stores audit data and configuration, including API-key-related settings, in a persistent workspace but does not prominently warn users about retention or sensitivity. In multi-tenant or shared-agent environments, silent persistence can expose site data, reports, or configuration details to later sessions or other components with filesystem access.

Missing User Warnings

Low
Confidence
88% confidence
Finding
Telling users to set an environment variable pointing to the service-account key is a common configuration pattern, but without any warning it may encourage insecure handling of credential paths and associated secrets in shell history, process environments, shared profiles, or deployment configs. In combination with the previous step, it normalizes credential use without teaching safe storage and access controls.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The code creates and writes a report file under a workspace directory, which is a file-write operation covered by the warning requirement for code files. Although the script prints the report path after writing, there is no warning, confirmation, or pre-action disclosure before the write occurs, and the write behavior is not explained in comments beyond the high-level audit description.

Context-Inappropriate Capability

Low
Confidence
85% confidence
Finding
The manifest describes technical SEO checks and site performance monitoring, which justifies fetching the target site and related SEO resources like robots.txt and sitemap.xml. However, this script additionally inspects the local environment for PAGESPEED_API_KEY and uses it to access Google's PageSpeed Insights API, introducing credential use and dependency on a third-party service that is not explicitly stated in the skill description or this script's documentation.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script persists tracking results to a timestamped JSON file under the user's workspace, but the code provides no prior warning in usage text, comments, or prompts that keyword queries and ranking results will be stored locally. Because this is a file write involving user-provided keywords and site data, it meets the code-file warning criterion.