Back to skill

Security audit

Smalltalk

Security checks across malware telemetry and agentic risk

Overview

This is a real Smalltalk development skill, but it needs review because it can run and persistently modify live images and optionally send source code to external LLM providers.

Install only if you want an agent to run and modify code inside a live Smalltalk image. Prefer playground mode for experiments, back up dev images before use, trust the configured VM/image paths, stop the daemon when finished, and use LLM explain/audit/generate commands only with code you are allowed to share with the configured provider.

SkillSpector

By NVIDIA
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (22)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Start Squeak
    env = os.environ.copy()
    env["DISPLAY"] = ":98"
    squeak = subprocess.Popen(
        [vm_path, image_path, "--mcp"],
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        env=env, text=True
Confidence
83% confidence
Finding
squeak = subprocess.Popen( [vm_path, image_path, "--mcp"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, text=True )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Use xvfb-run for headless operation
        cmd = ["xvfb-run", "-a", self.vm_path, self.image_path, "--mcp"]

        self.process = subprocess.Popen(
            cmd,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
Confidence
80% confidence
Finding
self.process = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Content
version_str = call_daemon("smalltalk_evaluate", {"code": "MCPServer version"})
            else:
                # No daemon running - spawn a quick VM to check
                result = subprocess.run(
                    ["xvfb-run", "-a", vm_path, image_path, "--mcp"],
                    input='{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"smalltalk_evaluate","arguments":{"code":"MCPServer version"}}}\n',
                    capture_output=True,
Confidence
82% confidence
Finding
result = subprocess.run( ["xvfb-run", "-a", vm_path, image_path, "--mcp"], input='{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name"

Tainted flow: 'PID_FILE' from os.environ.get (line 40, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
self.socket.settimeout(1.0)  # Allow periodic checks

        # Write PID file
        with open(PID_FILE, "w") as f:
            f.write(str(os.getpid()))

        print(f"🎧 Listening on {SOCKET_PATH}")
Confidence
83% confidence
Finding
with open(PID_FILE, "w") as f:

Tainted flow: 'vm_path' from os.environ.get (line 75, credential/environment) → subprocess.Popen (code execution)

Medium
Category
Data Flow
Content
# Start Squeak
    env = os.environ.copy()
    env["DISPLAY"] = ":98"
    squeak = subprocess.Popen(
        [vm_path, image_path, "--mcp"],
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        env=env, text=True
Confidence
92% confidence
Finding
squeak = subprocess.Popen( [vm_path, image_path, "--mcp"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, text=True )

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

Critical
Category
Data Flow
Content
)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            data = json.loads(resp.read())
            return data["content"][0]["text"]
    except urllib.error.HTTPError as e:
Confidence
98% confidence
Finding
with urllib.request.urlopen(req, timeout=30) as resp:

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

Critical
Category
Data Flow
Content
)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            data = json.loads(resp.read())
            return data["choices"][0]["message"]["content"]
    except urllib.error.HTTPError as e:
Confidence
98% confidence
Finding
with urllib.request.urlopen(req, timeout=30) as resp:

Tainted flow: 'vm_path' from os.environ.get (line 75, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
version_str = call_daemon("smalltalk_evaluate", {"code": "MCPServer version"})
            else:
                # No daemon running - spawn a quick VM to check
                result = subprocess.run(
                    ["xvfb-run", "-a", vm_path, image_path, "--mcp"],
                    input='{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"smalltalk_evaluate","arguments":{"code":"MCPServer version"}}}\n',
                    capture_output=True,
Confidence
92% confidence
Finding
result = subprocess.run( ["xvfb-run", "-a", vm_path, image_path, "--mcp"], input='{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name"

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises substantial capabilities including shell execution, network use, file read/write, environment access, and MCP interaction, yet no explicit permissions are declared. This creates a transparency and policy-enforcement gap: operators may authorize or invoke the skill without understanding that it can spawn processes, touch local files, access API keys, and communicate externally.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented purpose frames the skill mainly as a Smalltalk interaction/browsing tool, but the behavior includes persistent daemon management, snapshot/project manipulation, debug artifact capture, external LLM API calls, and filing generated code back into a live image. That mismatch can mislead users and security controls, causing them to approve a skill for low-risk inspection while it also performs persistence, exfiltration of source, and code modification actions.

Description-Behavior Mismatch

Medium
Confidence
77% confidence
Finding
This script introduces persistent background process management and image-saving behavior that goes beyond the stated skill purpose of interactive Smalltalk operations. That broader capability increases the attack surface by enabling long-lived local processes and persisted state that may survive user expectations or approval boundaries.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The skill manifest describes live Smalltalk-image interaction, but the file adds unrelated external LLM features that transmit code and method source to third-party services. This scope expansion is dangerous because users and agent operators may reasonably expect a local inspection tool, not a network-exfiltrating assistant.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
Debug mode goes beyond Smalltalk interaction by taking screenshots and writing HTML reports in /tmp. This can capture unrelated on-screen data and leave recoverable artifacts on disk accessible to other local users depending on system configuration.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The skill exposes destructive operations such as deleting methods/classes and modifying a live image without prominent warnings about irreversibility, persistence in dev mode, or recovery expectations. In this context, a user can unintentionally corrupt or lose work in a Smalltalk image, especially because the skill also supports persistent images and project state management.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The LLM-backed commands send source code, comments, or class/method contents to external providers using API-key-authenticated services, but the documentation does not clearly warn that proprietary code may leave the local environment. In a code-analysis skill, this is especially sensitive because users may assume all inspection remains local while the skill can transmit implementation details to third-party services.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The script uses predictable filenames in /tmp for PID and socket state based on unsanitized project names, then writes and unlinks those paths. On multi-user systems, this can enable symlink or path-manipulation attacks, causing unintended file overwrite/deletion or interference with other users' processes.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The debug flow captures the entire root window and embeds it into an HTML report without an explicit warning. Even in a headless Xvfb session, the screenshot may contain sensitive code, secrets, or debugging state, and the saved report becomes a persistent local disclosure artifact.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The CLI advertises explain/audit/generate features but does not clearly warn that they send user code and live method source to external LLM APIs. This is a material privacy and data-governance issue because operators may use the commands on proprietary or secret-bearing code under the assumption the tool is local.

External Transmission

Medium
Category
Data Exfiltration
Content
}).encode()

    req = urllib.request.Request(
        "https://api.anthropic.com/v1/messages",
        data=body,
        headers={
            "x-api-key": api_key,
Confidence
95% confidence
Finding
https://api.anthropic.com/

External Transmission

Medium
Category
Data Exfiltration
Content
import urllib.request
    import urllib.error

    base_url = os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1")
    model = os.environ.get("OPENAI_MODEL", "gpt-4o")

    messages = []
Confidence
98% confidence
Finding
https://api.openai.com/

Env Variable Harvesting

High
Category
Data Exfiltration
Content
time.sleep(2)
    
    # Start Squeak
    env = os.environ.copy()
    env["DISPLAY"] = ":98"
    squeak = subprocess.Popen(
        [vm_path, image_path, "--mcp"],
Confidence
78% confidence
Finding
os.environ.copy()

Session Persistence

Medium
Category
Rogue Agent
Content
print("🚀 Starting Smalltalk daemon...", file=sys.stderr)
    
    # Start daemon in background using nohup to survive parent exit
    try:
        subprocess.Popen(
            ["nohup", sys.executable, daemon_script, "start"],
Confidence
74% confidence
Finding
nohup

VirusTotal

63/63 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.