Back to skill

Security audit

aim-digital-human-video

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims, but it handles credentials and sensitive media in ways users should review carefully before installing.

Review before installing. Use this only for media you are comfortable sending to the AEP/TTS services and receiving back as a public video URL. Do not paste the aim-secret-key into chat; configure it through a secure local secret mechanism or a carefully protected .env file, and do not set AEP_BASE_URL to any untrusted endpoint. Periodically remove stale .task-history.jsonl entries if you do not want later runs to re-contact saved URLs.

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

Error
Location
scripts/gen_digital_human.py:99
Finding

Arbitrary AEP Endpoint Can Receive Authentication Credentials and Sensitive Media

Content
View full analysis

Vulnerability Details

File Location: scripts/gen_digital_human.py, lines 99-106, 188-200, 293-306, and 510-530
Vulnerability Type: Unrestricted authenticated upload destination
Risk Level: High

Vulnerable Code

python
p.add_argument("--user-id", default=os.getenv("AEP_USER_ID", "aim_user"))
p.add_argument("--test", action="store_true", default=True, help="使用测试环境(服务端FFS)")
p.add_argument("--prod", action="store_true", help="使用正式环境")

# aim-secret-key —— 展示/存储用这个规范名;读取时宽容接受常见变体
aep_group = p.add_argument_group("aim-secret-key 配置")
aep_group.add_argument("--aep-base-url", default=os.getenv("AEP_BASE_URL", DEFAULT_AEP_BASE_URL))
aep_group.add_argument("--aim-secret-key", default=resolve_secret_value())
python
tts_url = f"{args.aep_base_url.rstrip('/')}/{args.tts_service_id}{DEFAULT_TTS_AEP_ROUTE_URI}"
headers = {
    "X-AEP-CONSUMER-SECRET": args.aim_secret_key,
    "X-AEP-SERVICE-ID": args.tts_service_id,
    "X-AEP-REQUEST-ID": str(uuid.uuid4()),
}
print(f"[TTS] 走 AEP: {tts_url}")

resp = requests.post(tts_url, json=request_data, headers=headers, timeout=120,
                     proxies={"http": None, "https": None})
python
request_id = str(uuid.uuid4())
target_url = f"{args.aep_base_url.rstrip('/')}/{DEFAULT_AEP_SERVICE_ID}{DEFAULT_AEP_ROUTE_URI}"

headers = {
    "X-AEP-CONSUMER-SECRET": secret,
    "X-AEP-REQUEST-ID": request_id,
}

print(f"[AEP] 提交到: {target_url}")
print(f"[AEP] request_id: {request_id}")

resp = requests.post(target_url, json=payload, headers=headers, timeout=30,
                     proxies={"http": None, "https": None})
python
image_b64 = file_to_base64(args.image_path)
audio_b64 = file_to_base64(audio_path)

upstream_uuid = f"dh_{uuid.uuid4().hex[:12]}"

payload: dict = {
    "uuid": upstream_uuid,
    "userId": args.user_id,
    "imageBase64": image_b64,
    "audioBase64": audio_b64,
    "prompt": a
...[truncated 2468 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the production --aep-base-url override and avoid taking the authenticated endpoint from an ambient environment variable.
  2. Enforce HTTPS and an exact allowlist of approved AEP hostnames.
  3. Parse the endpoint with a standard URL parser and reject embedded credentials, fragments, unexpected ports, IP literals, and malformed hostnames.
  4. Disable redirects for authenticated submission requests. If redirects are operationally required, validate every destination and never forward the secret across origins.
  5. If development endpoints are necessary, place them behind an explicit unsafe-development mode and require a separate non-production credential.
  6. Display a clear consent notice describing which image, audio, transcript, and voice-biometric data will be transmitted.
  7. Apply request-size limits before reading and Base64-encoding files to reduce memory and service-abuse risks.
  8. Rotate any credential that may have been sent to an untrusted endpoint and review associated service usage.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gen_digital_human.py:235
Finding

Unvalidated Video URLs Enable Persistent Blind SSRF

Content
View full analysis

Vulnerability Details

File Location: scripts/gen_digital_human.py, lines 235-237, 398-416, and 544-568
Vulnerability Type: Server-side request forgery through untrusted polling URLs
Risk Level: Medium

Vulnerable Code

python
resp = requests.head(video_url, timeout=10, allow_redirects=True,
                     proxies={"http": None, "https": None})
if resp.status_code == 200:
    content_length = resp.headers.get("Content-Length", "?")
    print(f"[轮询] {elapsed}s - 视频已就绪!Content-Length: {content_length}")
    return
python
for e in pending:
    url = e.get("video_url", "")
    age_hours = (now - e.get("submitted_at", now)) / 3600
    try:
        resp = requests.head(url, timeout=10, allow_redirects=True,
                             proxies={"http": None, "https": None})
        status_code = resp.status_code
    except Exception:
        status_code = None
python
video_url = None
if isinstance(resp_data, dict):
    data_field = resp_data.get("data")
    if isinstance(data_field, dict):
        video_url = data_field.get("videoUrl")

if not video_url:
    raise RuntimeError(f"响应中未找到 videoUrl: {json.dumps(resp_data, ensure_ascii=False)[:200]}")

print(f"  TOS URL: {video_url}")

_record_task(upstream_uuid, args.task_name, video_url,
             audio_duration, f"{est_min}~{est_max}s", status="pending")

smart_poll_url(
    video_url=video_url,
    est_min=est_min, est_max=est_max,
    timeout=args.poll_timeout,
)

Technical Analysis

The script treats videoUrl from the remote AEP response as trusted. It neither validates the URL scheme and hostname nor checks whether the resolved address belongs to a loopback, private, link-local, reserved, multicast, or cloud-metadata network.

The polling request enables redirects through allow_redirects=True, so validating only the initial URL would not be sufficient. An externally accept ...[truncated 1945 chars]

Remediation
View remediation

Remediation Suggestions

  1. Accept only HTTPS video URLs hosted on an exact allowlist of expected TOS domains.
  2. Parse URLs before use and reject user information, unexpected ports, malformed hosts, IP-literal hosts, and non-HTTPS schemes.
  3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, reserved, unspecified, and known cloud-metadata addresses for both IPv4 and IPv6.
  4. Disable redirects. If redirects are unavoidable, validate the scheme, host, port, and resolved address at every redirect hop.
  5. Apply the same checks when reading existing URLs from .task-history.jsonl; do not trust persisted state.
  6. Use network-level egress controls so the Skill process can reach only approved AEP and object-storage endpoints.
  7. Limit redirect counts, total polling requests, and polling duration to reduce amplification.
  8. Consider using an opaque task-status API on the trusted AEP service rather than polling a service-provided arbitrary URL.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:89
Finding

Skill Instructions Require Users to Expose Authentication Secrets Through Chat

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 89-95
Vulnerability Type: Unsafe credential collection and handling guidance
Risk Level: Medium

Vulnerable Documentation

The relevant instructions, translated into English, direct the following workflow:

text
1. Visit the registration site and obtain an aim-secret-key.
2. Paste the key into the conversation.
3. The agent writes the user-provided key after aim-secret-key= in .env.
   The user does not edit the file directly.
4. Run the configuration check again.

Technical Analysis

The Skill explicitly asks the user to paste an authentication secret into the conversation and delegates writing that secret to the agent. This is unnecessary for the declared video-generation functionality and expands the credential's exposure beyond the local configuration file.

Conversation content may be retained in chat history, application logs, telemetry, debugging records, model-provider systems, or third-party integrations. The guidance also conflicts with the safer workflow in README.md, which tells the user to create and edit .env locally.

The code correctly excludes .env through .gitignore, but that does not protect a credential previously disclosed through chat. Git exclusion is not a substitute for secure secret entry and storage.

Attack Path

  1. The user follows SKILL.md and pastes the AEP secret into the conversation.
  2. The secret becomes part of the conversation record and may be copied into logs or telemetry.
  3. Any person, service, plugin, or integration with access to that retained content can recover the credential.
  4. The recovered key can be used against the AEP service within its assigned permissions and quota.
  5. If the conversation is shared for support or debugging, exposure can spread further without the user realizing that it contains a live credential.

Impact Assessment

The primary impact is disclo ...[truncated 408 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove every instruction asking users to paste secrets into chat.
  2. Use the safer local setup process already described in README.md, where the user creates and edits .env directly.
  3. Prefer an operating-system credential store, managed secret service, or non-echoing local terminal prompt over a plaintext configuration file.
  4. If .env remains supported, instruct users to apply owner-only permissions and ensure backups and diagnostics exclude it.
  5. Do not print the secret or include it in configuration-check output, exceptions, telemetry, or task history.
  6. Document immediate key rotation if a credential has been pasted into a conversation or sent to an untrusted endpoint.
  7. Reconcile SKILL.md and README.md so both prescribe the same secure credential-management workflow.
Vulnerability Patterns
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (23)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · .gitignore (reported line 13)May include surrounding context.

text
*.pyc

# Environment (contains secrets)
.env

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

  1. 复制配置模板并填入密钥:
bash
cp .env.example .env

编辑 .env,将密钥填入:

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill describes uploading image/audio/text to external AEP/TOS services and returning a public URL, but it does not clearly warn the user about external transmission, public accessibility, or retention implications. This creates a significant privacy and data-exposure risk because users may unknowingly submit sensitive biometrics, voice samples, or personal media.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

These instructions tell the agent to collect a secret key through the conversation and persist it into .env, without any safety warning or secure handling guidance. This is hazardous because it normalizes secret sharing in chat, risks transcript leakage, and stores credentials in a file that may be accessible to tools, logs, backups, or other processes.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill explicitly directs the agent to have the user paste a secret key into chat and then write it into a local .env file. In the context of agent skills, this is particularly dangerous because conversational input may be logged and the file write creates durable credential storage in the workspace, increasing the risk of theft, unintended reuse, or later exfiltration.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
SECRET_KEY_NAME = "aim-secret-key"

# 密钥唯一来源:skill 根目录的 .env 文件。不再读进程环境、不再摸家目录、不再跨 agent 找。
_ENV_FILE = Path(__file__).resolve().parent.parent / ".env"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 51)May include surrounding context.

python
SECRET_KEY_NAME = "aim-secret-key"

# 密钥唯一来源:skill 根目录的 .env 文件。不再读进程环境、不再摸家目录、不再跨 agent 找。
_ENV_FILE = Path(__file__).resolve().parent.parent / ".env"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 56)May include surrounding context.

python
SECRET_KEY_NAME = "aim-secret-key"

# 密钥唯一来源:skill 根目录的 .env 文件。不再读进程环境、不再摸家目录、不再跨 agent 找。
_ENV_FILE = Path(__file__).resolve().parent.parent / ".env"

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 52)May include surrounding context.

python
SECRET_KEY_NAME = "aim-secret-key"

# 密钥唯一来源:skill 根目录的 .env 文件。不再读进程环境、不再摸家目录、不再跨 agent 找。
_ENV_FILE = Path(__file__).resolve().parent.parent / ".env"


def resolve_secret() -> tuple[str, str]:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly instructs users to upload a face image, audio, and optional voice-cloning samples to remote AEP/TTS services, but it does not clearly disclose that biometric and voice data will leave the local environment and be processed by third parties. This creates a real privacy and consent risk, especially because face images and voice samples are sensitive personal data and may be retained, logged, or reused by external services.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill documents capabilities including environment access, local file read/write, network calls, and shell execution, but does not declare any tool scope constraints. This weakens least-privilege controls and makes it harder for a hosting agent to confine what the skill may do, especially since it edits local files and transmits user data to external services.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The activation guidance says to use the skill when the user says “帮我用这张图片做个数字人”, which is a natural everyday request rather than a narrowly scoped invocation condition. The file does not provide exclusion conditions or negative examples, so the boundary for when this skill should or should not activate is ambiguous.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill description sets the default preset voice to “中文女”, and later reiterates that if the user provides only text and no voice type, the default is a Chinese female voice. This imposes a language/locale choice by default rather than obtaining explicit user preference or opt-in.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The workflow instructs the agent to take a secret pasted by the user in chat and write it into the skill's local .env file. This is dangerous because chat channels, logs, transcripts, or downstream tools may expose the secret, and persisting it in a local file increases the chance of accidental disclosure or reuse beyond the user's intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The rules section explicitly requires the skill to default to “中文女” whenever the user gives text without specifying audio or voice type. That is a natural-language policy issue because it forces a locale/language choice without explicit user opt-in.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The manifest frames this skill as submitting media to AEP and polling for the resulting video URL. Spawning a local binary via subprocess to inspect media duration is an extra host-level capability that is not mentioned and is not inherent to the core purpose of image+audio to video generation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 133)May include surrounding context.

python
def get_audio_duration(audio_path: str) -> float:
    """用 ffprobe 获取音频时长(秒),失败返回 -1"""
    try:
        result = subprocess.run(
            ["ffprobe", "-v", "error", "-show_entries", "format=duration",
             "-of", "default=noprint_wrappers=1:nokey=1", audio_path],
            capture_output=True, text=True, timeout=10

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

This code sends user text and potentially biometric voice-sample data to a remote TTS service. External transmission is core to the feature, but in this context the content can be sensitive, and there are no in-code restrictions minimizing data, masking content in logs, or requiring explicit consent before upload.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 204)May include surrounding context.

python
}
    print(f"[TTS] 走 AEP: {tts_url}")

    resp = requests.post(tts_url, json=request_data, headers=headers, timeout=120,
                         proxies={"http": None, "https": None})
    if resp.status_code != 200:
        raise RuntimeError(f"[TTS] 请求失败,状态码: {resp.status_code}, 内容: {resp.text[:200]}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script transmits user-provided text, voice samples, images, and audio to external services and later returns a publicly accessible video URL, but it does not present an explicit user-facing consent/disclosure step at execution time. In this skill context, the data is highly sensitive biometric/media content, so lack of transparent notice increases privacy and data-exposure risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The script uploads base64-encoded user image and audio content to an external video-generation endpoint and accepts a public video URL in return. Given the skill's purpose, this is expected behavior, but it still represents real outbound transfer of potentially sensitive personal media and can expose users if they are not clearly informed.

Content

Scanner excerpt · scripts/gen_digital_human.py (reported line 317)May include surrounding context.

python
print(f"[AEP] 提交到: {target_url}")
    print(f"[AEP] request_id: {request_id}")

    resp = requests.post(target_url, json=payload, headers=headers, timeout=30,
                         proxies={"http": None, "https": None})

    if not resp.text.strip():

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

SQP-3 applies to natural-language policy issues in all file types, including README text. The README states a default of '中文女声' and lists preset voices, but it does not explicitly frame the language/locale choice as user-selected or opt-in, which can be read as forcing a locale-specific default behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The script’s human-facing documentation and defaults are entirely Chinese-centric, including the default voice preset of “中文女”, without offering a language choice or documenting that the skill is intentionally limited to Chinese users. This can violate language/locale policy when skills should not force a specific language absent opt-in or clear justification.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest describes a request/submit/poll/return flow for generating a digital human video from current user inputs. In addition to that flow, the script exposes a separate capability to persist task records locally, list them later, and automatically rescan prior pending tasks on future runs, which is broader than the stated user-facing behavior.

Content

No source excerpt is available for this finding.