Back to skill

Security audit

Google Maps Reviews Scraper (Apify)

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it gives agents too much room to run arbitrary Apify actors with your token and collects reviewer profile data by default.

Review before installing. Use a limited Apify token if possible, set a budget limit, avoid passing the token on the command line, do not override --actor-id unless you intentionally want another actor, and set personalData=false unless reviewer profile fields are explicitly needed and appropriate to retain.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/google_maps_reviews_scraper_actor.py:128
Finding

Apify API Token Exposed Through URL Query Parameters and CLI Arguments

Content
View full analysis
str: token = explicit or os.getenv("APIFY_TOKEN", "") if not token: raise SkillError("Apify token missing. Pass --apify-token or set APIFY_TOKEN.") return token ``` ```python params: dict[str, str | int | float] = { "token": token, "timeout": timeout_sec, "clean": "true", } if budget_usd is not None: if budget_usd <= 0: raise SkillError("--budget-usd must be > 0.") params["maxTotalChargeUsd"] = budget_usd url = ( f"https://api.apify.com/v2/acts/{urllib.parse.quote(actor_id, safe='')}/run-sync-get-dataset-items" f"?{urllib.parse.urlencode(params)}" ) body = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url=url, data=body, headers={"Content-Type": "application/json"}, method="POST", ) ``` ```python parser.add_argument("--apify-token", help="Apify API token (fallback: APIFY_TOKEN env)") ``` ### Technical Analysis The Apify token is inserted into the request URL as the `token` query parameter. Although the request uses HTTPS, query strings may still be recorded by client diagnostics, HTTP infrastructure, monitoring products, reverse proxies, exception telemetry, or endpoint-side access logs. HTTPS protects the URL while it is in transit but does not prevent disclosure through local or server-side logging. The optional `--apify-token` argument creates an additional exposure channel. Command-line arguments can be retained in shell history and may be visible to other local users through process-inspection interfaces while the command is running. The network request itself is necessary for the declared Apify-backed functionality. However, placing the credential in the U ...[truncated 1062 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/google_maps_reviews_scraper_actor.py:125
Finding

User-Controlled Actor ID Permits Execution Beyond the Declared Scraper

Content
View full analysis
dict[str, Any]: params: dict[str, str | int | float] = { "token": token, "timeout": timeout_sec, "clean": "true", } if budget_usd is not None: if budget_usd <= 0: raise SkillError("--budget-usd must be > 0.") params["maxTotalChargeUsd"] = budget_usd url = ( f"https://api.apify.com/v2/acts/{urllib.parse.quote(actor_id, safe='')}/run-sync-get-dataset-items" f"?{urllib.parse.urlencode(params)}" ) ``` ```python parser.add_argument("--actor-id", default=DEFAULT_ACTOR_ID, help="Apify actor ID") ``` ```python result = run_actor(token, args.actor_id, payload, args.timeout_sec, args.budget_usd) ``` ### Technical Analysis The Skill declares a specific Google Maps reviews actor, `V2kIsQs3Ta9C9kkEt`, but exposes `--actor-id` and places its value directly into the authenticated Apify API endpoint. The custom `run` command also accepts an arbitrary JSON object. No allowlist or confirmation step ensures that the selected actor is the declared reviews scraper. The actor identifier is URL-encoded, so this is not a URL path-injection issue. The security problem is authorization scope: the script intentionally permits the caller—or an agent manipulated into constructing arguments—to use the victim's Apify token to invoke a different actor. Because Apify actors are remotely hosted programs, selecting another actor may change the processing destination, billing behavior, output, and treatment of the submitted payload. This exceeds the minimum privilege required to run the single actor advertised by the Skil ...[truncated 961 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
README.md:70
Finding

Unpinned Package Execution in Installation Instructions

Content
View full analysis
Remediation
View remediation

other

Warning
Location
scripts/google_maps_reviews_scraper_actor.py:81
Finding

Reviewer Personal Data Collection Enabled by Default

Content
View full analysis
dict[str, Any]: payload: dict[str, Any] = { "maxReviews": positive_int(args.max_reviews, "max-reviews"), "reviewsSort": args.reviews_sort, "reviewsOrigin": args.reviews_origin, "language": args.language, "personalData": not args.no_personal_data, } ``` ```json { "placeIds": [ "ChIJ8Q2WSpJZwokRQz-bYYgEskM" ], "maxReviews": 25, "reviewsSort": "newest", "reviewsOrigin": "google", "language": "en", "personalData": true } ``` The documented output may include reviewer profile fields such as reviewer ID, profile URL, name, review count, local-guide status, and photo URL. ### Technical Analysis Both custom payload normalization and quick-command construction enable `personalData` unless the caller explicitly disables it. The bundled sample payload also enables the option. This makes personal-data collection opt-out even when a user only requests review text, ratings, dates, or place metadata. The collected information is described as publicly exposed Google profile data, but aggregation and export still increase privacy risk. The script prints all returned rows as JSON, which can cause reviewer profile information to be copied into logs, agent transcripts, files, or downstream systems. The personal-data fields are part of the Skill's declared optional functionality, so their collection is not covert. Nevertheless, enabling them by default exceeds data-minimization requirements for ordinary review extraction. ### Attack Path 1. A user ...[truncated 823 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill declares required credentials and clearly instructs the agent to run a Python script and call an external Apify actor, but it does not declare any explicit tool scope such as allowed tools or permissions. This creates a governance gap: an agent may use environment access, file reads, and network operations without a machine-readable restriction boundary, increasing the chance of overbroad execution or misuse in environments that rely on manifest-level policy enforcement.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill explicitly promotes collection of reviewer profile data and personal data, but it does not give a clear user-facing privacy warning or require justification/minimization before collecting those fields. That can lead agents to gather personally related data by default or without informed user awareness, creating privacy, compliance, and data-handling risk even if the data is publicly visible.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The example payload sets personalData to true without any adjacent warning, consent check, or data-minimization guidance. Concrete examples strongly influence downstream agent behavior, so this makes privacy-sensitive collection more likely to be copied into real use without considering necessity or compliance requirements.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Line L20 states that personalData can include public reviewer profile fields when exposed by Google, but the document provides no warning about privacy impact, downstream handling, or need for user awareness. Because this is a markdown file and the behavior may affect personal data, the omission fits the missing user warnings category.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code sets the payload language default to "en", which forces a specific language/locale when the caller does not explicitly choose one. Under the stated policy, locale constraints should be user-selected or clearly justified rather than silently imposed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code sends the user-supplied scraping payload to the Apify API and includes an API token, which is a network operation that can transmit place identifiers, URLs, language settings, and personal-data-related options to a third-party service. While the CLI help describes parameters, there is no confirmation prompt, user-facing disclosure, or inline warning explaining that input data will be sent to Apify for processing.

Content

No source excerpt is available for this finding.

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/google_maps_reviews_scraper_actor.py (reported line 139)May include surrounding context.

python
params["maxTotalChargeUsd"] = budget_usd

    url = (
        f"https://api.apify.com/v2/acts/{urllib.parse.quote(actor_id, safe='')}/run-sync-get-dataset-items"
        f"?{urllib.parse.urlencode(params)}"
    )
    body = json.dumps(payload).encode("utf-8")

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The --language argument is initialized with default="en", meaning the tool will use English even when the user does not request that locale. This is a natural-language policy issue because it forces a language choice rather than offering neutral behavior or explicit opt-in.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

Scanner excerpt · LICENSE (reported line 12)May include surrounding context.

text
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

Line L19 specifies a language parameter controlling returned labels, URLs, and relative dates, but the document does not indicate that language selection should be based on user preference or explicit opt-in. This can be a natural-language policy issue when a skill sets locale behavior without documenting user choice.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The skill reads the APIFY_TOKEN environment variable to authenticate with an external service. Although this is a common pattern, the file does not provide any user-facing warning, comment, or docstring disclosure that environment-based credentials will be accessed and used.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.