Back to skill

Security audit

C# LSP

Security checks for vulnerabilities and agentic risk

Overview

This skill is a C# code-navigation helper, but its installer and runtime grant broader and more persistent local authority than the C# description makes clear.

Review before installing. Prefer not to run the bundled setup script as-is on a shared or privileged machine. If you proceed, avoid SUDO_PASS, inspect any existing /usr/local/bin/lsp-query before linking, prefer a user-local bin path, restrict LSP_SERVER/LSP_LANG use, and treat queried files as being shared with local language-server subprocesses.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (5)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lsp-query.py:153
Finding

Undocumented Language-Server Command Override Enables Arbitrary Process Execution

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lsp-query.py:714
Finding

Unauthenticated Unix Socket Accepts Privileged Daemon Requests

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lsp-query.py:275
Finding

Unconditional Logging to a Predictable Shared Temporary File Permits Symlink Attacks

Content
View full analysis
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/setup.sh:124
Finding

Forced System-Wide Symlink Replacement Enables Tool Hijacking

Content
View full analysis
/dev/null || true if [ -L "$SYMLINK_PATH" ] && [ "$(readlink -f "$SYMLINK_PATH")" = "$(readlink -f "$LSP_QUERY")" ]; then skip "$SYMLINK_PATH → $LSP_QUERY" elif [ -w "$(dirname "$SYMLINK_PATH")" ] 2>/dev/null; then ln -sf "$LSP_QUERY" "$SYMLINK_PATH" ok "$SYMLINK_PATH → $LSP_QUERY" else # sudo 필요 if command -v sudo &>/dev/null; then info "심볼릭 링크 생성에 sudo가 필요합니다..." if [ -n "${SUDO_PASS:-}" ]; then echo "$SUDO_PASS" | sudo -S ln -sf "$LSP_QUERY" "$SYMLINK_PATH" 2>/dev/null ok "$SYMLINK_PATH → $LSP_QUERY (sudo)" elif sudo -n true 2>/dev/null; then sudo ln -sf "$LSP_QUERY" "$SYMLINK_PATH" ok "$SYMLINK_PATH → $LSP_QUERY (sudo)" ``` ### Technical Analysis The setup process uses `ln -sf` to create `/usr/local/bin/lsp-query`. The force option replaces an existing destination without first proving that the existing command belongs to this Skill. The privileged global command is a symbolic link to the script inside the current Skill or Git working directory, rather than an immutable root-owned installed copy. Project documentation explicitly describes edits and branch switches in that working tree as immediately changing the command's behavior. This creates two related risks: - An unrelated existing `/usr/local/bin/lsp-query` can be overwritten. - Anyone who can later modify the linked working-tree script can change what users execute through a trusted-looking global command without repeating the privileged setup step. ### Attack Path 1. A user runs `setup.sh`, potentially authorizing the operation through `sudo`. 2. The script force-replaces `/usr/local/bin/lsp-query`. 3. The resulting global command points to a mutable repository script. ...[truncated 925 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:133
Finding

Sudo Password Is Accepted Through an Environment Variable and Pipeline

Content
View full analysis
/dev/null; then info "심볼릭 링크 생성에 sudo가 필요합니다..." if [ -n "${SUDO_PASS:-}" ]; then echo "$SUDO_PASS" | sudo -S ln -sf "$LSP_QUERY" "$SYMLINK_PATH" 2>/dev/null ok "$SYMLINK_PATH → $LSP_QUERY (sudo)" ``` ### Technical Analysis The setup script supports supplying a sudo password through the `SUDO_PASS` environment variable. Privileged credentials should not be stored in process environments because they can be retained or exposed by parent processes, automation systems, diagnostic tooling, crash capture, shell configuration, and improperly secured logs. The password is passed through a pipeline to `sudo -S`. Although it is not included directly in the command-line argument list, the environment variable remains available to the setup process and any unexpectedly invoked child process that inherits the environment. This password handling is unnecessary for the Skill's legitimate purpose because `sudo` can prompt interactively, and the command can instead be shown for explicit manual execution. ### Attack Path 1. A user or CI system exports `SUDO_PASS` before running setup. 2. The setup process inherits the plaintext password in its environment. 3. A compromised parent process, diagnostic collector, shell wrapper, or other component with access to that environment captures the value. 4. The attacker obtains the victim's sudo password. 5. The attacker reuses the password to execute unrelated privileged commands where sudo policy permits. ### Impact Assessment Disclosure may allow privilege escalation to root or another privileged account, depending on the victim's sudo policy and password reuse. The affected scope is not limited to `lsp-query`: a stolen sudo password can authorize arbitrary commands permitted by the syste ...[truncated 27 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (39)

Tainted flow: 'response' from os.environ.get (line 774, credential/environment) → socket.socket.sendall (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/lsp-query.py (reported line 775)May include surrounding context.

python
request = json.loads(data.decode("utf-8").strip())
                result = _handle_request(daemon, request)
                response = json.dumps(result).encode("utf-8")
                conn.sendall(response)
            except Exception as e:
                try:
                    conn.sendall(json.dumps({"error": str(e)}).encode("utf-8"))

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is described as providing code intelligence, but the setup flow performs privileged or persistent system modifications such as global dotnet tool installation, shell profile edits, symlink creation, and local cache setup. That is dangerous because these side effects change the host environment beyond the expected scope of a language-assistance skill and can create persistence, PATH hijacking risk, or unintended system-wide impact if users run the setup without understanding the consequences.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is described as providing code intelligence, but the setup flow performs privileged or persistent system modifications such as global dotnet tool installation, shell profile edits, symlink creation, and local cache setup. That is dangerous because these side effects change the host environment beyond the expected scope of a language-assistance skill and can create persistence, PATH hijacking risk, or unintended system-wide impact if users run the setup without understanding the consequences.

Content

No source excerpt is available for this finding.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · docs/troubleshooting.md (reported line 32)May include surrounding context.

First query is very slow (30–60 seconds)

Normal behavior. Roslyn needs to load the entire solution and build the type system on first use. Subsequent queries are ~200ms.

For large solutions (100+ projects), initial load can take up to 60 seconds.

"csharp-ls: command not found"

Cause: ~/.dotnet/tools not in PATH.

bash
export PATH="$HOME/.dotnet/tools:$PATH"

# Permanent fix
echo 'export PATH="$HOME/.dotnet/tools:$PATH"' >> ~/.bashrc

Or re-run setup:

bash
bash scripts/setup.sh

Stale daemon (old code still running)

Cause: Daemon was started before code changes and is still running the old version.

bash
# Kill daemon
lsp-query shutdown

# Clean up
rm -f ~/.cache/lsp-query/daemon.sock ~/.cache/lsp-query/daemon.sock.pid

# Next lsp-query call will start fresh daemon

"dotnet tool install fails with DotnetToolSettings.xml"

Cause: Some csharp-ls versions have packaging issues.

bash
# Pin to known-good version
dotnet tool instal

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The implementation materially differs from the manifest: it is a multi-language LSP daemon rather than a narrowly scoped C# helper. This mismatch is security-relevant because reviewers and users may grant trust based on a C#-only description while the code actually enables broader file access and process execution across many ecosystems.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill exposes analysis capability for many languages unrelated to its declared C# purpose. In agent environments, unnecessary capability expansion increases attack surface, broadens the set of files that may be opened and transmitted to subprocesses, and makes policy enforcement harder.

Content

No source excerpt is available for this finding.

Chaining Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

Piping an environment-sourced secret into sudo chains together plaintext secret handling and privileged execution. In an agent skill context, where scripts may run in automated environments with logs and inherited environment state, this is more dangerous than in a purely local interactive installer because it normalizes unattended privilege escalation.

Content

Scanner excerpt · scripts/setup.sh (reported line 136)May include surrounding context.

sh
if command -v sudo &>/dev/null; then
        info "심볼릭 링크 생성에 sudo가 필요합니다..."
        if [ -n "${SUDO_PASS:-}" ]; then
            echo "$SUDO_PASS" | sudo -S ln -sf "$LSP_QUERY" "$SYMLINK_PATH" 2>/dev/null
            ok "$SYMLINK_PATH → $LSP_QUERY (sudo)"
        elif sudo -n true 2>/dev/null; then
            sudo ln -sf "$LSP_QUERY" "$SYMLINK_PATH"

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/setup.sh (reported line 221)May include surrounding context.

sh
info "LSP 쿼리 테스트 (첫 실행은 30초+ 소요)..."
    lsp-query shutdown 2>/dev/null || true
    rm -f "$CACHE_DIR/daemon.sock" "$CACHE_DIR/daemon.sock.pid"

    RESULT=$(LSP_WORKSPACE="$TEST_DIR/Verify" lsp-query hover "$TEST_DIR/Verify/Program.cs" 4 16 2>&1)

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 · docs/troubleshooting.md (reported line 49)May include surrounding context.

md
export LSP_WORKSPACE=/tmp/test/Mini
        lsp-query shutdown 2>/dev/null || true
        rm -f ~/.cache/lsp-query/daemon.sock ~/.cache/lsp-query/daemon.sock.pid

        # ═══ TEST 3: Hover ═══
        echo "TEST:hover:START"

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 · docs/troubleshooting.md (reported line 76)May include surrounding context.

md
export LSP_WORKSPACE=/tmp/test/Mini
        lsp-query shutdown 2>/dev/null || true
        rm -f ~/.cache/lsp-query/daemon.sock ~/.cache/lsp-query/daemon.sock.pid

        # ═══ TEST 3: Hover ═══
        echo "TEST:hover:START"

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 · tests/test.sh (reported line 104)May include surrounding context.

sh
export LSP_WORKSPACE=/tmp/test/Mini
        lsp-query shutdown 2>/dev/null || true
        rm -f ~/.cache/lsp-query/daemon.sock ~/.cache/lsp-query/daemon.sock.pid

        # ═══ TEST 3: Hover ═══
        echo "TEST:hover:START"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill exposes shell, network, environment, and file read/write capabilities through setup and daemon operations, but it does not declare any explicit tool scope or permissions boundaries. That omission is dangerous because consumers cannot easily assess or restrict what the skill may do, and the setup instructions include actions like global installs and filesystem changes that exceed passive code-intelligence behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file's natural-language instructions are presented in Korean throughout, which effectively forces a specific language for operators reading the deployment process. Under the stated policy, a fixed language without user opt-in or a documented locale-specific justification is a policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

This markdown file includes operational steps that publish code (git push origin main --tags) and explicitly states that the pushed code is immediately the deployed version and will be received by other users. Because this behavior can affect system integrity and other users, the document should include a clear user-facing warning about the immediate deployment impact before those commands are run.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest claims use of csharp-ls, but the code permits replacing the server command globally via LSP_SERVER, allowing execution of arbitrary alternative binaries. That undermines the trust model implied by the manifest and turns a specific language-server dependency into a generic command-execution mechanism.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The server-command resolution logic allows execution of whatever binary is configured for a language, and also supports a global override. For a C#-scoped skill, that is broader than necessary and creates an avoidable local execution primitive with access to workspace content.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
90% confidence
Finding

The code launches a subprocess using self.server_cmd, and that command can be influenced indirectly through LSP_SERVER or by selecting any configured language server. In the context of an agent skill, this broad process-execution surface enables arbitrary local binaries to run under the agent's privileges, which exceeds the declared C#-only purpose and can be abused for unintended code execution.

Content

Scanner excerpt · scripts/lsp-query.py (reported line 237)May include surrounding context.

python
self._diagnostics = {}

    def start(self):
        self._proc = subprocess.Popen(
            self.server_cmd,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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/lsp-query.py (reported line 256)May include surrounding context.

python
if msg is None:
                break
            # Server-to-client request (has both "method" and "id")
            # e.g. client/registerCapability, window/workDoneProgress/create
            if "method" in msg and "id" in msg:
                self._handle_server_request(msg)
                continue

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

When a file is opened, its full contents are read and sent to a background language-server subprocess. In this skill context, that can expose sensitive source code or secrets embedded in files to additional tools/processes without explicit disclosure or minimization, which is more concerning because the skill's scope is broader than advertised.

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/lsp-query.py (reported line 629)May include surrounding context.

python
if ws:
        return ws
    try:
        root = subprocess.check_output(
            ["git", "rev-parse", "--show-toplevel"],
            stderr=subprocess.DEVNULL, text=True
        ).strip()

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The usage banner lists supported languages as python, typescript/js, rust, go, c/c++, bash, java, css, html, and json, but omits C# even though the code includes a csharp entry and the manifest is specifically for C#. This is an active documentation contradiction about the tool's intended and actual language scope.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file’s comments, usage guidance, and later runtime messages are written in Korean, indicating the skill is designed to communicate in a fixed language. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup.sh (reported line 57)May include surrounding context.

sh
fail ".NET SDK가 설치되어 있지 않습니다."
    info ""
    info "설치 방법:"
    info "  Ubuntu/Debian:  sudo apt install dotnet-sdk-9.0"
    info "  macOS:          brew install dotnet-sdk"
    info "  기타:           https://dot.net/download"
    info ""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup.sh (reported line 132)May include surrounding context.

sh
fail ".NET SDK가 설치되어 있지 않습니다."
    info ""
    info "설치 방법:"
    info "  Ubuntu/Debian:  sudo apt install dotnet-sdk-9.0"
    info "  macOS:          brew install dotnet-sdk"
    info "  기타:           https://dot.net/download"
    info ""

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/setup.sh (reported line 139)May include surrounding context.

sh
fail ".NET SDK가 설치되어 있지 않습니다."
    info ""
    info "설치 방법:"
    info "  Ubuntu/Debian:  sudo apt install dotnet-sdk-9.0"
    info "  macOS:          brew install dotnet-sdk"
    info "  기타:           https://dot.net/download"
    info ""