Back to skill

Security audit

PowPow Simple — đăng bút ký du lịch lên bản đồ, tạo Người số biết trò chuyện

Security checks for vulnerabilities and agentic risk

Overview

The skill’s PowPow workflow is mostly disclosed and purpose-aligned, but it asks the agent to collect a PowPow password and can publish, delete, upload images, or spend badges through scripts without enforced confirmation gates.

Review before installing. This skill should only be used if you are comfortable giving the agent your PowPow login once, storing a PowPow session token under POWPOW_STATE_DIR, and letting scripts upload chosen local images, publish public posts, delete your own posts, and spend 2 non-refundable badges to create public digital humans. Confirm the exact draft, account, images, location, and badge cost before any publish/create/delete step, and clear the stored session when done.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish.js:68
Finding

Public post publication lacks an executable user-confirmation gate

Content
View full analysis

Vulnerability Details

File Location: scripts/publish.js:68-71, 99-108, 154-170
Vulnerability Type: Missing authorization confirmation for a public state-changing operation
Risk Level: Medium

Complete Code Snippet

javascript
async function publishWithRetry(postData, maxRetries = 3) {
  let lastError;
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const body = await api('POST', '/api/posts', postData);
      if (body && body.success && body.post) return body.post;
      throw new Error(body && body.error ? body.error : 'Unexpected response');
javascript
async function main() {
  const [, , htmlPath] = process.argv;
  if (!htmlPath) {
    console.error('Usage: node publish.js <html-file-path>');
    process.exit(1);
  }

  let content = fs.readFileSync(htmlPath, 'utf-8');
  console.log(`Content length: ${content.length} chars`);
javascript
const postData = {
  type: 'text',
  content,
  ...(locInfo
    ? {
        lng: locInfo.lng,
        lat: locInfo.lat,
        locationName: locInfo.name,
        isLocationExposed: true,
      }
    : { isLocationExposed: false }),
};

console.log('\nPublishing...');
try {
  const post = await publishWithRetry(postData);

Technical Analysis

The publication script accepts only a draft path and immediately proceeds toward an authenticated POST /api/posts. It does not require a confirmation flag, interactive approval, dry-run transition, or short-lived authorization artifact.

SKILL.md:302-325 instructs the agent to present a preview and wait for an explicit publish decision. That documentation is useful workflow guidance, but it is not enforced by the executable entry point. Any agent or other caller with access to the script can bypass the documented confirmation stage by invoking the script directly.

Before posting, the same execution path can upload local images referenced by the draft. The resulting payload can also include a location with ...[truncated 1465 chars]

Remediation
View remediation

Remediation Suggestions

  • Make preview or dry-run behavior the default.
  • Require an explicit --confirm-publish control before any upload or publication occurs.
  • Prefer a short-lived approval artifact generated after the user-facing preview step.
  • Bind the approval artifact to:
    • A cryptographic hash of the final draft.
    • The authenticated account identifier.
    • The image manifest.
    • The location and exposure setting.
    • An expiration time.
  • Reject publication if the draft or account differs from the approved values.
  • Perform the confirmation check before uploading local images, preventing disclosure when publication was not authorized.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create-digital-human.js:159
Finding

Badge-consuming digital-human creation relies only on documentation-level confirmation

Content
View full analysis

Vulnerability Details

File Location: scripts/create-digital-human.js:37-60, 159-198
Vulnerability Type: Missing executable consent enforcement for an irreversible resource-consuming operation
Risk Level: Medium

Complete Code Snippet

javascript
function parseArgs(argv) {
  const opts = {
    name: null, desc: null, descFile: null,
    lng: null, lat: null, locationName: null,
    avatar: null, avatarRef: null,
    asJson: false,
  };
  for (let i = 0; i < argv.length; i++) {
    switch (argv[i]) {
      case '--name': opts.name = argv[++i]; break;
      case '--desc': opts.desc = argv[++i]; break;
      case '--desc-file': opts.descFile = argv[++i]; break;
      case '--lng': opts.lng = argv[++i]; break;
      case '--lat': opts.lat = argv[++i]; break;
      case '--location-name': opts.locationName = argv[++i]; break;
      case '--avatar': opts.avatar = argv[++i]; break;
      case '--avatar-ref': opts.avatarRef = argv[++i]; break;
      case '--json': opts.asJson = true; break;
      default:
        throw new Error(`Unknown argument: ${argv[i]}`);
    }
  }
  return opts;
}
javascript
// Badge pre-check: creating costs BADGES_REQUIRED badges and deletion does
// NOT refund them - fail fast before uploading anything.
if (session.userId) {
  const bal = await api('GET', `/api/badges/balance?userId=${session.userId}`);
  const current = bal.balance ? bal.balance.balance : 0;
  if (current < BADGES_REQUIRED) {
    throw new Error(`Badge balance too low: ${current} available, ${BADGES_REQUIRED} required. Earn badges on the platform first (see https://global.powpow.online).`);
  }
  console.log(`  💰 Badge balance: ${current} (creating a digital human consumes ${BADGES_REQUIRED})`);
} else {
  console.warn('  ⚠️ No userId in session - skipping local badge pre-check (server will still reject if insufficient).');
}

const { avatarUrl, referenceImageUrl } = await resolveAvatar(opts, description);

console.log('  ⏳ Creating digital h
...[truncated 2248 chars]
Remediation
View remediation

Remediation Suggestions

  • Add a default preview mode that displays the exact name, persona, image source, location, account, expiration, and badge cost without uploading or creating anything.
  • Require a distinct --confirm-create control for the state-changing path.
  • Prefer a short-lived signed approval artifact created only after the confirmation step.
  • Bind approval to:
    • The authenticated user ID.
    • The exact name and persona text.
    • Avatar and reference-image identifiers or hashes.
    • Coordinates and location name.
    • The two-badge cost.
    • An expiration time.
  • Validate approval before uploading any local image or requesting avatar generation.
  • Fail closed if the payload changes after approval.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/delete-post.js:11
Finding

Authenticated post deletion is performed without confirming the exact target

Content
View full analysis

Vulnerability Details

File Location: scripts/delete-post.js:11-19
Vulnerability Type: Missing confirmation gate for a destructive operation
Risk Level: Medium

Complete Code Snippet

javascript
async function main() {
  const [, , postId] = process.argv;
  if (!postId) {
    console.error('Usage: node delete-post.js <post-id>');
    process.exit(1);
  }

  try {
    const body = await api('DELETE', `/api/posts/${postId}`);
    console.log(`✅ Post deleted: ${postId}`);

Technical Analysis

The deletion helper accepts a post identifier and immediately issues an authenticated DELETE request. It does not retrieve and display the target post first, require an interactive response, support a safe dry-run default, or demand an explicit confirmation value tied to the post ID.

Server-side ownership restrictions limit deletion to posts belonging to the authenticated user. This is an important authorization control, but it does not prove that the user authorized deletion of the particular post selected by the agent.

Because the helper is directly callable, an agent can bypass any conversational confirmation process and permanently delete a user-owned post.

Attack Path

  1. The user has a valid authenticated PowPow session.
  2. An agent learns or is supplied with the identifier of a post owned by that user.
  3. Due to prompt manipulation, mistaken identification, or workflow error, the agent invokes: node scripts/delete-post.js <post-id> without obtaining approval for that exact post.
  4. The script issues DELETE /api/posts/<post-id> using the saved user session.
  5. The server verifies ownership and deletes the post.
  6. The user loses the selected post despite never approving the destructive action.

Impact Assessment

The server-side account scope prevents deletion of other users’ posts, so the impact is confined to content owned by the authenticated account. Within that scope, successful exploitation can permanently rem ...[truncated 240 chars]

Remediation
View remediation

Remediation Suggestions

  • Default to a non-destructive inspection mode that retrieves and displays the target post’s author, title or excerpt, creation time, and location.
  • Require a separate --confirm-delete value tied to the exact post ID.
  • Prefer a short-lived approval token bound to both the authenticated account and target post.
  • Validate that the post metadata has not changed between preview and deletion where practical.
  • Consider a recoverable soft-delete or trash period if supported by the platform.
  • Fail closed when confirmation is absent, expired, or associated with a different account or post.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 62)May include surrounding context.

md
dưới `scripts/` là một bước được văn bản hóa của quy trình đăng bài (đăng nhập → tự kiểm →
  mã hóa địa lý → ghép → dựng → đăng → xác minh), không phải công cụ ẩn độc lập.
- `delete-post.js` chỉ xóa bài của chính người dùng đang đăng nhập
  (`DELETE /api/posts/{id}`, giới hạn bởi JWT, server ép buộc) và được văn bản hóa tại
  Bước 8 trong SKILL.md để dọn bài kiểm thử. Năng lực này cũng được khai báo trong
  description của kỹ năng.
- Không kèm bất kỳ khóa bí mật nào. `config.json` chỉ chứa giá trị mặc định. JWT phiên

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill behavior extends beyond the declared description by including badge-balance consumption checks and AI avatar generation with a non-refundable cost. When a skill can spend user resources or trigger materially different actions than advertised, users and reviewers may not understand the real risk surface, which undermines informed consent and security review.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill instructs the agent to ask the user for their PowPow username and password directly in chat, then use those credentials for login. Collecting primary credentials through the conversational agent is dangerous because it conditions users to reveal secrets to an intermediary and creates a high-value phishing and credential-exposure channel if logs, prompts, or tooling are compromised.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The onboarding flow explicitly prescribes a trust-building introduction followed by a simple request for username and password, normalizing direct secret sharing with the agent. Even if the implementation tries to avoid command-line leakage, the core pattern remains unsafe because the agent becomes a credential collector and any transcript retention, debugging, or compromise could expose account passwords.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/match-digital-human.js "<topic>" --limit 3 --json # gợi ý đã xếp hạng

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/match-digital-human.js "<topic>" --limit 3 --json # gợi ý đã xếp hạng

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/list-digital-humans.js "<name>" --json # tìm theo tên

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/list-digital-humans.js "<name>" --json # tìm theo tên

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/geocode.js "潮白河大桥" --limit 5 # thêm --city 北京 để thu hẹp

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- **Tự tìm ảnh** (chỉ khi đã cấu hình Unsplash key): `--image search:<từ khóa tiếng Anh>` (hoặc `node scripts/search-image.js "<keywords>"`)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**Bắt buộc: luôn dựng HTML bài đăng bằng `scripts/compose.js`. Tuyệt đối không tự tay viết span thành phần, tuyệt đối không tự viết code dán nối vòng qua html-f

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
**Bắt buộc: luôn dựng HTML bài đăng bằng `scripts/compose.js`. Tuyệt đối không tự tay viết span thành phần, tuyệt đối không tự viết code dán nối vòng qua html-f

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/verify.js <post-id>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/delete-post.js <post-id>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/create-digital-human.js \

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · references/security.md (reported line 41)May include surrounding context.

md
- `POST /api/upload/post-image` — tải ảnh cục bộ lên (multipart `file`; cần đăng nhập)
- `POST /api/posts` — tạo bài đăng (cần đăng nhập; server ép buộc giới hạn & kiểm duyệt)
- `GET  /api/posts/{id}` — xác minh một bài đăng
- `DELETE /api/posts/{id}` — xóa bài của chính mình (dọn bài kiểm thử)
- `POST /api/digital-humans` — tạo Người số (cần đăng nhập; tiêu 2 huy hiệu, hết hạn 30 ngày)
- `POST /api/digital-humans/generate-avatar` — avatar AI từ thiết lập nhân vật
  (cần đăng nhập; 8–30 giây; theo thiết kế nền tảng cần URL ảnh tham chiếu)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/delete-post.js (reported line 2)May include surrounding context.

js
/**
 * Delete one of your own posts (DELETE /api/posts/[id], JWT-scoped).
 * Useful for cleaning up test posts. You can only delete your own posts.
 *
 * Usage: node delete-post.js <post-id>

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill declares no explicit tool/permission scope even though the workflow clearly relies on environment variables and outbound network access. Missing scope boundaries increases the chance an agent runtime grants broader capabilities than intended, making credential handling and public posting actions less auditable and easier to misuse.

Content

No source excerpt is available for this finding.

Ssd 4

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill deliberately uses a rapport-building introduction before later requesting credentials, a social-engineering pattern that lowers user suspicion and trains them to trust secret requests from the agent. In the context of an LLM skill, this is especially risky because it mirrors phishing tactics and can make users more likely to disclose passwords both here and in future malicious interactions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The document explicitly describes persisting and reading a user JWT session token from a local file, but frames it as routine operation without a prominent user-facing warning that the file is a live credential. In an agent skill context, normalized handling of a reusable auth token increases the chance of accidental exposure, reuse by other components, or unsafe file access patterns if the skill later reads or transmits it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The documented endpoints include uploading local images, creating public posts, verifying them, and deleting posts, but the file does not present these as sensitive side-effecting operations requiring explicit user awareness and confirmation. In a skill that handles local files and publishes content externally, lack of strong disclosure raises the risk of unintended data transmission or destructive actions being performed under an authenticated session.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/geocode.js (reported line 81)May include surrounding context.

js
console.log('Present these to the user to pick. If none fit, in this order:');
    console.log('  1) retry with different wording or --city <城市>;');
    console.log('  2) widen --limit and help the user recognise one from its district/address;');
    console.log('  3) publish without a location. Never ask the user for coordinates.');
  } catch (err) {
    if (err instanceof SessionExpiredError) {
      console.error(`❌ ${err.message}`);

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file sets DEFAULT_ACCEPT_LANGUAGE to zh-CN and includes user-facing error messages in Chinese, which effectively forces a specific language/locale behavior. The policy allows locale constraints only when users are given a choice or the constraint is clearly documented and justified, neither of which is present here.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script queries /api/digital-humans?scope=all, which enumerates all digital humans on the platform rather than limiting results to the current user or to objects needed for the declared posting/creation workflow. In a skill whose stated purpose is creating and publishing the user's own content, broad listing expands access to unrelated public or semi-public records and can enable unnecessary data exposure, profiling, or downstream misuse.

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/search-image.js (reported line 57)May include surrounding context.

js
}

async function searchUnsplashOnce(query, key) {
  const url = `https://api.unsplash.com/search/photos?query=${encodeURIComponent(query)}&per_page=10&orientation=landscape`;

  for (let attempt = 0; attempt < 2; attempt++) {
    try {

Static analysis

Detected: suspicious.potential_exfiltration

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/lib/api-client.js:66