Back to skill

Security audit

WeChat MiniProgram CloudBase Scaffold

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent WeChat CloudBase mini-program scaffold, but its included backend templates have authorization and quota-control weaknesses that should be reviewed before deployment.

Review this skill before using it for a real app. It is suitable as a scaffold or learning template, but do not deploy the included backend unchanged: add authorization for private group joins, validate or bound subscription grant recording, add rate limits and unique indexes, pin dependencies or commit lockfiles, and adjust locale/privacy disclosures for your users.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
assets/templates/cloudfunctions/api/index.js:131
Finding

Private Group Membership Authorization Bypass

Content
View full analysis

Vulnerability Details

File Location: assets/templates/cloudfunctions/api/index.js, lines 131-137
Vulnerability Type: Missing authorization check
Risk Level: Medium

Vulnerable Code

js
async function joinGroup(ctx, id) {
  const g = (await db.collection('groups').doc(id).get()).data;
  if (!g) return { error: '组不存在' };
  const existing = (await db.collection('group_memberships').where({ openid: ctx.OPENID, groupId: id }).get()).data;
  if (existing.length) return { error: '已经在组里了' };
  await db.collection('group_memberships').add({ data: { openid: ctx.OPENID, groupId: id, role: 'member' } });
  return { ok: true };
}

Technical Analysis

The joinGroup handler verifies that the requested group exists and that the caller is not already a member, but it does not verify whether the group is public or private. It also does not require an invitation, approval from the group owner, or any other server-side authorization evidence.

The caller's identity is safely derived from the platform-provided OPENID, so identity spoofing is not required. However, any authenticated user who learns or obtains a private group document ID can submit that ID to the join route and receive a valid membership record.

The route exposing this operation is:

js
if ((m = path.match(/^\/api\/groups\/([^/]+)\/join$/)) && method === 'POST') return await joinGroup(ctx, m[1]);

Attack Path

  1. The attacker authenticates normally through the WeChat mini-program.
  2. The attacker obtains a private group ID through disclosure, client-side data, logs, shared links, or another application feature.
  3. The attacker invokes POST /api/groups/{privateGroupId}/join.
  4. The server retrieves the group but does not inspect its type or require an invitation.
  5. The server inserts a group_memberships record with the attacker's platform-derived OPENID.
  6. Subsequent features that trust membership r ...[truncated 623 chars]
Remediation
View remediation

Remediation Suggestions

  • Permit direct membership creation only for public groups.
  • For private groups, require a server-generated, cryptographically random, single-use invitation token or an owner-approved join request.
  • Bind invitations to the intended group and, where appropriate, the intended user.
  • Store invitation expiration and consumption state on the server and update it atomically with membership creation.
  • Enforce authorization independently on every endpoint that reads or modifies group resources; do not rely solely on the join workflow.
  • Create a unique database index on (openid, groupId) to prevent duplicate memberships during concurrent requests.
  • Return a generic authorization error for private groups to avoid disclosing whether a supplied private group ID exists.

Example control flow:

js
if (g.type === 'private') {
  const invitation = await validateInvitation(ctx.OPENID, id, ctx.body.inviteToken);
  if (!invitation) return { error: 'Not authorized to join this group' };
}

T09 · Insecure Skill Coding Practices

Warning
Location
assets/templates/cloudfunctions/api/index.js:92
Finding

Client-Controlled Subscription Message Quota Inflation

Content
View full analysis

Vulnerability Details

File Location: assets/templates/cloudfunctions/api/index.js, lines 92-98
Vulnerability Type: Unverified client assertion and missing replay protection
Risk Level: Medium

Vulnerable Code

js
// Subscription authorization once, quota +1
async function subscribeGrant(ctx) {
  const g = (await db.collection('subscribe_grants').where({ openid: ctx.OPENID }).get()).data;
  if (g.length) await db.collection('subscribe_grants').doc(g[0]._id).update({ data: { remain: _.inc(1) } });
  else await db.collection('subscribe_grants').add({ data: { openid: ctx.OPENID, remain: 1 } });
  const g2 = (await db.collection('subscribe_grants').where({ openid: ctx.OPENID }).get()).data[0];
  return { ok: true, remain: g2.remain };
}

The endpoint is directly accessible through the API router:

js
if (path === '/api/users/me/subscribe-grant' && method === 'POST') return await subscribeGrant(ctx);

Technical Analysis

The endpoint increments the stored subscription-message quota solely because the client calls it. It does not receive or validate platform-issued evidence that wx.requestSubscribeMessage succeeded, and it has no nonce, idempotency key, replay protection, quota ceiling, or rate limit.

A modified client or direct cloud-function caller can therefore invoke the endpoint repeatedly. Although the platform-derived OPENID prevents one user from directly modifying another user's quota, it does not prevent unlimited inflation of the caller's own stored quota.

The scheduled dailyPush function trusts this value:

js
const remain = g.length ? g[0].remain : 0;
if (remain <= 0) {
  skipped++;
  continue;
}

It only decrements the value after a successful send. Permanent delivery failures are caught without reducing or clearing the inflated quota, allowing repeated failed attempts during later scheduled executions.

Attack Path

  1. The ...[truncated 1217 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not treat a client callback as authoritative evidence of subscription consent.
  • Where the platform offers verifiable authorization state or transaction identifiers, validate them server-side before recording quota.
  • Otherwise, redesign the stored value as a bounded, best-effort state rather than an authoritative count.
  • Require a server-issued, short-lived, single-use nonce for each grant-recording request.
  • Add an idempotency key and reject replayed requests.
  • Apply per-user rate limits and enforce a strict maximum quota.
  • Use an atomic transaction or conditional update to avoid concurrent increments and duplicate records.
  • Create a unique index on openid in subscribe_grants.
  • Classify permanent send failures and reset or decrement stale quota when the platform indicates that no valid subscription authorization exists.
  • Avoid logging raw OPENID values; use a redacted or keyed-hash identifier for operational correlation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (14)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs the agent to default to a specific backend choice ('默认走云开发,不必每次问') unless certain red-line conditions are met. This reduces user autonomy and can cause the agent to make an architectural decision without first confirming requirements, constraints, compliance needs, or cost preferences, potentially steering users into an unsuitable stack.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This login handler does more than authenticate or identify the caller: on first login it automatically provisions a user record and enrolls the user into every public group. That creates authorization-relevant side effects on an unauthenticated or minimally authenticated path, expands user access without explicit consent or policy checks, and can be abused to mass-populate groups, skew metrics, trigger downstream automations, or create denial-of-service/logic issues if many accounts are created.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The entire skill reference is written only in Chinese and does not indicate that the language is optional, user-selected, or required for a specific region-scoped skill. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code returns and stores natural-language content such as the default nickname '微信用户' in a single fixed language. The file also contains multiple Chinese user-facing error strings elsewhere, with no indication that users can opt into another language or locale.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The API returns user-facing natural-language messages such as '用户不存在' in Chinese, and similar fixed-language responses appear throughout the file. Under the language/locale policy, forcing a specific language without opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The package description is written only in Chinese ("核心 API 云函数(单函数 REST 路由)"), which can impose a specific language on users or maintainers without offering any language choice or documenting a locale-specific scope. This matches the policy category for language or locale constraints expressed in natural language.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency uses a caret range (^2.7.2), which permits automatic installation of newer minor and patch releases. In build or deployment environments, this can introduce supply-chain risk, unexpected breaking changes, or newly introduced vulnerabilities without explicit review, especially for cloud function code that runs server-side with platform privileges.

Content

Scanner excerpt · assets/templates/cloudfunctions/api/package.json (reported line 7)May include surrounding context.

json
"description": "核心 API 云函数(单函数 REST 路由)",
  "main": "index.js",
  "dependencies": {
    "wx-server-sdk": "^2.7.2"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency is specified with a caret range (^2.7.2), which allows newer minor and patch versions to be installed. This can lead to non-reproducible builds and inadvertent adoption of a compromised or breaking upstream release, creating a software supply chain risk even though the package itself is a standard SDK.

Content

Scanner excerpt · assets/templates/cloudfunctions/dailyPush/package.json (reported line 7)May include surrounding context.

json
"description": "每日提醒推送(定时触发器 + 云调用订阅消息)",
  "main": "index.js",
  "dependencies": {
    "wx-server-sdk": "^2.7.2"
  }
}

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The natural-language description at L4 is written only in Chinese and provides no indication that users may choose another language or that the locale restriction is intentional for a region-specific audience. Under the policy for natural-language violations, forcing a specific language without opt-in or documented justification should be flagged.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Using a caret version range for a production dependency allows newer compatible releases to be installed automatically, which can introduce unexpected behavior or supply-chain risk if an upstream package is compromised or ships a breaking security-relevant change. In a cloud function login component, dependency drift is more sensitive because authentication-related code should be reproducible and tightly controlled.

Content

Scanner excerpt · assets/templates/cloudfunctions/login/package.json (reported line 7)May include surrounding context.

json
"description": "登录云函数(OPENID 幂等建号 + 自动加入公开组)",
  "main": "index.js",
  "dependencies": {
    "wx-server-sdk": "^2.7.2"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The dependency uses a caret range (^2.7.2), which allows newer non-major versions to be installed over time. This can introduce supply-chain risk and reduce build reproducibility, because different installs may resolve to different package versions, including versions with bugs or compromised transitive content.

Content

Scanner excerpt · assets/templates/cloudfunctions/seed/package.json (reported line 7)May include surrounding context.

json
"description": "幂等建集合 + 预置种子数据",
  "main": "index.js",
  "dependencies": {
    "wx-server-sdk": "^2.7.2"
  }
}

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The app performs a login request automatically during onLaunch, which is a network operation that may transmit user or device-related data. Although failures are logged, there is no user-facing prompt, notice, or comment here explaining that login happens immediately at startup.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The tab bar text values are fixed Chinese strings ("打卡", "我的组", "我的"), which indicates the skill enforces a specific language in user-facing text. Under the policy rules, locale-specific behavior should either offer user choice or clearly document a justified regional constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The guidance mandates using an East China/UTC+8 date string approach ("东八区字符串") as a general design rule. This is a locale-specific requirement expressed in natural language, but the document does not indicate that users can choose their locale or that the skill is explicitly limited to a China-only deployment context.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.