Back to skill

Security audit

Ad Copy Writer

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it advertises, but it has under-disclosed credential and prompt-data exposure risks through its endpoint override and verbose logging behavior.

Review before installing. Use this only with a trusted environment: leave WERYAI_BASE_URL unset unless you intentionally point it at a trusted HTTPS WeryAI-compatible endpoint, use a revocable API key, avoid --verbose when prompts or source copy are sensitive, and assume submitted marketing material is sent to WeryAI and may consume paid credits.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vendor/weryai-text/cli.js:11
Finding

Unrestricted API endpoint override can disclose credentials and request content

Content
View full analysis

Vulnerability Details

File Location: scripts/vendor/weryai-text/cli.js:11-15; scripts/vendor/weryai-core/client.js:14-17, 39-43
Vulnerability Type: Unvalidated service endpoint configuration causing credential and data disclosure
Risk Level: High

Vulnerable Code

From scripts/vendor/weryai-text/cli.js:

js
const ctx = {
  apiKey: process.env.WERYAI_API_KEY || '',
  baseUrl: process.env.WERYAI_BASE_URL || 'https://api.weryai.com',
  verbose: flags.verbose,
  dryRun: flags.dryRun,
  requestTimeoutMs: Number(process.env.WERYAI_REQUEST_TIMEOUT_MS) || 30_000,
};

From scripts/vendor/weryai-core/client.js:

js
const url = `${baseUrl}${path}`;
const headers = {
  Authorization: `Bearer ${apiKey}`,
  'Content-Type': 'application/json',
};
js
const res = await fetch(url, {
  method,
  headers,
  body: body ? JSON.stringify(body) : undefined,
  signal: controller.signal,
});

Technical Analysis

The destination URL is derived directly from the WERYAI_BASE_URL environment variable without URL validation, an HTTPS requirement, or a trusted-host allowlist. The HTTP client then sends the WERYAI_API_KEY as a bearer credential to that destination.

For write requests, the body also contains the generated chat messages, including the user's prompt, source material, campaign details, and other supplied options. Therefore, control over the process environment is sufficient to redirect credentials and request content to an arbitrary endpoint.

This is a trust-boundary failure: a security-sensitive credential is attached to requests whose origin can be changed through untrusted configuration. A plaintext http:// destination is also accepted, allowing exposure through network interception.

Attack Path

  1. An attacker gains the ability to influence the environment of the process invoking the skill, such as through a compromised launcher, CI configuration, shell profile, container configuration, or deployment manifest. ...[truncated 1160 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the endpoint override in production and use a fixed WeryAI API origin where possible.
  2. If an override is operationally necessary, parse it with new URL() and require:
    • The https: protocol.
    • An exact trusted-host allowlist.
    • An expected port.
    • No embedded username or password.
  3. Reject malformed URLs and unapproved hosts before constructing the client.
  4. Attach the authorization header only after confirming that the final request origin is trusted.
  5. Configure redirect behavior defensively. Reject cross-origin redirects or ensure credentials are never forwarded to a different origin.
  6. Separate development credentials from production credentials and issue least-privilege, revocable API keys.
  7. Document WERYAI_BASE_URL as security-sensitive configuration and prevent untrusted users or jobs from setting it.
  8. Add automated tests confirming rejection of HTTP URLs, lookalike domains, embedded credentials, unexpected ports, and cross-origin redirects.

A hardened configuration could follow this pattern:

js
const allowedOrigins = new Set(['https://api.weryai.com']);
const configuredUrl = new URL(
  process.env.WERYAI_BASE_URL || 'https://api.weryai.com'
);

if (
  configuredUrl.protocol !== 'https:' ||
  !allowedOrigins.has(configuredUrl.origin) ||
  configuredUrl.username ||
  configuredUrl.password
) {
  throw new Error('Untrusted WeryAI API endpoint.');
}

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/vendor/weryai-core/client.js:20
Finding

Verbose request logging exposes complete prompts and source material

Content
View full analysis

Vulnerability Details

File Location: scripts/vendor/weryai-core/client.js:20-22, 79-98; request content is assembled in scripts/vendor/weryai-text/presets.js:130-156
Vulnerability Type: Sensitive information exposure through insufficient log sanitization
Risk Level: Medium

Vulnerable Code

From scripts/vendor/weryai-core/client.js:

js
if (verbose) {
  const sanitized = body ? sanitizeForLog(body) : null;
  log(`${method} ${url}${sanitized ? ` ${JSON.stringify(sanitized)}` : ''}`);
}
js
function sanitizeForLog(obj) {
  if (!obj || typeof obj !== 'object') return obj;
  const copy = Array.isArray(obj) ? [...obj] : { ...obj };

  for (const key of Object.keys(copy)) {
    const value = copy[key];
    if (/authorization|api[_-]?key/i.test(key)) {
      copy[key] = '***';
    } else if (typeof value === 'string' && /^(prompt|negative_prompt|negativePrompt|description|lyrics)$/i.test(key)) {
      copy[key] = truncate(value);
    } else if (typeof value === 'string' && /^(image|reference_audio|webhook_url)$/i.test(key)) {
      copy[key] = truncate(value);
    } else if (Array.isArray(value) && /^(images|videos|audios|image_url)$/i.test(key)) {
      copy[key] = value.map((item) => (typeof item === 'string' ? truncate(item) : item));
    } else if (value && typeof value === 'object') {
      copy[key] = sanitizeForLog(value);
    }
  }

  return copy;
}

The request content is placed into chat message content fields by scripts/vendor/weryai-text/presets.js:

js
return [
  { role: 'system', content: preset.systemPrompt },
  { role: 'user', content: buildUserPrompt(input, preset) },
];
js
if (input.sourceText) sections.push(`Source material:\n${input.sourceText}`);

sections.push(
  'Write the final deliverable directly. Do not explain your reasoning unless the user explicitly asks for notes or alternatives.'
);

return sections.join('\n\n');

Technical Analysis

The sanitizer recursively traverses the ...[truncated 2124 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not log request bodies by default, even in verbose mode.
  2. Explicitly redact every messages[].content value before serialization.
  3. Prefer metadata-only logging, such as:
    • HTTP method and trusted endpoint.
    • Message count and roles.
    • Content lengths.
    • Request identifiers.
    • A non-reversible digest when correlation is necessary.
  4. Use a deny-by-default sanitizer for nested request objects rather than relying on a list of sensitive property names.
  5. Add a separate, explicit opt-in mechanism for payload diagnostics and display a warning before use.
  6. Ensure production and CI environments cannot casually enable payload logging.
  7. Apply short retention periods and restrictive access controls to stderr and centralized logs.
  8. Add tests containing secrets in messages[].content and verify that none of the secret text appears in captured log output.

A safer logging approach would be:

js
function sanitizeForLog(obj) {
  if (!obj || typeof obj !== 'object') return obj;

  if (Array.isArray(obj)) {
    return obj.map(sanitizeForLog);
  }

  const copy = {};
  for (const [key, value] of Object.entries(obj)) {
    if (/authorization|api[_-]?key|content/i.test(key)) {
      copy[key] = '***';
    } else if (value && typeof value === 'object') {
      copy[key] = sanitizeForLog(value);
    } else {
      copy[key] = value;
    }
  }
  return copy;
}

For chat messages, logging only { role, contentLength } is preferable to retaining any content preview.

Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Model discovery and metadata filtering are not inherently malicious, but they are outside the narrow function implied by an ad-copy writer. Undisclosed auxiliary behaviors matter in security review because they can reveal service capabilities, expand network/API usage, and undermine informed consent about what the skill actually does.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes a skill for generating and polishing ad and marketing copy via a chat completion API, but this file returns fields like lyrics and coverUrl, which indicate music/media generation task handling rather than copywriting. That is a semantic mismatch in implemented behavior, not an expected implementation detail for an ad-copy writer.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares network access and environment-variable use in metadata but does not define an explicit tool scope such as allowed-tools or permissions. That omission weakens least-privilege guarantees and makes it harder for reviewers and runtime policy to constrain what the skill may access, especially since it uses an API key and external network calls.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger tests include broad natural-language phrases like writing ad copy variations and landing page copy, which are common requests that may appear in general assistant conversations. This can cause the skill to activate in contexts where the user did not explicitly intend tool use, increasing the chance of over-triggering, scope creep, or unintended routing of user content into the skill pipeline.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The manifest says the skill creates marketing copy through the WeryAI chat completion API, but this code is built around generic asynchronous generation task polling endpoints under /v1/generation/. That suggests the implementation supports a broader generation workflow than the narrowly described chat-completion copywriting purpose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This file exposes a model-enumeration capability by calling '/v1/chat/models', which is broader than the declared ad-copy-writing purpose of the skill. Scope mismatch is dangerous because it gives the skill discovery/access patterns not obviously required for its advertised function, increasing the attack surface and enabling capability probing or unauthorized use of backend model inventory.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file defines multiple presets for general-purpose writing tasks such as email, long-form, blog, social, summary, translation, and rewrite, which materially exceed the manifest's stated scope of ad/marketing copy generation. This kind of scope expansion increases the skill's utility for unintended tasks, weakens least-privilege boundaries, and can enable misuse or policy bypass by exposing broader text-processing capabilities than users or reviewers expect.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The summarization preset introduces a distinct capability not required for ad-copy writing and allows the skill to process arbitrary source material into condensed outputs. In context, this broadens the operational scope beyond the declared purpose and can be abused to repurpose the skill as a general document-processing tool, contrary to user and platform expectations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The translation/localization preset enables a broad language transformation capability that is not limited to advertising content, making the skill usable as a general translation tool. Because the manifest represents the skill as ad-copy-focused, this mismatch creates a capability-disclosure gap and expands opportunities for unintended or unauthorized use.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

This file is a generic CLI helper that parses flags and resolves JSON input, but it also pulls an API key, base URL, and timeout from process environment into the execution context. For an ad-copy-writing skill overall, using a remote API is expected, but for this helper module's documented/observed role as CLI plumbing, credential loading is an extra capability not reflected in the file's own behavior or interface.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.