Back to skill

Security audit

Song Cover EN - 中文歌转英文翻唱

Security checks for vulnerabilities and agentic risk

Overview

ClawScan marks this song-cover skill suspicious because scanners report unsafe checkpoint loading and a file-write path issue, alongside workflow and disclosure concerns; the review is high confidence, but analysis was partial.

Consider the suspicious verdict before installing, especially the confirmed unsafe checkpoint loading and the reported possibility of writing files outside the output directory. The scan is incomplete: binary demo files were outside analyzer coverage, and several referenced local paths were unresolved. The results do not establish malicious intent, but they also cannot establish that the unreviewed parts or external model files are safe.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/comfy_client.py
Finding

Server-controlled image filenames escape the local output directory

Content
View full analysis

Impact

A filename such as ../outside.txt writes into the existing parent directory; an absolute filename discards out_dir entirely. The service can create or truncate files writable by the client process and supply their contents. No image decoding, extension restriction, containment check, or confirmation precedes the write.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (21)

Tainted flow: 'req' from os.environ.get (line 16, credential/environment) → urllib.request.build_opener.open (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/comfy_client.py (reported line 17)May include surrounding context.

python
body = json.dumps(data).encode()
        headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    with _OPENER.open(req, timeout=60) as r:
        raw = r.read()
        ct = r.headers.get("Content-Type", "")
        return json.loads(raw) if "json" in ct else raw

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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/03_separate.py (reported line 100)May include surrounding context.

python
# mix's own RMS so it neither clips nor needs guessing downstream
    rv = float(np.sqrt(np.mean(voc ** 2)))
    rm = float(np.sqrt(np.mean(y ** 2)))
    gain = (rm / rv) if rv > 1e-9 else 0.0
    voc = voc * min(gain, 6.0)
    resid = y[:len(voc)] - voc[:len(y)]

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

The edge-tts workflow omits disclosure that lyrics are sent to an external speech service.

Medium
Category
Output Handling
Confidence
93% confidence
Finding

Following the documented edge-tts workflow sends lyric text to an external speech service without explicitly disclosing that boundary. This could expose unpublished or confidential lyrics; the risk is limited to this older workflow, since the current synthesis script replaces it with local SoulX inference.

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/02_audio.py (reported line 34)May include surrounding context.

python
s = requests.Session()
    s.headers["User-Agent"] = UA["User-Agent"]
    s.get("https://www.bilibili.com", timeout=20)      # cookie for the playback api
    r = s.get("https://api.bilibili.com/x/web-interface/search/type",
              params={"search_type": "video", "keyword": term, "page": 1},
              headers={"Referer": "https://www.bilibili.com/"}, timeout=25)
    out = []

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 · references/pipeline.md (reported line 50)May include surrounding context.

text
GET https://www.bilibili.com                       # 拿 buvid cookie
GET https://api.bilibili.com/x/web-interface/view?bvid=%E2%80%A6
GET https://api.bilibili.com/x/player/playurl?bvid=%E2%80%A6&cid=%E2%80%A6&fnval=16&fourk=1
    → max(dash["audio"], key=lambda x: x["bandwidth"])["baseUrl"]
GET baseUrl  (Referer: https://www.bilibili.com/video/<bv>)

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 · references/pipeline.md (reported line 51)May include surrounding context.

text
GET https://www.bilibili.com                       # 拿 buvid cookie
GET https://api.bilibili.com/x/web-interface/view?bvid=%E2%80%A6
GET https://api.bilibili.com/x/player/playurl?bvid=%E2%80%A6&cid=%E2%80%A6&fnval=16&fourk=1
    → max(dash["audio"], key=lambda x: x["bandwidth"])["baseUrl"]
GET baseUrl  (Referer: https://www.bilibili.com/video/<bv>)

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/02_audio.py (reported line 51)May include surrounding context.

python
s.headers["User-Agent"] = UA["User-Agent"]
    if not s.cookies:
        s.get("https://www.bilibili.com", timeout=20)
    v = s.get("https://api.bilibili.com/x/web-interface/view",
              params={"bvid": bv}, headers={"Referer": "https://www.bilibili.com/"},
              timeout=25).json()
    if v.get("code") != 0:

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/02_audio.py (reported line 58)May include surrounding context.

python
raise SystemExit(f"view failed for {bv}: {v.get('message')}")
    d = v["data"]
    cid = d["pages"][0]["cid"]
    pu = s.get("https://api.bilibili.com/x/player/playurl",
               params={"bvid": bv, "cid": cid, "fnval": 16, "fourk": 1},
               headers={"Referer": f"https://www.bilibili.com/video/{bv}"},
               timeout=25).json()

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/02_audio.py (reported line 80)May include surrounding context.

python
def to_wav(src: Path, dst: Path):
    cmd = [ffmpeg_exe(), "-hide_banner", "-loglevel", "error", "-y",
           "-i", str(src), "-ac", "1", "-ar", str(SR), "-vn", str(dst)]
    subprocess.run(cmd, check=True)


def find_offset(a_wav: Path, b_wav: Path, hop: int = 2048) -> tuple[float, float]:

Insecure deserialization: torch.load() without weights_only=True

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

Explicitly setting weights_only=False permits pickle-based code execution while loading a checkpoint, before state-dictionary validation occurs. Because model weights are obtained separately, a malicious or substituted checkpoint could execute with the agent process's filesystem, network, and credential access.

Content

Scanner excerpt · scripts/06_svs.py (reported line 42)May include surrounding context.

python
def build_model(cfg, use_fp16: bool = True):
    model = SoulXSinger(cfg)
    ck = torch.load(str(CKPT), weights_only=False, map_location="cpu")
    model.load_state_dict(ck["state_dict"], strict=True)
    del ck
    if use_fp16:

Artwork generation uses fixed song-specific scenes instead of adapting to the submitted song.

Medium
Category
Excessive Agency
Confidence
94% confidence
Finding

The manifest describes producing illustrated English covers of user-selected Chinese songs, but SCENES contains a fixed set of wuxia images at L23–L48. The generation loop at L67–L79 always uses these prompts and accepts no lyrics or song-specific scene plan, so other songs receive unrelated imagery rather than illustrations derived from their lyrics.

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/08_video.py (reported line 212)May include surrounding context.

python
"-c:v", "libx264", "-preset", "veryfast", "-crf", "20",
           "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "192k",
           "-shortest", "-movflags", "+faststart", str(dst)]
    proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)

    bar_area = 78           # a thin strip along the very bottom, not a curtain over the art
    n_bands = spec.shape[1]

Every generated video is labeled as an English cover of Xu Song's Su Di, regardless of the input song.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

The manifest offers English-cover videos for Chinese songs generally, and the script accepts an arbitrary run directory and audio file. Nevertheless, L313–L319 hardcode the title 'NEMESIS', the attribution "an English cover of Xu Song's Su Di", and '宿敌 · 英文翻唱', misidentifying videos produced for other songs.

Content

No source excerpt is available for this finding.

Documented note-duration-aware vibrato is actually applied continuously to the entire recording.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

L15–L19 explicitly promise per-note depth scaling, with no vibrato on short notes and full depth on long notes. However, L95–L100 pass the entire waveform to apply_vibrato once, and L68–L73 build one recording-wide envelope without reading note boundaries or durations; short notes are therefore modulated too, and onset does not reset for each note.

Content

No source excerpt is available for this finding.

Insecure deserialization: torch.load() without weights_only=True

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

Loading a checkpoint with weights_only=False permits pickle-based arbitrary code execution before load_state_dict validates the weights. Exploitation requires an attacker-controlled or tampered CKPT file; the supplied source does not establish its provenance, but model loading is a normal execution path in this skill.

Content

Scanner excerpt · scripts/probe_melody_mode.py (reported line 44)May include surrounding context.

python
def build_model(cfg, use_fp16=True):
    model = SoulXSinger(cfg)
    ck = torch.load(str(CKPT), weights_only=False, map_location="cpu")
    model.load_state_dict(ck["state_dict"], strict=True)
    del ck
    if use_fp16:

The documented original-pitch control instead supplies frequencies 44 times too low.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

scripts/probe_melody_mode.py L008-L011 promises to preserve the original singer's pitch, and L067 documents output in Hz. However, L082 converts MIDI using a 10 Hz reference instead of 440 Hz, so MIDI 69 becomes 10 Hz rather than 440 Hz; the melody control therefore does not receive the promised original pitch.

Content

No source excerpt is available for this finding.

Insecure deserialization: torch.load() without weights_only=True

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The checkpoint loader explicitly enables unrestricted pickle deserialization, allowing a malicious checkpoint to execute code if this function runs. The script currently references SRC before importing it, blocking ordinary standalone execution, but that incidental startup bug does not make the loader safe once corrected or invoked through another context.

Content

Scanner excerpt · scripts/probe_prompt.py (reported line 38)May include surrounding context.

python
def build(cfg):
    m = SoulXSinger(cfg)
    ck = torch.load(CKPT, map_location="cpu", weights_only=False)
    m.load_state_dict(ck["state_dict"], strict=True)
    del ck
    m.half()

Insecure deserialization: torch.load() without weights_only=True

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

The explicit weights_only=False setting allows a malicious checkpoint to execute arbitrary code during deserialization, before state-dictionary validation. The fixed checkpoint path reduces direct user control but does not protect against compromised downloads or local replacement; its trustworthiness is not established in this source.

Content

Scanner excerpt · scripts/probe_seeds.py (reported line 48)May include surrounding context.

python
def build_model(cfg, use_fp16=True):
    m = SoulXSinger(cfg)
    ck = torch.load(str(CKPT), weights_only=False, map_location="cpu")
    m.load_state_dict(ck["state_dict"], strict=True)
    del ck
    if use_fp16:

The documented hard minimum note allocation is not enforced.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

scripts/singlib.py L214-L215 explicitly describes floor[i] as a hard minimum note count needed to accommodate English syllables. At L248, the lower bound uses min(..., f), allowing allocations below that minimum, while L249 only expands the upper bound; this contradicts the documented allocation guarantee.

Content

No source excerpt is available for this finding.

The fallback silently deletes trailing syllables rather than assigning them to the last note.

Medium
Category
Excessive Agency
Confidence
100% confidence
Finding

scripts/singlib.py L321 says the fallback gives tail syllables to the last note when note splitting remains insufficient. Instead, L322-L324 reduces the syllable count and truncates the tokens and weights, so the trailing lyric syllables are omitted from the generated singing metadata entirely.

Content

No source excerpt is available for this finding.