Back to skill

Security audit

Phy App Launcher

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it advertises, but it ships a hard-coded external API credential and generates persistent launcher scripts with unsafe path and command handling.

Install only if you are comfortable with a skill that writes persistent macOS .app launchers which run project commands in Terminal. Avoid using --auto-icon unless the embedded FAL credential has been removed and you supply your own approved key. Do not run this on untrusted projects or project paths until app names, paths, and generated commands are validated and safely escaped.

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/create_launcher.py:22
Finding

Hard-Coded FAL API Credential Exposed in Source Code

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_launcher.py:224
Finding

Path Traversal Enables Unsafe Recursive Directory Deletion

Content
View full analysis
/../../target.app ``` If an absolute value is supplied, `pathlib` can also discard the preceding `output_dir` when composing the path. The code does not resolve the final destination and verify that it remains beneath the intended output directory. The risk is amplified by: ```python shutil.rmtree(app_bundle) ``` If the attacker-selected destination already exists, the script recursively deletes it before creating the new launcher. There is no check that the destination is an application bundle previously generated by this tool. ### Attack Path 1. An attacker prepares a Node.js project containing a crafted `package.json` name with traversal components, or convinces the user to invoke the script with a malicious `--name`. 2. The user runs the documented automatic lau ...[truncated 1063 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_launcher.py:242
Finding

Shell and AppleScript Injection Through Unescaped Launcher Values

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose is only local launcher creation, but the detected behavior includes external network/service usage, remote content downloading, and API credential injection via an environment variable. That mismatch is dangerous because users and policy engines may authorize the skill expecting only local file generation while it can also exfiltrate metadata, fetch untrusted content, or invoke third-party services with hidden credentials.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code embeds a FAL API credential and silently injects it into the environment at runtime. Hardcoded secrets are inherently unsafe because they can be extracted, reused by unauthorized parties, incur billing abuse, and normalize hidden external access in a skill that appears local-only.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This code reads a hardcoded credential into process environment state without clear user warning or consent. That creates secret-exposure and unauthorized-service-use risk, especially because the skill can trigger network behavior under a seemingly local feature set.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises commands that invoke a local Python script to create .app bundles and launch local services, but it declares no explicit tool scope or permissions. In a skill system, missing scope around shell, file write, environment access, and possible network use increases the chance the agent will execute higher-risk actions without clear user-visible constraints or policy enforcement.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The skill is explicitly designed to create a persistent desktop launcher that repeatedly runs a local dev server outside the immediate agent session. Session persistence is risky because it creates an enduring execution path on the user's desktop that can later run commands with a single click, potentially outliving the user's original context or review.

Content

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

md
---
name: app-launcher
description: Create macOS desktop app launchers for dev projects. Click an icon on Desktop → auto-runs local dev server in Terminal. Use when user wants to make an app "persistent", create a desktop shortcut, launch dev server with one click, or says "make this app clickable" / "create launcher" / "desktop icon for this project".
homepage: https://canlah.ai
---

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases are broad enough to match ordinary requests like making an app clickable or persistent, which can cause the skill to activate in situations where the user did not intend launcher creation or shell execution. Over-broad activation is especially risky here because the skill performs file creation and may run project commands, increasing the likelihood of unintended side effects.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The documented workflow instructs the agent to create a desktop .app that persists after the session, reinforcing the creation of a long-lived execution artifact. In the context of a skill that can auto-detect commands and possibly perform undeclared network-related actions, that persistence increases risk because unsafe behavior can be packaged into a reusable launcher.

Content

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

md
## Workflow

1. User says "create launcher for this project" or "make desktop icon"
2. Identify project path and any custom requirements (name, icon, command)
3. Run the script with appropriate options
4. Confirm the .app was created on Desktop

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill's stated purpose is local launcher creation, but enabling --auto-icon sends app metadata in prompts to a third-party service and downloads remote content without strong disclosure or isolation. This broadens the trust boundary unexpectedly and can leak project/app names or introduce untrusted remote files into the workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The function performs an external network request and downloads remote image content without meaningful disclosure that user-provided app names are transmitted to a third party. In a local-launcher skill, this is an unexpected data flow and could expose sensitive project identities or fetch untrusted content.

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/create_launcher.py (reported line 128)May include surrounding context.

python
resized.save(output_file, "PNG")

        # Convert iconset to icns using iconutil
        result = subprocess.run(
            ["iconutil", "-c", "icns", iconset_dir, "-o", icns_path],
            capture_output=True,
            text=True

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 276)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 277)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 280)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 281)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 282)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 303)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 305)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 353)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 355)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 356)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 357)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 359)May include surrounding context.

python
launcher_script.write_text(script_content)
    launcher_script.chmod(launcher_script.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

    # Create Info.plist
    info_plist = contents_dir / "Info.plist"
    bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_launcher.py (reported line 281)May include surrounding context.

python
bundle_id = app_name.lower().replace(" ", "-").replace("_", "-")

    plist_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key>

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/create_launcher.py (reported line 333)May include surrounding context.

python
sizes = [16, 32, 64, 128, 256, 512]
                        for size in sizes:
                            subprocess.run([
                                "sips", "-z", str(size), str(size),
                                str(icon_path), "--out", str(iconset_path / f"icon_{size}x{size}.png")
                            ], capture_output=True)

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/create_launcher.py (reported line 338)May include surrounding context.

python
str(icon_path), "--out", str(iconset_path / f"icon_{size}x{size}.png")
                            ], capture_output=True)
                            if size <= 256:
                                subprocess.run([
                                    "sips", "-z", str(size*2), str(size*2),
                                    str(icon_path), "--out", str(iconset_path / f"icon_{size}x{size}@2x.png")
                                ], capture_output=True)

Static analysis

No suspicious patterns detected.