T09 · Insecure Skill Coding Practices
- Location
scripts/lsp-query.py:153- Finding
Undocumented Language-Server Command Override Enables Arbitrary Process Execution
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/lsp-query.py:153Undocumented Language-Server Command Override Enables Arbitrary Process Execution
scripts/lsp-query.py:714Unauthenticated Unix Socket Accepts Privileged Daemon Requests
scripts/lsp-query.py:275Unconditional Logging to a Predictable Shared Temporary File Permits Symlink Attacks
scripts/setup.sh:124Forced System-Wide Symlink Replacement Enables Tool Hijacking
scripts/setup.sh:133Sudo Password Is Accepted Through an Environment Variable and Pipeline
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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"))
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.
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.
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
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.
Cause: ~/.dotnet/tools not in PATH.
export PATH="$HOME/.dotnet/tools:$PATH"
# Permanent fix
echo 'export PATH="$HOME/.dotnet/tools:$PATH"' >> ~/.bashrc
Or re-run setup:
bash scripts/setup.sh
Cause: Daemon was started before code changes and is still running the old version.
# 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
Cause: Some csharp-ls versions have packaging issues.
# Pin to known-good version
dotnet tool instal
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.
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.
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.
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 parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
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 parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
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 parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
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 parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
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"
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.
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.
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.
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.
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.
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.
self._diagnostics = {}
def start(self):
self._proc = subprocess.Popen(
self.server_cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
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.
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
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.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if ws:
return ws
try:
root = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"],
stderr=subprocess.DEVNULL, text=True
).strip()
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.
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.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
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 ""
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
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 ""
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
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 ""