Back to skill

Security audit

Intranet

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed local file server, but its CGI/plugin code has real code-execution and secret-handling risks that need review before installation.

Install only if you understand that enabling CGI lets local index.py files run as code under your user account. Avoid enabling CGI for writable or untrusted plugin directories, do not expose it beyond loopback unless authentication and host allowlists are configured, keep tokens out of shared or committed files, and treat the plugin hash check as unreliable until fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/intranet_web.py:345
Finding

Plugin CGI Hash Verification Bypass

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/intranet.py:106
Finding

Bearer Token Stored in Plaintext Runtime Configuration

Content
View full analysis
None: """Write server configuration.""" _config_file().write_text(json.dumps(config, indent=2) + "\n") ``` ```python # Save configuration config = { "host": host, "port": port, } if token: config["token"] = token _write_config(config) ``` ### Technical Analysis The bearer token supplied through `--token` or loaded from persistent configuration is copied into `workspace/intranet/.conf` in plaintext. The runtime file is needed for status information, but the status command only needs to know whether authentication is enabled; it does not need the actual secret. `Path.write_text()` does not explicitly enforce owner-only permissions. When the file is created, its effective permissions depend on the process umask and surrounding directory permissions. In a shared or permissively configured workspace, another local account or process may be able to read the token. The token functions as an authentication credential, so possession provides the same HTTP access as the legitimate operator. ### Attack Path 1. The operator starts the server with `--token SECRET`, or the token is loaded from `config.json`. 2. The startup code places the complete token in the runtime configuration dictionary. 3. `_write_config()` writes the dictionary to `intranet/.conf` without explicitly setting mode `0600`. 4. A local user or process with workspace read access reads `.conf`. 5. The recovered token is submitted through the bearer header or token query parameter. 6. The attacker receives authenticated access to served files and enabled CGI routes. ### Impact Assessment An attacker who obtains the runtime file can bypass token authentication and access all content exposed by ...[truncated 292 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/intranet_web.py:409
Finding

CGI Processes Inherit the Complete Server Environment

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/intranet.py:230
Finding

Unverified PID File Can Terminate an Unrelated Process

Content
View full analysis
int: """Stop the intranet server.""" pid = _read_pid() if not pid: print("[intranet] Server is not running") return 1 if not _is_process_running(pid): print(f"[intranet] Server not running (stale PID {pid})") _clear_pid() _clear_config() return 1 try: os.kill(pid, signal.SIGTERM) print(f"[intranet] Server stopped (PID {pid})") _clear_pid() _clear_config() return 0 except OSError as e: print(f"[intranet] ERROR: Failed to stop server (PID {pid}): {e}") return 1 ``` ### Technical Analysis The stop command accepts any integer contained in `intranet/.pid`. It verifies only that some process with that identifier exists by using `os.kill(pid, 0)`. It does not verify that the process is an intranet server, was created by the current server instance, or has the expected start time and executable. PID reuse can also cause a stale PID file to refer to an unrelated process after the original server exits. If the PID file is writable by another local user or process, it can be deliberately replaced with the identifier of a target process. Calling `os.kill(pid, SIGTERM)` then terminates the unrelated process whenever the invoking account has permission to signal it. ### Attack Path 1. An attacker gains write access to `workspace/intranet/.pid`, or a stale PID is reused by another process. 2. The file is populated with the PID of a target process. 3. The operator or an automated workflow invokes `intranet.py stop`. 4. `_is_process_running()` confirms only that the target PID exists. 5. The stop command sends `SIGTERM` to the target process. 6. The unrelated process terminates or begins its signa ...[truncated 557 chars]
Remediation
View remediation
/cmdline`, `/proc//exe`, and the process start time against recorded values. - Prefer a local authenticated control socket or pidfd-based process management where available. - Remove stale PID files only after verifying that they do not correspond to an active server instance. - Add tests covering PID reuse, file tampering, malformed PIDs, and unrelated live processes. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The documented purpose presents the skill mainly as a lightweight static file server with plugin/CGI support, but the behavior also includes process management, persistent state handling, auth policy enforcement, dynamic module loading, and workspace autodiscovery. This mismatch is security-relevant because users may approve or deploy the skill under an incomplete mental model, while the extra behaviors expand attack surface and create opportunities for unauthorized persistence, exposure, or execution paths.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
95% confidence
Finding

The CGI environment starts from os.environ.copy(), passing the server process's full ambient environment into executed scripts. In this skill context, CGI scripts are reachable through HTTP and may be plugin-provided, so secrets in environment variables (API keys, tokens, credentials, proxy settings, workspace paths) can be exposed to script authors or leaked back to clients.

Content

Scanner excerpt · scripts/intranet_web.py (reported line 448)May include surrounding context.

python
parsed = urllib.parse.urlparse(self.path)

        env = os.environ.copy()
        env.update({
            "REQUEST_METHOD": "GET",
            "SCRIPT_NAME": url_path,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation explicitly recommends storing the bearer token in config.json, which is a plaintext file under the workspace. In a local developer environment, workspace files are often readable by other users, included in backups, accidentally committed to source control, or exposed through adjacent tooling, so this guidance can lead to credential disclosure and unauthorized access to the intranet service.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill describes capabilities that require broad access to environment variables, filesystem, networking, and shell execution, but it does not declare any explicit tool scope or permission boundaries. That omission weakens reviewability and least-privilege enforcement, making it easier for the skill to be granted more power than operators expect, especially because it starts a server, writes runtime files, and can execute CGI scripts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The start command stores the bearer token into the runtime config file in the intranet workspace, and there is no indication that file permissions are restricted or that users are warned credentials will be written to disk. On multi-user systems, shared workspaces, backups, or accidental file disclosure, this can expose the token and allow unauthorized access to the server.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This code executes local index.py files via subprocess.run, which is a safety-critical operation because it runs arbitrary executable code from the served directory or plugin root. While the module docstring documents CGI support, there is no confirmation prompt, user-facing log at execution time, or explicit warning in this file near the operation to disclose that requests may trigger code execution.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
91% confidence
Finding

The server executes local CGI scripts via subprocess based on HTTP requests, which creates a direct remote-to-code-execution path whenever CGI is enabled. Although execution is limited to index.py and some path controls exist, this still means any writable or compromised webroot/plugin content can be executed by a requester, making the feature inherently dangerous in this context.

Content

Scanner excerpt · scripts/intranet_web.py (reported line 464)May include surrounding context.

python
})

        try:
            result = subprocess.run(
                [sys.executable, str(actual_script)],
                capture_output=True,
                timeout=_CGI_TIMEOUT,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The generated HTML sets lang="en", which forces an English locale in the skill's user-facing output. Under the policy, locale-specific behavior should either offer user choice or be clearly documented and justified; this file provides neither.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.