Back to skill

Security audit

Google Analytics MCP

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent Google Analytics read-only purpose, but its helper runs unpinned external code with service-account credentials and contains unsafe path handling.

Review before installing. Use only with trusted, isolated workspaces; keep credentials out of source control; restrict service accounts to the minimum GA property Viewer role; rotate keys if exposed; and prefer pinned, reviewed dependency versions or a locked local install before running the helper.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T08 · Insecure Dependencies

Error
Location
scripts/ga.sh:35
Finding
Unpinned Packages Are Retrieved and Executed with Access to GA Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ga.sh:35-42` **Related Documentation**: `SKILL.md:12-18`, `SKILL.md:51-55`, `references/setup.md:7` **Vulnerability Type**: Unpinned runtime dependency execution **Risk Level**: High ### Vulnerable Code ```bash # --- Call via mcporter --- exec npx --yes mcporter call \ --stdio uvx \ --stdio-arg analytics-mcp \ "${ENV_FLAGS[@]}" \ "analytics-mcp.$TOOL" \ "$@" ``` The documentation also recommends installation without a pinned version: ```bash mcporter — npm i -g mcporter ``` ### Technical Analysis The script invokes both `mcporter` and `analytics-mcp` by package name without pinning an audited version or verifying package integrity: - `npx --yes mcporter` can retrieve and immediately execute the current registry version of `mcporter` when it is not already available locally. - `uvx analytics-mcp` similarly resolves and executes an unpinned Python package. - No lockfile, package hash, exact version, or trusted artifact verification is present. The invoked MCP process receives `GOOGLE_APPLICATION_CREDENTIALS`, which identifies the service-account private-key file. Because the downloaded code executes with the caller's operating-system permissions, it can read that key file whenever filesystem permissions allow it. This is a supply-chain vulnerability rather than evidence that the current upstream packages are malicious. The effective executable code may nevertheless change after this Skill has been reviewed. ### Attack Path 1. An attacker compromises the registry account, release process, or upstream package for `mcporter` or `analytics-mcp`. 2. The attacker publishes a malicious version under the package name used by the script. 3. A user invokes `scripts/ga.sh` on a system where the relevant package is not securely pinned and cached. 4. `npx --yes` or `uvx` retrieves and executes the malicious release without an interactive approval step. 5. The malicious process reads `GOOGLE ...[truncated 847 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin exact audited versions of both dependencies rather than resolving their latest releases: - Invoke an exact `mcporter` version. - Invoke an exact `analytics-mcp` version. 2. Avoid downloading executable dependencies during each Skill invocation. Install verified artifacts during a controlled deployment phase. 3. Use lockfiles and integrity hashes for npm and Python artifacts. 4. Configure package managers to use approved registries and verify package provenance or signatures where supported. 5. Run the MCP process in a restricted environment that can access only the required credential file and necessary network endpoints. 6. Use a short-lived credential mechanism, such as workload identity federation, instead of a long-lived JSON private key where the deployment environment supports it. 7. Document and periodically review the exact approved package versions before upgrades. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ga.sh:27
Finding
Workspace Path Is Interpolated Directly into Executable Python Source<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ga.sh:25-28` **Vulnerability Type**: Python code injection through an attacker-influenced filesystem path **Risk Level**: High ### Vulnerable Code ```bash # --- Load optional config --- PROJECT_ID="" if [[ -f "$CONFIG_FILE" ]]; then PROJECT_ID="$(python3 -c "import json,sys; d=json.load(open('$CONFIG_FILE')); print(d.get('projectId',''))" 2>/dev/null || true)" fi ``` `CONFIG_FILE` is derived from the first command-line argument: ```bash WORKSPACE="${1:?Usage: ga.sh <workspace_dir> <tool> [args...]}" CREDS_DIR="$WORKSPACE/credentials" CREDS_FILE="$CREDS_DIR/ga-service-account.json" CONFIG_FILE="$CREDS_DIR/ga-config.json" ``` ### Technical Analysis Although shell expansion of `CONFIG_FILE` occurs inside double quotes, the expanded value is inserted into Python source inside a single-quoted Python string: ```python open('$CONFIG_FILE') ``` A workspace path containing a single quote and valid Python syntax can terminate the Python string and modify the program supplied to `python3 -c`. Shell quoting does not protect data after it has been embedded into another language's source code. The preceding `[[ -f "$CONFIG_FILE" ]]` check makes exploitation less convenient because a file must exist at the crafted path, but it does not sanitize the path. An attacker who can create or induce use of such a workspace directory can satisfy this condition. The `2>/dev/null || true` suffix suppresses Python errors and may make attempted exploitation or malformed configuration less visible. ### Attack Path 1. An attacker creates a workspace directory whose name contains quote characters and Python syntax, or convinces the Agent to invoke the script with such a path. 2. The attacker creates the expected `credentials/ga-config.json` and `ga-service-account.json` files under that directory so the file checks succeed. 3. The script expands the crafted path directly inside the `python3 -c` source string. 4. The ...[truncated 902 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Never interpolate a filesystem path into executable Python source. Pass it as a positional argument instead: ```bash PROJECT_ID="$( python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as config_file: print(json.load(config_file).get("projectId", "")) ' "$CONFIG_FILE" )" ``` Additional hardening should include: 1. Validate that the workspace path resolves beneath an approved workspace root. 2. Canonicalize the path before use and reject symlink escapes. 3. Report malformed JSON explicitly rather than suppressing every Python error with `2>/dev/null || true`. 4. Validate that `projectId` is a string matching the expected Google Cloud project-ID syntax. 5. Add automated tests using paths containing spaces, quotes, newlines, and shell metacharacters. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/ga.sh:8
Finding
Caller Can Select Arbitrary Workspace Credential Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ga.sh:8-12` **Related Documentation**: `SKILL.md:37-43`, `references/setup.md:61-68` **Vulnerability Type**: Missing workspace authorization and credential-boundary enforcement **Risk Level**: Medium ### Vulnerable Code ```bash WORKSPACE="${1:?Usage: ga.sh <workspace_dir> <tool> [args...]}" TOOL="${2:?Missing tool name}" shift 2 CREDS_DIR="$WORKSPACE/credentials" CREDS_FILE="$CREDS_DIR/ga-service-account.json" CONFIG_FILE="$CREDS_DIR/ga-config.json" ``` The resulting credential path is passed to the MCP process: ```bash ENV_FLAGS=(--env "GOOGLE_APPLICATION_CREDENTIALS=$CREDS_FILE") ``` The setup documentation identifies predictable workspace credential locations: ```text ~/.openclaw/workspace-relayter/credentials/ga-service-account.json → RELAYTO GA ~/.openclaw/workspace-nickta/credentials/ga-service-account.json → Ticket-Alerts GA ~/.openclaw/workspace/credentials/ga-service-account.json → Main / personal GA ``` ### Technical Analysis The script accepts the workspace directory entirely from its first command-line argument. It checks whether a credential file exists but does not establish that: - The requested workspace belongs to the invoking Agent. - The path is beneath an approved workspace root. - The path corresponds to a configured Agent-to-workspace identity mapping. - The resolved credential file is not reached through a symbolic-link escape. The documentation states that each Agent sees only its own GA data, but that isolation is not enforced by the helper. It depends entirely on external operating-system permissions or runtime controls that are not implemented in this project. Accessing a workspace-specific credential is necessary for the declared GA functionality. The excess privilege arises from allowing the caller to select any readable workspace rather than deriving the authorized workspace from trusted context. ### Attack Path 1. An Agent identifies anot ...[truncated 1223 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not accept an unrestricted workspace path from the caller. Derive it from trusted Agent runtime identity or configuration. 2. Maintain an explicit mapping between each Agent identity and its authorized workspace. 3. Canonicalize the workspace and credential paths with `realpath`, then verify that they remain beneath the authorized workspace root. 4. Reject symbolic links for credential directories and files where possible. 5. Apply restrictive filesystem permissions: - Workspace credential directories should be accessible only to their owning Agent or operating-system identity. - Service-account JSON files should normally use mode `0600`. 6. Prefer separate operating-system identities or sandbox boundaries for separate tenants instead of relying solely on application checks. 7. Grant the service account Viewer access only to the specific GA property required. Avoid account-level access unless listing every property in the account is necessary. 8. Replace long-lived JSON keys with workload identity federation or another short-lived authentication method where available. 9. Avoid publishing real tenant or workspace names in reusable documentation; use clearly fictional placeholders. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs users to store and use a Google service account JSON key from the workspace without any explicit guidance on protecting, restricting, or excluding that credential from version control and logs. Service account keys are highly sensitive bearer secrets; if they are exposed through the workspace, shell history, backups, or repository commits, an attacker could use them to access Google Analytics data and potentially other resources granted to that account.

Session Persistence

Medium
Category
Rogue Agent
Content
- [Google Analytics Admin API](https://console.cloud.google.com/apis/library/analyticsadmin.googleapis.com)
- [Google Analytics Data API](https://console.cloud.google.com/apis/library/analyticsdata.googleapis.com)

## Step 2 — Create a service account

1. Go to [GCP IAM → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)
2. Create a new service account (e.g. `openclaw-ga-reader`)
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
The script executes `mcporter` via `npx --yes` without pinning an exact package version or integrity-verified source, so each invocation may fetch whatever package version is currently published. If the npm package is compromised, typosquatted, or a malicious update is released, arbitrary code could run in the user's environment with access to the Google Analytics service account credentials referenced by `GOOGLE_APPLICATION_CREDENTIALS`.

Static analysis

No suspicious patterns detected.