Back to skill

Security audit

Apify Competitor Intelligence

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its Apify competitor-analysis purpose, but it can run any Apify Actor with the user's token and has under-scoped credential and output handling that warrant review before installation.

Install only if you are comfortable giving this skill access to an Apify token that can start Actor runs and consume account resources. Use a dedicated low-privilege Apify token with spend limits, keep Actor choices to the documented catalog, avoid sensitive input, treat exported CSV files as untrusted scraped data, and be cautious with the unpinned mcpc installation path.

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

T09 · Insecure Skill Coding Practices

Warning
Location
reference/scripts/run_actor.js:245
Finding

Spreadsheet Formula Injection in CSV Exports

Content
View full analysis
{ let value = row[key]; // Truncate long text fields if (typeof value === 'string' && value.length > 200) { value = value.slice(0, 200) + '...'; } else if (Array.isArray(value) || (typeof value === 'object' && value !== null)) { value = JSON.stringify(value) || ''; } // CSV escape: wrap in quotes if contains comma, quote, or newline if (value === null || value === undefined) { return ''; } const strValue = String(value); if (strValue.includes(',') || strValue.includes('"') || strValue.includes('\n')) { return `"${strValue.replace(/"/g, '""')}"`; } return strValue; }); csvLines.push(values.join(',')); } ``` ### Technical Analysis The CSV exporter escapes commas, double quotes, and newlines, but it does not neutralize values interpreted as formulas by spreadsheet applications. Remotely sourced Actor results can contain strings beginning with formula-triggering characters such as `=`, `+`, `-`, or `@`. CSV quoting does not reliably prevent formula evaluation. Consequently, a value such as `=HYPERLINK("https://attacker.example","Open")` may remain an active expression when the exported file is opened in Excel, LibreOffice Calc, or another spreadsheet application. Field names are also remotely derived through `Object.keys(data[0])` and written without CSV escaping or formula neutralization. ### Attack Path 1. An attacker publishes crafted content on a platform targeted by one of the supported scrapers. 2. The content begins with a spreadsheet f ...[truncated 1041 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
reference/scripts/run_actor.js:61
Finding

Output Directory Restriction Can Be Bypassed Through Symbolic Links

Content
View full analysis
0) { const fieldnames = Object.keys(data[0]); const csvLines = [fieldnames.join(',')]; for (const row of data) { const values = fieldnames.map((key) => { let value = row[key]; // Truncate long text fields if (typeof value === 'string' && value.length > 200) { value = value.slice(0, 200) + '...'; } else if (Array.isArray(value) || (typeof value === 'object' && value !== null)) { value = JSON.stringify(value) || ''; } // CSV escape: wrap in quotes if contains comma, quote, or newline if (value === null || value === undefined) { return ''; } const strValue = String(value); if (strValue.includes(',') || strValue.includes('"') || strValue.includes('\n')) { return `"${strValue.replace(/"/g, '""')}"`; } return strValue; }); csvLines.push(values.join(',')); } ...[truncated 2045 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:102
Finding

Apify Bearer Token Exposed Through Command-Line Arguments

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:15
Finding

Unpinned Globally Installed Executable Dependency

Content
View full analysis
Remediation
View remediation
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 (11)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares environment and network-capable behavior but does not define an explicit tool scope such as allowed tools or permissions boundaries. In practice, this increases the attack surface because the agent is instructed to use a bearer token, make outbound requests, and run scripts without a formally constrained execution policy.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documented workflow allows substitution of a generic ACTOR_ID into the fetch and execution commands, which enables execution beyond the listed competitor-intelligence actors. That broadens the skill from a bounded scraper workflow into effectively arbitrary Apify Actor execution, which could trigger unreviewed third-party code paths, wider data access, or unexpected external actions.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The skill explicitly transmits data and an authorization token to external Apify infrastructure in order to fetch actor details and run scrapers. External transmission is expected for this skill's purpose, but it remains security-relevant because user-supplied query data and execution parameters leave the local environment and may be processed by third-party services.

Content

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

md
This skill instructs the agent to select an Apify Actor, fetch its schema (via mcpc), and run scrapers. The included script communicates only with api.apify.com and writes outputs to files under the current working directory; it does not access unrelated system files or other environment variables.

Apify Actors only scrape publicly available data and do not collect private or personally identifiable information beyond what is openly accessible on the target platforms. For additional security assurance, you can check an Actor's permission level by querying `https://api.apify.com/v2/acts/:actorId` — an Actor with `LIMITED_PERMISSIONS` operates in a restricted sandbox, while `FULL_PERMISSIONS` indicates broader system access. For full details, see [Apify's General Terms and Conditions](https://docs.apify.com/legal/general-terms-and-conditions).

## Error Handling

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script allows the caller to supply any Apify actor ID and arbitrary JSON input, then executes that actor with the user's APIFY_TOKEN. That turns a competitor-intelligence skill into a generic remote job runner using a broadly scoped credential, enabling misuse for unrelated data access, spending, or execution of unreviewed third-party actors.

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 · reference/scripts/run_actor.js (reported line 155)May include surrounding context.

js
async function startActor(token, actorId, parsedInput) {
    // Convert "author/actor" format to "author~actor" for API compatibility
    const apiActorId = actorId.replace('/', '~');
    const url = `https://api.apify.com/v2/acts/${apiActorId}/runs`;

    const response = await fetch(url, {
        method: 'POST',

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 · reference/scripts/run_actor.js (reported line 187)May include surrounding context.

js
async function startActor(token, actorId, parsedInput) {
    // Convert "author/actor" format to "author~actor" for API compatibility
    const apiActorId = actorId.replace('/', '~');
    const url = `https://api.apify.com/v2/acts/${apiActorId}/runs`;

    const response = await fetch(url, {
        method: 'POST',

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 · reference/scripts/run_actor.js (reported line 226)May include surrounding context.

js
async function startActor(token, actorId, parsedInput) {
    // Convert "author/actor" format to "author~actor" for API compatibility
    const apiActorId = actorId.replace('/', '~');
    const url = `https://api.apify.com/v2/acts/${apiActorId}/runs`;

    const response = await fetch(url, {
        method: 'POST',

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 · reference/scripts/run_actor.js (reported line 291)May include surrounding context.

js
async function startActor(token, actorId, parsedInput) {
    // Convert "author/actor" format to "author~actor" for API compatibility
    const apiActorId = actorId.replace('/', '~');
    const url = `https://api.apify.com/v2/acts/${apiActorId}/runs`;

    const response = await fetch(url, {
        method: 'POST',

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code reads a general-purpose APIFY_TOKEN from the environment and uses it to launch whatever actor the user requested. In the context of unrestricted actor selection, this gives the skill a broader authority boundary than its stated purpose and can let an attacker consume account resources or invoke actors with access to sensitive datasets and integrations available to that token.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description says the skill will 'Analyze competitor strategies, content, pricing, ads, and market positioning' across many platforms, but it does not define clear trigger phrases, scope boundaries, or exclusion conditions. For a markdown/manifest file, this broad natural-language description could overlap with many generic competitor-analysis requests and may cause unintended invocation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The security section states the script communicates only with api.apify.com, but the documented schema-fetch command also contacts mcp.apify.com. This inaccurate assurance can mislead reviewers and users about the actual network surface, reducing informed consent and weakening trust in the skill's security claims.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.