T09 · Insecure Skill Coding Practices
- Location
scripts/statusline_installer.py:81- Finding
Malformed Claude settings are silently replaced during installation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears intended to install a Claude Code status line, but its installer persistently changes Claude configuration and has avoidable risks that could overwrite or corrupt user settings.
Review before installing. This skill does not show evidence of malicious exfiltration, but it will persistently alter Claude Code configuration and can overwrite an existing statusLine setting. Back up ~/.claude/settings.json and ~/.claude/settings.local.json first, inspect ~/.claude for symlinks or unexpected files, and only run install/configure/uninstall commands when you explicitly want those changes.
scripts/statusline_installer.py:81Malformed Claude settings are silently replaced during installation
scripts/statusline_installer.py:202Installer follows symlinks when writing or replacing security-sensitive files
The skill modifies ~/.claude/settings.local.json and installs an executable script under ~/.claude/scripts/, directly affecting the agent's runtime behavior. Any feature that writes to agent configuration directories is high impact because it creates persistence, can alter future command execution, and could be abused to run unintended code every time the status line is rendered.
## How It Works
1. **install** -- Copies `statusline.py` to `~/.claude/scripts/`, creates config at `~/.claude/statusline.config` (perms 600), updates `~/.claude/settings.local.json` with `python3 ~/.claude/scripts/statusline.py` command.
2. **status** -- JSON output: script installed, config state, settings configured, git availability.
This subprocess explicitly copies all current environment variables into the git child process. In a statusline skill that may execute within sensitive developer or agent sessions, this increases the chance that secrets in the environment become accessible to child-process chains, external helpers, or repository-influenced git behaviors.
result = subprocess.run(
["git", "-C", cwd, "symbolic-ref", "--short", "HEAD"],
capture_output=True, text=True, timeout=5,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
)
if result.returncode == 0:
return _sanitize(result.stdout.strip(), 128)
As with the other git calls, the full environment is inherited by the subprocess even though the command only needs limited runtime context. In the skill context this is more dangerous because the script is intended to run automatically and repeatedly, potentially inside repositories or sessions controlled by untrusted projects while secrets are present in environment variables.
result = subprocess.run(
["git", "-C", cwd, "rev-parse", "--short", "HEAD"],
capture_output=True, text=True, timeout=5,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
)
if result.returncode == 0:
return _sanitize(result.stdout.strip(), 128)
The code forwards the entire parent environment into a child git process via os.environ. In an agent context, environment variables commonly contain API keys, tokens, proxy settings, and other secrets; passing them wholesale to subprocesses unnecessarily broadens exposure to git hooks, helper programs, credential helpers, or misconfigured tooling invoked under git.
def get_git_ahead_behind(cwd):
"""Get ahead/behind counts vs upstream. Returns e.g. '↑2↓1', '↑3', '↓1', or ''."""
git_env = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}
try:
# Check upstream exists
check = subprocess.run(
The skill describes file reads/writes, environment access, and shell execution but does not declare any explicit tool scope or permission boundaries. That makes the automation less auditable and easier to invoke with broader-than-necessary capabilities, increasing the chance of unintended filesystem or command execution during installation and configuration.
The trigger list includes broad, natural phrases like 'install status bar', 'show token usage', and generic customization requests, which could cause the skill to activate when a user intended a different task. Because this skill performs installation and configuration steps that modify local agent settings, accidental invocation can lead to unintended persistent changes.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_git_branch(cwd):
"""Get current git branch or short hash. Returns str or empty string."""
try:
result = subprocess.run(
["git", "-C", cwd, "symbolic-ref", "--short", "HEAD"],
capture_output=True, text=True, timeout=5,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if result.returncode == 0:
return _sanitize(result.stdout.strip(), 128)
# Detached HEAD -- try short hash
result = subprocess.run(
["git", "-C", cwd, "rev-parse", "--short", "HEAD"],
capture_output=True, text=True, timeout=5,
env={**os.environ, "GIT_TERMINAL_PROMPT": "0"},
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
git_env = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}
try:
# Check upstream exists
check = subprocess.run(
["git", "-C", cwd, "rev-parse", "--abbrev-ref", "@{u}"],
capture_output=True, text=True, timeout=5, env=git_env,
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if check.returncode != 0:
return ""
ahead_result = subprocess.run(
["git", "-C", cwd, "rev-list", "HEAD...@{u}", "--left-only", "--count"],
capture_output=True, text=True, timeout=5, env=git_env,
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
["git", "-C", cwd, "rev-list", "HEAD...@{u}", "--left-only", "--count"],
capture_output=True, text=True, timeout=5, env=git_env,
)
behind_result = subprocess.run(
["git", "-C", cwd, "rev-list", "HEAD...@{u}", "--right-only", "--count"],
capture_output=True, text=True, timeout=5, env=git_env,
)
No suspicious patterns detected.