Back to skill

Security audit

Skill Analyzer

Security checks for vulnerabilities and agentic risk

Overview

The skill is not malicious, but its advertised security-review function is materially unreliable and could give users misplaced confidence in unsafe third-party skills.

Install only if you treat it as a lightweight quality checklist, not as a security approval tool. Review any reported LOW risk result manually, especially for skills with nested scripts, non-Python files, network access, subprocess use, persistence, or sensitive data handling.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/analyzer.py:265
Finding
Broken Score Normalization Produces Unreliable Risk Classifications<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyzer.py`, lines 265-286 **Vulnerability Type**: Incorrect security-score normalization and fail-open risk classification **Risk Level**: High ### Vulnerable Code ```python def _calculate_scores(self) -> Dict[str, float]: """Calculate weighted scores.""" scores = {} for dimension, config in self.DIMENSIONS.items(): score = self.results.get(dimension, 0) scores[dimension] = score * config["weight"] * 10 return scores def _weighted_average(self, scores: Dict[str, float]) -> float: """Calculate weighted average.""" return sum(scores.values()) def _assess_risk(self, scores: Dict[str, float]) -> str: """Assess overall risk level.""" overall = sum(scores.values()) if overall >= 7.5: return "LOW" elif overall >= 5: return "MEDIUM" else: return "HIGH" ``` ### Technical Analysis Each raw dimension score is already expressed on a scale from 0 to 10. The calculation multiplies that score by its configured weight and then multiplies it by 10 again: ```python score * config["weight"] * 10 ``` Because the dimension weights sum to 1, this produces an aggregate range of 0 to 100. However, `_assess_risk()` applies thresholds designed for a range of 0 to 10. Consequently, an aggregate score only needs to reach 7.5 out of 100 to be classified as `LOW` risk. The same incorrectly scaled values are exposed as dimension scores and printed as though each were out of 10. For example, the functionality dimension has a weight of 0.25 and can produce a displayed value as high as 25. This is security-relevant because the tool is explicitly presented as a security review mechanism for third-party skills. A severely unsafe skill can receive an authoritative-looking `LOW` risk result when modest points from non-security dimensions overwhelm the incorrectly scaled threshold. ### Attack Path 1. An attacker prepares a malicious or unsafe ...[truncated 1249 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Preserve the intended 0-10 aggregate scale by removing the extra multiplication: ```python scores[dimension] = score * config["weight"] ``` 2. Keep the existing `7.5` and `5.0` thresholds only after confirming that the aggregate cannot exceed 10. 3. Report raw dimension scores separately from weighted contributions. For example: ```python raw_scores[dimension] = score weighted_total += score * config["weight"] ``` 4. Add explicit invariants before classification: ```python if not 0 <= overall <= 10: raise ValueError(f"Invalid normalized score: {overall}") ``` 5. Avoid allowing strong documentation or usability scores to conceal critical security findings. Introduce fail-closed rules, such as: - Any confirmed critical behavior results in `HIGH` risk. - A security score below a defined minimum prevents `LOW` risk. - Incomplete scan coverage produces `UNKNOWN` or `REVIEW REQUIRED`, not `LOW`. 6. Add unit tests for minimum, maximum, and boundary values, including: - All dimensions equal to zero. - All dimensions equal to ten. - Security equal to zero with high non-security scores. - Aggregates immediately below and above 5.0 and 7.5. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/analyzer.py:145
Finding
Incomplete and Bypassable Security Scan Can Approve Malicious Skill Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyzer.py`, lines 145-184 **Vulnerability Type**: Insufficient file coverage and weak dynamic-code detection **Risk Level**: High ### Vulnerable Code ```python def _run_security_checks(self): """Run security analysis.""" score = 10 # Check for hardcoded secrets if self._has_hardcoded_secrets(): score -= 5 self.issues.append("Potential hardcoded credentials found") else: self.strengths.append("No obvious hardcoded secrets") # Check scripts for security # Only flag eval/exec when combined with dynamic input (dangerous pattern) scripts_dir = self.skill_path / "scripts" if scripts_dir.exists(): for script in scripts_dir.glob("*.py"): content = script.read_text(encoding="utf-8") # Only flag if: eval/exec + (user input functions or variable injection risk) dangerous = False if "eval(" in content or "exec(" in content: # Check for variable injection patterns: eval(x), exec(f.read()), etc. if re.search(r'(eval|exec)\s*\(\s*(f["\'(]|open\(|request|json\.loads|argv\[)', content): dangerous = True if dangerous: score -= 3 self.issues.append(f"Potentially unsafe operation in {script.name}") self.results["security"] = max(0, score) def _has_hardcoded_secrets(self) -> bool: """Check for hardcoded secrets in code.""" patterns = [ r'password\s*=\s*["\'][^"\']+["\']', r'api_key\s*=\s*["\'][^"\']+["\']', r'secret\s*=\s*["\'][^"\']+["\']', r'token\s*=\s*["\'][^"\']+["\']', ] scripts_dir = self.skill_path / "scripts" if not scripts_dir.exists(): return False for script in scripts_dir.glob("*.py"): content = script.read_text(encoding="utf-8") for pattern in patterns: if re.search(pattern, ...[truncated 3234 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Recursively enumerate relevant files rather than scanning only top-level Python files: ```python for path in scripts_dir.rglob("*"): if path.is_file(): ... ``` 2. Define supported file types and inspect Python, shell, JavaScript, PowerShell, executable configuration, and other text-based artifacts. Report unsupported executable or binary files as requiring manual review. 3. Include `SKILL.md` and installation instructions in the security assessment. Detect commands that download or execute remote content, modify startup configuration, access sensitive paths, or request excessive privileges. 4. Replace regular-expression-only Python analysis with `ast`-based inspection. Track calls to dangerous primitives even when arguments pass through variables, including: - `eval` and `exec`; - `compile`; - `subprocess` APIs; - `os.system`; - dynamic imports; - deserialization and code-loading operations. 5. Add data-flow or conservative source/sink checks for command-line arguments, files, environment variables, network responses, and decoded content flowing into execution functions. 6. Add explicit checks for: - External network access and payload retrieval. - Subprocess and shell invocation. - Credential and sensitive-file access. - Persistence mechanisms. - Obfuscated or encoded payloads. - Unsafe path handling and file writes. 7. Fail closed when scan coverage is incomplete. Use a result such as `UNKNOWN` or `MANUAL REVIEW REQUIRED` instead of awarding a clean security score. 8. Distinguish carefully between verified safety and absence of a signature. Replace reassuring statements such as `"No obvious hardcoded secrets"` with coverage-specific language that cannot be interpreted as a comprehensive approval. 9. Add regression tests containing nested payloads, non-Python scripts, `exec(payload)`, aliases, intermediate variables, and malicious commands in documentation. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (1)

Missing User Warnings

Low
Confidence
83% confidence
Finding
This code performs a file write operation using a path supplied by the user, but there is no preceding warning in comments, docstrings, or user-facing messaging that the tool will create or overwrite an output file. Under the code-file criteria, file writes should have some form of disclosure unless clearly covered by the skill description, which is not visible in this file.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/analyzer.py:178