Back to skill

Security audit

GA4 Analytics Toolkit

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its analytics and SEO purpose, but it needs Review because it can request URL removal from Google indexing and its exported storage helpers can read or write outside the intended results folder.

Install only if you are comfortable granting a Google service account access to the specific GA4/Search Console properties involved. Use least-privilege credentials, avoid granting indexing permissions unless needed, review any URL removal request manually, and treat the local results folder as sensitive because reports are saved by default. The storage helpers should be fixed or avoided for untrusted arguments before broader agent use.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/src/core/storage.ts:58
Finding
Exported Storage Utilities Permit Filesystem Path Traversal<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/core/storage.ts:58-126` **Additional Export Location**: `scripts/src/index.ts:22` **Vulnerability Type**: Path traversal and unrestricted file access **Risk Level**: Medium ### Vulnerable Code ```typescript export function saveResult<T>( data: T, category: string, operation: string, extraInfo?: string ): string { const settings = getSettings(); const categoryDir = join(settings.resultsDir, category); // Ensure category directory exists if (!existsSync(categoryDir)) { mkdirSync(categoryDir, { recursive: true }); } // Build filename const timestamp = getTimestamp(); const sanitizedOperation = sanitizeFilename(operation); const sanitizedExtra = extraInfo ? `__${sanitizeFilename(extraInfo)}` : ''; const filename = `${timestamp}__${sanitizedOperation}${sanitizedExtra}.json`; const filepath = join(categoryDir, filename); // Build wrapped result const result: SavedResult<T> = { metadata: { savedAt: new Date().toISOString(), category, operation, propertyId: settings.propertyId, ...(extraInfo && { extraInfo }), }, data, }; // Write to file writeFileSync(filepath, JSON.stringify(result, null, 2), 'utf-8'); return filepath; } export function loadResult<T = unknown>(filepath: string): SavedResult<T> | null { if (!existsSync(filepath)) { return null; } try { const content = readFileSync(filepath, 'utf-8'); return JSON.parse(content) as SavedResult<T>; } catch { return null; } } export function listResults(category: string, limit?: number): string[] { const settings = getSettings(); const categoryDir = join(settings.resultsDir, category); if (!existsSync(categoryDir)) { return []; } const files = readdirSync(categoryDir) .filter(f => f.endsWith('.json')) .map(f => join(categoryDir, f)) .sort((a, b) => { const nameA = a.split('/').pop() || ''; co ...[truncated 3412 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the result root to a canonical absolute path: ```typescript import { resolve, sep } from 'path'; const resultsRoot = resolve(getSettings().resultsDir); ``` 2. Allow only predefined categories rather than arbitrary directory names: ```typescript const ALLOWED_CATEGORIES = new Set([ 'reports', 'realtime', 'searchconsole', 'indexing', 'metadata', 'summaries', ]); function validateCategory(category: string): string { if (!ALLOWED_CATEGORIES.has(category)) { throw new Error('Invalid result category'); } return category; } ``` 3. Enforce containment after resolving every path: ```typescript function resolveWithinResults(relativePath: string): string { const target = resolve(resultsRoot, relativePath); if (target !== resultsRoot && !target.startsWith(resultsRoot + sep)) { throw new Error('Path escapes the results directory'); } return target; } ``` 4. Reject absolute paths, null bytes, and traversal components before filesystem access. 5. Replace `loadResult(filepath)` with an API accepting a validated category and generated result filename: ```typescript loadResult(category, filename); ``` 6. Validate that filenames match the expected timestamped result format and contain no path separators. 7. Apply containment checks to `saveResult()`, `loadResult()`, `listResults()`, and `getLatestResult()`. 8. Regenerate or patch the compiled files under `scripts/dist/` so runtime behavior matches the corrected TypeScript source. 9. Add tests covering absolute paths, `../` traversal, nested traversal, platform-specific separators, symbolic links, and valid category access. For stronger protection against symlink-based escapes, verify the canonical path with `realpath()` before reading or writing. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (42)

Known Vulnerable Dependency: protobufjs==7.5.4 — 12 advisory(ies): CVE-2026-44294 (protobuf.js: Denial of service from crafted field names in generated code); CVE-2026-44293 (protobuf.js: Code injection through bytes field defaults in generated toObject c); CVE-2026-44289 (protobuf.js: Denial of service through unbounded protobuf recursion) +9 more

Critical
Category
Supply Chain
Confidence
97% confidence
Finding
protobufjs 7.5.4 has numerous advisories, including denial-of-service and possible code-generation injection issues, making this the most serious dependency finding in the lockfile. Because Google analytics and API client libraries rely on protobuf serialization/parsing, a flaw in this layer could affect processing of structured remote data or any codegen-related workflows, increasing the blast radius across the skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description presents a comprehensive GA4/Search Console/Indexing toolkit centered on analytics and SEO reporting, but this code chunk is narrowly focused on indexing operations and URL inspection. The implemented behavior covers re-indexing requests, batch indexing, URL removal from index, and Search Console inspection details. The main mismatch is that most of the declared analytics capabilities are absent from this code, while the code includes URL removal, which is not explicitly declared. Although URL re-indexing and index inspection are accurately represented, the chunk’s actual scope is materially narrower and partially different from the declared broad analytics toolkit.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description is broader than what this code chunk supports. The code exposes only GA4 reporting-related functions and types. It includes no visible interfaces or functions for Search Console access, Indexing API operations, URL inspection, or real-time analytics. It also does not specifically expose bounce rate or comparative date-range report features. While several declared GA4 capabilities are consistent with the code—such as page views, traffic sources, demographics, conversions, and e-commerce revenue—the overall description materially overstates the implemented functionality in this chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description presents a multi-service toolkit spanning GA4, Search Console, and the Indexing API, with many analytics and indexing features. The supplied code chunk, however, only exposes Search Console-related interfaces and functions: parsing Search Console date ranges and retrieving search analytics by query, page, device, country, and search appearance. There is no evidence in this chunk of GA4 data access, demographics, real-time analytics, conversions, revenue metrics, or Indexing API operations like URL re-indexing or index inspection. This is a material description-to-code mismatch because the implementation shown is significantly narrower than the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This specific code is narrowly focused on Search Console analytics. It parses date ranges, calls the Search Console searchanalytics.query endpoint, and provides helpers for top queries, pages, device, country, and search appearance. It does not implement GA4 functionality, user demographics, traffic sources, real-time visitors, bounce rates, conversions, e-commerce revenue, or Indexing API actions like re-indexing URLs or inspecting index status. Since the declared description presents a much broader multi-API toolkit than what this code chunk actually does, the description does not accurately represent this supplied code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This is a clear mismatch. The declared description presents a feature-rich Google analytics and SEO toolkit, but the supplied code chunk is only a setup script that installs npm packages. While setup scripts can be supportive implementation details, this chunk alone does not substantiate the declared capabilities and its actual behavior is limited to environment preparation. There are no unrelated dangerous actions shown, but the described operational purpose is not reflected in the code provided.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents a broad analytics and SEO toolkit spanning GA4, Search Console, and Indexing API capabilities. However, this specific code chunk is narrowly focused on indexing operations: publish URL_UPDATED notifications, publish URL_DELETED notifications, batch indexing requests, and perform URL inspection through Search Console. That means the code behavior is only a subset of the declared purpose and does not support most of the described analytics capabilities in this chunk. Additionally, it performs URL removal from index, an action not explicitly called out in the description. This is a material description-behavior mismatch for the supplied code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code is narrowly focused on standard GA4 reports via client.runReport(). It supports traffic/page-view related analytics, traffic sources, demographics, bounce rate through traffic source metrics, conversions, and e-commerce revenue, which partially aligns with the description. However, the declared purpose presents a broader multi-service toolkit covering Search Console and Indexing API capabilities, as well as real-time and index inspection features, none of which appear in this code chunk. There is no evidence here of undeclared harmful behavior or access to inconsistent resources beyond GA4, but there is a material description-to-behavior mismatch because the description substantially overstates what this specific code actually does.

Missing User Warnings

High
Confidence
97% confidence
Finding
The API reference exposes a removeFromIndex capability that can request deletion of URLs from Google's index, but it provides no warning that this is a destructive SEO-affecting action. In an agent-operated environment, lack of friction or caution around this function increases the risk of accidental or unauthorized de-indexing that can materially reduce site visibility and disrupt business operations.

Credential Access

High
Category
Privilege Escalation
Content
*/
import { config } from 'dotenv';
import { join } from 'path';
// Load .env file from current working directory
config();
/**
 * Get current settings from environment variables
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
*/
import { config } from 'dotenv';
import { join } from 'path';
// Load .env file from current working directory
config();
/**
 * Get current settings from environment variables
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Known Vulnerable Dependency: @grpc/grpc-js==1.14.3 — 2 advisory(ies): CVE-2026-48068 (@grpc/grpc-js: A malformed request can cause a server crash); CVE-2026-48069 (@grpc/grpc-js: An incoming malformed compressed message can cause a client or se)

High
Category
Supply Chain
Confidence
94% confidence
Finding
The lockfile pins @grpc/grpc-js to 1.14.3, and the cited advisories describe malformed-message crash conditions. In this skill, the package is transitively pulled in by Google client libraries; while the skill is primarily an API client rather than a gRPC server, a vulnerable parsing path could still expose denial-of-service risk in client or service interactions if untrusted responses or compressed messages are processed.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
93% confidence
Finding
brace-expansion 2.0.2 has multiple DoS-style advisories involving pathological expansion inputs that can cause hangs or memory exhaustion. In this file it is a transitive dependency of tooling rather than core runtime analytics logic, which lowers exposure, but it remains a real risk if any user-influenced glob or pattern processing reaches it during execution or development workflows.

Known Vulnerable Dependency: form-data==2.5.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
89% confidence
Finding
form-data 2.5.5 is flagged for CRLF injection via unescaped multipart field names/filenames, which can enable request smuggling or malformed multipart requests when attacker-controlled values are used. This skill interacts with Google APIs and may construct outbound HTTP requests through dependency code, so any future use of user-supplied multipart fields would make the issue more dangerous.

Possible Typosquatting: 'gaxios' resembles popular package 'axios'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Possible Typosquatting: 'gaxios' resembles popular package 'axios'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares capabilities that require environment-variable access and outbound network access, but it does not explicitly declare any tool scope or permissions boundary. In an agent environment, this weakens reviewability and least-privilege enforcement, increasing the chance that a user or operator grants broader access than intended to a skill that handles sensitive analytics credentials and data.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
Using `npx tsx` without pinning a version allows resolution of whatever package version is current at execution time, creating a supply-chain risk. A compromised or incompatible upstream release could execute unintended code in a context that also has access to service-account credentials and network connectivity.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly states that analytics outputs are automatically written to local JSON and markdown files, but it does not warn users that traffic, query, demographic, and revenue-related data may persist on disk. In shared workspaces or long-lived agent environments, this can create unintended retention and secondary exposure of sensitive business intelligence or personal data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation states that report results are saved to JSON by default, but it does not warn users that analytics, search, realtime, and indexing outputs may contain sensitive operational or business data that will persist on local disk. In an agent skill context, silent persistence increases the chance of unintended data retention, later exposure through logs/artifacts, or reuse by other tools without the user's awareness.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The code exposes a destructive capability to submit URL_DELETED notifications to Google's Indexing API, while the skill metadata only advertises re-indexing and index inspection. This mismatch increases the chance that users or downstream agents invoke harmful functionality unexpectedly, causing legitimate pages to be deindexed and damaging site visibility.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The removeFromIndex function performs an irreversible or high-impact SEO action without any built-in confirmation, safety interlock, or warning path. In an agentic context, this makes accidental or prompt-induced destructive actions more likely, especially because the function accepts arbitrary URLs and immediately publishes the deletion request.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest describes a toolkit for querying GA4, Search Console, and the Indexing API, but this file documents functionality for persisting results to local storage. Local result archival is not mentioned in the skill description and is a distinct behavior beyond the stated analytics/querying scope.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The write operation saves result data to disk unconditionally via writeFileSync, with no user-facing warning at the point of collection or storage. For an analytics skill, silently storing reports can expose sensitive operational and SEO data to other local users, backups, or later compromise, especially when users may reasonably expect ephemeral processing.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The module doc comment describes the entry point as a 'GA4 Analytics Toolkit' and a 'Simple interface for Google Analytics 4 data analysis,' which suggests a narrower GA4-focused analytics scope. However, the exports include Search Console, Indexing API, bulk lookup, and storage helpers, which go beyond that stated behavior even though some align with the broader manifest.

Static analysis

No suspicious patterns detected.