Back to skill

Security audit

Creativault Creator Scraper

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent for creator search and outreach, but its updater and credential handling need human review before installation.

Install only if you trust Creativault and can review or disable the self-update path. Keep CV_SKILL_AUTO_UPDATE unset, use an HTTPS Creativault API URL you control or trust, use a scoped API key, confirm recipient lists before sending, and treat exported creator/contact files as sensitive personal data.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (5)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/skill_update.mjs:207
Finding
Unauthenticated Remote Self-Update Allows Replacement of Executable Skill Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_api_client.mjs:161-193`, `scripts/skill_update.mjs:207-224, 234-325`, `skill.json:6-9` **Vulnerability Type**: Unauthenticated remote code update **Risk Level**: Critical ### Vulnerable Code ```js // scripts/_api_client.mjs:161-193 function maybeHandleSkillUpdateMeta(meta = {}) { const latestVersion = meta?.skill_latest_version; const updateRequired = Boolean(meta?.skill_update_required); const updateAvailable = updateRequired || Boolean(meta?.skill_update_available); if (!latestVersion && !updateAvailable) { return; } const message = meta?.skill_update_message || `creator-scraper-cv has a newer version: current=${SKILL_META.version}, latest=${latestVersion || 'unknown'}`; console.error(JSON.stringify({ skill_update: { required: updateRequired, current_version: SKILL_META.version, latest_version: latestVersion || null, min_supported_version: meta?.skill_min_supported_version || null, message, update_command: 'node scripts/skill_update.mjs --yes', }, }, null, 2)); if (process.env.CV_SKILL_AUTO_UPDATE === 'true') { const result = spawnSync(process.execPath, [join(SCRIPT_DIR, 'skill_update.mjs'), '--yes'], { encoding: 'utf8', stdio: 'inherit', }); if (result.status !== 0) { console.error(JSON.stringify({ skill_update_error: 'Auto update failed. Please run node scripts/skill_update.mjs --yes manually.', exit_code: result.status, })); } } } ``` ```js // scripts/skill_update.mjs:207-224 async function fetchJSON(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch manifest: HTTP ${response.status} ${response.statusText}`); } return response.json(); } async function fetchText(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch file: HTTP ${response.status} ${response.sta ...[truncated 4132 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic executable updates from API-response processing. 2. Require an explicit, interactive update approval separate from ordinary Skill operations. 3. Authenticate manifests with an asymmetric digital signature and an immutable publisher public key embedded in the installed package. 4. Require a valid digest for every managed file and reject incomplete manifests. 5. Parse all URLs and enforce HTTPS with an exact hostname and repository-path allowlist. 6. Disable redirects, or validate every redirect destination against the same allowlist. 7. Require payload URLs to use the same authenticated origin as the manifest. 8. Download the complete release into a staging directory and validate all paths, hashes, signatures, file counts, and metadata before modifying the live installation. 9. Install updates atomically and implement automatic rollback if any operation fails. 10. Prefer package-manager releases, immutable release artifacts, or signed archives rather than independently downloaded mutable files. ]]>

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/skill_update.mjs:122
Finding
Remote Manifest Can Delete Files Across the Skill Root<![CDATA[ ## Vulnerability Details **File Location**: `skill-manifest.json:13-18`, `scripts/skill_update.mjs:122-152, 288-337` **Vulnerability Type**: Remotely controlled destructive synchronization **Risk Level**: High ### Vulnerable Code ```json // skill-manifest.json:13-18 "sync": { "mode": "mirror", "delete_missing": true, "managed_roots": [ "." ], "exclude": [ ".skill-backups/**" ] } ``` ```js // scripts/skill_update.mjs:122-152 function listMissingLocalFiles(manifestFiles, managedRoots) { if (managedRoots.length === 0) { return []; } const remotePaths = new Set(manifestFiles); const localPaths = []; for (const root of managedRoots) { const fullRoot = root === '.' ? skillRoot : join(skillRoot, root); const prefix = root === '.' ? '' : root; localPaths.push(...listLocalFiles(fullRoot, prefix)); } return [...new Set(localPaths)] .filter((path) => !remotePaths.has(path)) .filter((path) => !isSyncExcluded(path)) .sort((left, right) => left.localeCompare(right)); } ``` ```js // scripts/skill_update.mjs:288-289 const managedRoots = manifest.sync?.delete_missing ? getManagedRoots(manifest) : []; const staleFiles = listMissingLocalFiles(manifestPaths, managedRoots); ``` ```js // scripts/skill_update.mjs:327-337 let deletedFileCount = 0; for (const relativePath of staleFiles) { if (backupAndRemoveFile(relativePath, backupRoot)) { deletedFileCount += 1; pruneEmptyDirectories(dirname(join(skillRoot, relativePath))); } } ``` ```js // scripts/skill_update.mjs:158-170 function backupAndRemoveFile(relativePath, backupRoot) { const target = join(skillRoot, relativePath); if (!existsSync(target) || !statSync(target).isFile()) { return false; } const backup = join(backupRoot, relativePath); mkdirSync(dirname(backup), { recursive: true }); renameSync(target, backup); return true; } ``` ### Technical Analysis The remote manifest controls both the deletion policy and the mana ...[truncated 1985 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not allow a remote manifest to define managed roots or deletion policy. 2. Hard-code a minimal allowlist of files that the updater is permitted to replace. 3. Disable deletion during automatic and routine updates. 4. Never use the entire Skill root as a remotely controlled mirror target. 5. Preserve unknown and locally created files by default. 6. Require a separate, explicit confirmation that lists every file proposed for deletion. 7. Stage all changes and create a complete rollback journal before modifying the live installation. 8. Automatically restore all moved or replaced files if any update step fails. 9. Authenticate the manifest as described in the remote-update finding. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/_api_client.mjs:12
Finding
API Key and Operator Identity Can Be Transmitted over Plaintext HTTP or to an Arbitrary Host<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:72-74`, `scripts/_api_client.mjs:12-14, 82-110` **Vulnerability Type**: Insecure credential transmission and unrestricted API endpoint **Risk Level**: High ### Vulnerable Code ```md <!-- SKILL.md:72-74 --> - `CV_API_KEY` — Creativault Open API Key (obtain from admin dashboard) - `CV_USER_IDENTITY` — Operator email address - `CV_API_BASE_URL` (optional) — API base URL, defaults to `http://api.creativault.vip` ``` ```js // scripts/_api_client.mjs:12-14 const API_BASE = (process.env.CV_API_BASE_URL || '').replace(/\/+$/, ''); const API_KEY = process.env.CV_API_KEY; const USER_IDENTITY = process.env.CV_USER_IDENTITY || ''; ``` ```js // scripts/_api_client.mjs:93-110 const url = `${API_BASE}${path}`; for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { let response; try { if (!options.skipUserIdentity) { ensureUserIdentity(); } const headers = { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-CV-Skill-Name': SKILL_META.name || 'creator-scraper-cv', 'X-CV-Skill-Version': SKILL_META.version || 'unknown', 'X-CV-Skill-Channel': SKILL_META.channel || 'unknown', }; if (!options.skipUserIdentity) { headers['X-User-Identity'] = USER_IDENTITY; } response = await fetch(url, { method: 'POST', headers, body: JSON.stringify(processedBody), }); ``` ### Technical Analysis The documentation specifies a plaintext HTTP endpoint, while the client sends the API key in `X-API-Key` and the operator email in `X-User-Identity`. HTTP does not provide confidentiality or server authentication, allowing an on-path attacker to observe or alter requests and responses. The implementation also accepts any value from `CV_API_BASE_URL` without URL parsing, HTTPS enforcement, or host allowlisting. Consequently, a modified environment can direct credentials and sensitive request bodies to an arbitrary server. There i ...[truncated 1368 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the documented and implemented default to `https://api.creativault.vip`. 2. Parse `CV_API_BASE_URL` with the standard `URL` class before any request. 3. Reject all non-HTTPS protocols in production. 4. Enforce an exact allowlist of approved API hostnames and ports. 5. Permit HTTP only for an explicit development mode restricted to loopback addresses. 6. Validate redirect destinations or disable redirects for authenticated API requests. 7. Avoid placing reusable secrets in requests to configurable arbitrary origins. 8. Rotate any API keys that may have been used with the documented HTTP endpoint. 9. Apply narrowly scoped API keys, rate limits, expiration, and server-side anomaly detection. 10. Align documentation and implementation so operators are not instructed to configure an insecure endpoint. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export_to_csv.mjs:82
Finding
CSV Exporter Allows Arbitrary Writable File Overwrite or Append<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_to_csv.mjs:82-114` **Vulnerability Type**: Unrestricted filesystem write **Risk Level**: Medium ### Vulnerable Code ```js const params = parseArgs(); const outputPath = resolve(params.output || 'output.csv'); const mode = params.mode || 'append'; // Flatten all rows const flatRows = rows.map(r => flattenObject(r)); // Collect all headers const allHeaders = [...new Set(flatRows.flatMap(r => Object.keys(r)))]; const fileExists = existsSync(outputPath); if (mode === 'overwrite' || !fileExists) { // Write header + data (BOM for Excel UTF-8 compatibility) const bom = '\ufeff'; const headerLine = allHeaders.map(escapeCSV).join(','); const dataLines = flatRows.map(r => rowToCSV(allHeaders, r)).join('\n'); writeFileSync(outputPath, bom + headerLine + '\n' + dataLines + '\n', 'utf-8'); console.error(`[export] ${fileExists ? 'Overwritten' : 'Created'} ${outputPath}, wrote ${rows.length} rows`); } else { // Incremental append: read existing headers const existingContent = readFileSync(outputPath, 'utf-8'); const firstLine = existingContent.split('\n')[0].replace(/^\ufeff/, ''); const existingHeaders = firstLine.split(',').map(h => h.replace(/^"|"$/g, '')); // Check for new columns const newHeaders = allHeaders.filter(h => !existingHeaders.includes(h)); if (newHeaders.length > 0) { console.error(`[export] Warning: new data contains ${newHeaders.length} new column(s) (${newHeaders.join(', ')}), ignored during append`); } // Append using existing header order const dataLines = flatRows.map(r => rowToCSV(existingHeaders, r)).join('\n'); appendFileSync(outputPath, dataLines + '\n', 'utf-8'); console.error(`[export] Appended ${rows.length} rows to ${outputPath}`); } ``` ### Technical Analysis The `output` argument is resolved directly into an absolute path without restricting it to an export directory. Absolute paths and traversal-containing relative paths are ...[truncated 1625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a dedicated export directory owned by the application. 2. Reject absolute output paths. 3. Resolve the requested path against the export directory and verify that the final path remains inside it. 4. Reject traversal components and unexpected file extensions. 5. Use exclusive file creation by default instead of silently replacing existing files. 6. Require explicit user confirmation before overwriting an existing export. 7. Use `lstat` and safe file-opening flags to reject symbolic links. 8. Apply restrictive file permissions to exports containing contact or communication data. 9. Validate `mode` against a strict allowlist rather than treating every non-`overwrite` value as append mode. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export_to_csv.mjs:35
Finding
CSV Export Does Not Neutralize Spreadsheet Formula Injection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_to_csv.mjs:35-46, 86-114` **Vulnerability Type**: CSV formula injection **Risk Level**: Medium ### Vulnerable Code ```js function escapeCSV(value) { if (value === null || value === undefined) return ''; const str = String(value); if (str.includes(',') || str.includes('"') || str.includes('\n')) { return `"${str.replace(/"/g, '""')}"`; } return str; } function rowToCSV(headers, row) { return headers.map(h => escapeCSV(row[h])).join(','); } ``` ```js const flatRows = rows.map(r => flattenObject(r)); const allHeaders = [...new Set(flatRows.flatMap(r => Object.keys(r)))]; if (mode === 'overwrite' || !fileExists) { const bom = '\ufeff'; const headerLine = allHeaders.map(escapeCSV).join(','); const dataLines = flatRows.map(r => rowToCSV(allHeaders, r)).join('\n'); writeFileSync(outputPath, bom + headerLine + '\n' + dataLines + '\n', 'utf-8'); } else { const dataLines = flatRows.map(r => rowToCSV(existingHeaders, r)).join('\n'); appendFileSync(outputPath, dataLines + '\n', 'utf-8'); } ``` ### Technical Analysis The escaping routine correctly handles CSV delimiters, double quotes, and newlines, but it does not address spreadsheet formula interpretation. Spreadsheet applications may treat cells beginning with characters such as `=`, `+`, `-`, or `@` as formulas. Leading tab or carriage-return characters can also be used to bypass simplistic checks. Creator profile data is externally controlled. Fields such as nicknames, biographies, URLs, or other returned attributes can therefore begin with formula syntax. Quoting a value according to CSV syntax does not necessarily prevent spreadsheet software from evaluating it as a formula. ### Attack Path 1. A creator or another upstream data source places a formula-like value in a field that the Skill collects, such as `=HYPERLINK(...)`, `+SUM(...)`, or another spreadsheet expression. 2. The API returns this value in creator ...[truncated 998 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all exported profile and API fields as untrusted spreadsheet input. 2. Before CSV quoting, detect cells whose first effective character is `=`, `+`, `-`, or `@`. 3. Also detect dangerous prefixes after leading spaces, tabs, carriage returns, and other control characters. 4. Neutralize dangerous cells using a spreadsheet-compatible strategy, such as prefixing them with an apostrophe while preserving the original value for display. 5. Apply protection to headers as well as data values. 6. Enable spreadsheet-safe output by default rather than as an optional mode. 7. Document that exported files contain untrusted external data. 8. Add tests covering quoted formulas, leading whitespace, tabs, carriage returns, Unicode variants, URLs, and nested flattened fields. 9. Consider generating a format with explicit text cell types when strong spreadsheet interoperability is required. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (41)

Ae1

High
Category
analysis-evasion
Content
node scripts/skill_update.mjs --check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/skill_update.mjs --check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/skill_update.mjs --check
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/generate_manifest.mjs --note "Describe this release"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
const excludedPathNames = new Set([
  '.DS_Store',
  'Thumbs.db',
  '.env',
]);

function readJSON(path) {
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
const excludedPathNames = new Set([
  '.DS_Store',
  'Thumbs.db',
  '.env',
]);

function readJSON(path) {
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
const excludedPathNames = new Set([
  '.DS_Store',
  'Thumbs.db',
  '.env',
]);

function readJSON(path) {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Self-Modification

High
Category
Rogue Agent
Content
#!/usr/bin/env node
// Check and update this skill from a remote manifest.
//
// Usage:
//   node scripts/skill_update.mjs --check
Confidence
96% confidence
Finding
A self-modifying/self-updating script is a genuine security concern here because it allows the skill to replace its own local files from remote content. In the context of a creator scraping/outreach skill, this capability is unnecessary and increases the blast radius of any upstream compromise into arbitrary code or behavior changes on the client side.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises capabilities that require environment-variable access and outbound network access, but it does not declare any explicit tool scope or permission boundary. In an agent environment, this can lead to overbroad authority being granted implicitly, making it easier for the skill to access secrets and perform external actions without clear operator review.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill explicitly promotes scraping creator data from multiple social platforms and sending batch outreach emails, but it provides no visible safeguards around consent, privacy, lawful data use, rate limits, or recipient impact. In context, this makes the skill more dangerous because it combines mass data collection with automated outbound communication, increasing the risk of privacy abuse, spam, and platform-policy violations.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The invocation keywords are broad enough to match generic requests like outreach or send email, which can cause the skill to activate in contexts beyond creator discovery. Because this skill includes batch scraping and outbound messaging behavior, overly broad routing increases the chance of unintended external actions or privacy-invasive collection being triggered by ambiguous prompts.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill explicitly supports exporting scraped creator data into downloadable xlsx/csv/html files and sharing file links, but the description does not warn users that collected data may contain personal information or sensitive profile/contact data. In a scraping and outreach context, omission of this warning increases the likelihood of inappropriate collection, distribution, or retention of personal data, especially when users may treat generated download links as low-risk artifacts.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger phrase list contains very broad terms such as 'similar creators', 'lookalike', and especially generic discovery phrases in both Chinese and English. In an agentic system, overly broad activation terms can cause this skill to be selected for unrelated user requests, leading to unintended scraping or creator-search actions and increasing the chance of privacy, compliance, or policy-violating behavior in contexts where the user did not explicitly request it.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger list includes broad, everyday phrases such as 'search creators' and 'influencer discovery', which can cause the agent to invoke this skill in loosely related contexts without strong user intent. In this skill, accidental activation is more concerning because it can lead into creator scraping and downstream outreach workflows, increasing the chance of unintended data collection or contact actions.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The skill metadata includes very broad invocation phrases such as '建联, 发邮件, email outreach, send email, outreach', which can match ordinary user requests without sufficient scoping to the Creativault workflow. In a high-risk skill that can trigger email sending, overly generic triggers increase the chance of unintended routing into an outbound-contact capability, potentially leading to accidental outreach or user confusion about what action will be taken.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The decision rules map ambiguous natural-language inputs like '发邮件', '建联', 'reach out', '待办', and '沟通历史' directly to operational scripts without clear scope boundaries or eligibility checks. Even though the document later requires confirmation before sending, ambiguous routing at the skill-selection stage can still cause unintended invocation of sensitive outreach functionality and increase the risk of acting on incomplete or mismatched user intent.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This is a markdown file, so SQP-2 applies to missing safety/privacy warnings in the description. The document instructs users to send `X-API-Key` and `X-User-Identity` headers and later to provide `webhook_url` and handle authenticated `file_url` exports, but it does not warn that these values may expose credentials, identity, or exported creator/contact data if logged or shared improperly.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Line L03 states that when the user says a country name in Chinese, the agent must convert it to an ISO code, and the document overall is written as a Chinese-only mapping reference. There is no indication that other languages are accepted or that Chinese is an optional locale, which can violate a language/locale policy requiring user choice or explicit justification.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
This markdown file is written as an operational instruction and states that the agent should act when the user provides Chinese language names. That imposes a Chinese-language expectation without any user opt-in or explicit note that the skill is intentionally region- or locale-specific.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This file documents retrieval of direct contact details (email, WhatsApp, Line, Zalo) together with creator profile and audience analytics, but provides no privacy constraints, consent requirements, use limitations, or anti-abuse guidance. In the context of a skill explicitly designed for scraping creators and sending outreach at scale, that omission materially increases the risk of unsolicited contact, profiling, and misuse of personal data.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code performs a POST request and includes both the request body and an X-User-Identity header when enabled. While the module has internal comments and error handling, it does not provide a user-facing disclosure here that user/system data is transmitted to an external API.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The client can automatically invoke a local update script via child_process when server-supplied metadata indicates an update and the CV_SKILL_AUTO_UPDATE flag is enabled. Even though the executed path is local, this creates a remote-influenced execution path in a scraping/outreach skill where self-modification is not essential, increasing supply-chain and unintended code execution risk if the update script or package contents are tampered with.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The auto-update path launches a subprocess during normal API handling without a strong user-facing confirmation at the point of execution. In the context of a data-collection and outreach skill, this hidden execution behavior is riskier because a remote service response can influence local code changes and process spawning, which users may not expect.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code performs a network/API call to send emails, which transmits recipient identifiers, email addresses, and message content. Although the header documents parameters, it does not clearly warn the user that running the script will actually send outbound email and transmit this data to an external service.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file’s natural-language documentation and runtime status messages are entirely in Chinese, including usage instructions and progress output. This imposes a specific language on users without any opt-in, language selection, or documented justification that the skill is China/Chinese-specific.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/_api_client.mjs:183

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/_api_client.mjs:12

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/skill_update.mjs:224